Skip to content
Merged
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: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@askalf/agent",
"version": "3.1.6",
"version": "3.2.0",
"description": "Connect any device to an AI workforce that thinks, heals, remembers, and evolves. Nervous system signals, immune system alerts, auto-reconnect, capabilities scan. One command to install as a persistent service.",
"type": "module",
"main": "dist/index.js",
Expand Down
18 changes: 18 additions & 0 deletions src/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ interface TaskPayload {
maxBudget?: number;
credentials?: string;
mode?: ExecutionMode;
// When set, passed to Claude CLI as --model. Without this, claude falls back
// to the device's local default — which can be any tier the device's OAuth
// account happens to be on. The dispatcher should send the forge agent's
// model_id so cost stays controllable from the platform side.
model?: string;
}

export function scanCapabilities(): Record<string, unknown> {
Expand Down Expand Up @@ -328,6 +333,7 @@ export class AgentBridge {
private async handleTask(task: TaskPayload): Promise<void> {
console.log(` [${new Date().toISOString()}] Task received: ${task.agentName} (${task.executionId})`);
console.log(` Input: ${task.input.substring(0, 100)}${task.input.length > 100 ? '...' : ''}`);
if (task.model) console.log(` Model: ${task.model}`);

// Acknowledge receipt
this.send('execution:accepted', { executionId: task.executionId });
Expand Down Expand Up @@ -382,6 +388,18 @@ export class AgentBridge {
if (task.maxBudget) {
args.push('--max-budget-usd', String(task.maxBudget));
}
if (task.model) {
// Validate against an allowlist of model prefixes to avoid passing
// attacker-controlled strings into the CLI. Claude CLI itself rejects
// unknown models, but a malformed value could still spawn the process
// with surprising args.
const safe = /^[a-z0-9][a-z0-9.\-]{0,60}$/.test(task.model);
if (safe) {
args.push('--model', task.model);
} else {
console.warn(` Ignoring malformed model "${task.model}" — falling back to local default`);
}
}

// Pass prompt as a positional arg, not via stdin. Claude CLI's stdin code
// path skips API-key auth and falls back to OAuth-only — so a host running
Expand Down
Loading