Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/templates/typescript/anthropic-computer-use/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Kernel, type KernelContext } from '@onkernel/sdk';
import { CuaAgent } from '@onkernel/cua-agent';
import type { AssistantMessage } from '@onkernel/cua-ai';
import { KernelBrowserSession } from './session';
import { logAgentEvent } from './logging';

const kernel = new Kernel();

Expand Down Expand Up @@ -82,6 +83,7 @@ app.action<QueryInput, QueryOutput>(
systemPrompt: SYSTEM_PROMPT,
},
});
agent.subscribe(logAgentEvent);

await agent.prompt(payload.query);

Expand Down
45 changes: 45 additions & 0 deletions pkg/templates/typescript/anthropic-computer-use/logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { AgentEvent } from '@onkernel/cua-agent';

// Logs the agent's narration (`agent>`) and each browser action (`→`) it takes.
// Pass to `agent.subscribe(logAgentEvent)`.
export function logAgentEvent(event: AgentEvent): void {
if (event.type === 'tool_execution_start') {
console.log(` → ${describe(event.toolName, event.args)}`);
} else if (event.type === 'tool_execution_end' && event.isError) {
console.log(` ✗ ${event.toolName} failed`);
} else if (event.type === 'message_end') {
const text = narration(event.message);
if (text) console.log(`\nagent> ${text}`);
}
}

// Anthropic nests actions under `computer_batch`, OpenAI under
// `computer_use_extra`; unwrap those, then print the action name and its args.
function describe(name: string, args: any): string {
if (name === 'computer_batch' && Array.isArray(args?.actions)) {
return args.actions.map((a: any) => describe(a.type, a)).join('; ');
}
if (name === 'computer_use_extra' && typeof args?.action === 'string') {
return describe(args.action, args);
}
const params = Object.entries(args ?? {})
.filter(([key]) => key !== 'type' && key !== 'action')
.map(([key, value]) => `${key}=${short(value)}`)
.join(' ');
return params ? `${name} ${params}` : name;
}

function narration(message: any): string {
if (message?.role !== 'assistant') return '';
return message.content
.filter((block: any) => block.type === 'text')
.map((block: any) => block.text)
.join(' ')
.replace(/\s+/g, ' ')
.trim();
}

function short(value: unknown): string {
const text = typeof value === 'string' ? value : JSON.stringify(value);
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
}
2 changes: 2 additions & 0 deletions pkg/templates/typescript/gemini-computer-use/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Kernel, type KernelContext } from '@onkernel/sdk';
import { CuaAgent } from '@onkernel/cua-agent';
import type { AssistantMessage } from '@onkernel/cua-ai';
import { KernelBrowserSession } from './session';
import { logAgentEvent } from './logging';

const kernel = new Kernel();

Expand Down Expand Up @@ -67,6 +68,7 @@ The current date is ${currentDate}.`;
systemPrompt,
},
});
agent.subscribe(logAgentEvent);

await agent.prompt(payload.query);

Expand Down
45 changes: 45 additions & 0 deletions pkg/templates/typescript/gemini-computer-use/logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { AgentEvent } from '@onkernel/cua-agent';

// Logs the agent's narration (`agent>`) and each browser action (`→`) it takes.
// Pass to `agent.subscribe(logAgentEvent)`.
export function logAgentEvent(event: AgentEvent): void {
if (event.type === 'tool_execution_start') {
console.log(` → ${describe(event.toolName, event.args)}`);
} else if (event.type === 'tool_execution_end' && event.isError) {
console.log(` ✗ ${event.toolName} failed`);
} else if (event.type === 'message_end') {
const text = narration(event.message);
if (text) console.log(`\nagent> ${text}`);
}
}

// Anthropic nests actions under `computer_batch`, OpenAI under
// `computer_use_extra`; unwrap those, then print the action name and its args.
function describe(name: string, args: any): string {
if (name === 'computer_batch' && Array.isArray(args?.actions)) {
return args.actions.map((a: any) => describe(a.type, a)).join('; ');
}
if (name === 'computer_use_extra' && typeof args?.action === 'string') {
return describe(args.action, args);
}
const params = Object.entries(args ?? {})
.filter(([key]) => key !== 'type' && key !== 'action')
.map(([key, value]) => `${key}=${short(value)}`)
.join(' ');
return params ? `${name} ${params}` : name;
}

function narration(message: any): string {
if (message?.role !== 'assistant') return '';
return message.content
.filter((block: any) => block.type === 'text')
.map((block: any) => block.text)
.join(' ')
.replace(/\s+/g, ' ')
.trim();
}

function short(value: unknown): string {
const text = typeof value === 'string' ? value : JSON.stringify(value);
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
}
2 changes: 2 additions & 0 deletions pkg/templates/typescript/openai-computer-use/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Kernel, type KernelContext } from '@onkernel/sdk';
import { CuaAgent } from '@onkernel/cua-agent';
import type { AssistantMessage } from '@onkernel/cua-ai';
import { maybeStartReplay, maybeStopReplay } from './lib/replay';
import { logAgentEvent } from './lib/logging';

const kernel = new Kernel();
const app = kernel.app('ts-openai-cua');
Expand Down Expand Up @@ -52,6 +53,7 @@ app.action<CuaInput, CuaOutput>(
systemPrompt: `You are operating a Chromium browser on a Kernel cloud VM. Use the navigation tool to open URLs directly, and review the screenshot after each action before continuing. The current date and time is ${new Date().toISOString()}.`,
},
});
agent.subscribe(logAgentEvent);

await agent.prompt(payload.task);

Expand Down
45 changes: 45 additions & 0 deletions pkg/templates/typescript/openai-computer-use/lib/logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { AgentEvent } from '@onkernel/cua-agent';

// Logs the agent's narration (`agent>`) and each browser action (`→`) it takes.
// Pass to `agent.subscribe(logAgentEvent)`.
export function logAgentEvent(event: AgentEvent): void {
if (event.type === 'tool_execution_start') {
console.log(` → ${describe(event.toolName, event.args)}`);
} else if (event.type === 'tool_execution_end' && event.isError) {
console.log(` ✗ ${event.toolName} failed`);
} else if (event.type === 'message_end') {
const text = narration(event.message);
if (text) console.log(`\nagent> ${text}`);
}
}

// Anthropic nests actions under `computer_batch`, OpenAI under
// `computer_use_extra`; unwrap those, then print the action name and its args.
function describe(name: string, args: any): string {
if (name === 'computer_batch' && Array.isArray(args?.actions)) {
return args.actions.map((a: any) => describe(a.type, a)).join('; ');
}
if (name === 'computer_use_extra' && typeof args?.action === 'string') {
return describe(args.action, args);
}
const params = Object.entries(args ?? {})
.filter(([key]) => key !== 'type' && key !== 'action')
.map(([key, value]) => `${key}=${short(value)}`)
.join(' ');
return params ? `${name} ${params}` : name;
}

function narration(message: any): string {
if (message?.role !== 'assistant') return '';
return message.content
.filter((block: any) => block.type === 'text')
.map((block: any) => block.text)
.join(' ')
.replace(/\s+/g, ' ')
.trim();
}

function short(value: unknown): string {
const text = typeof value === 'string' ? value : JSON.stringify(value);
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
}
Loading