@roll-agent/core 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/roll.js CHANGED
@@ -1,2 +1,40 @@
1
1
  #!/usr/bin/env node
2
- import "../dist/cli/index.js";
2
+ import { spawnSync } from "node:child_process";
3
+ import { existsSync } from "node:fs";
4
+ import { dirname, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ async function sqliteAvailable() {
8
+ try {
9
+ await import("node:sqlite");
10
+ return true;
11
+ } catch {
12
+ return false;
13
+ }
14
+ }
15
+
16
+ function sourceTreeAvailable() {
17
+ return existsSync(resolve(dirname(fileURLToPath(import.meta.url)), "../src/cli/index.ts"));
18
+ }
19
+
20
+ function hasExecFlag(flag) {
21
+ return process.execArgv.includes(flag);
22
+ }
23
+
24
+ const needsSqliteFlag = !(await sqliteAvailable());
25
+ const needsTypeStripFlag = sourceTreeAvailable() && !hasExecFlag("--experimental-strip-types");
26
+
27
+ if (!process.env.ROLL_SQLITE_RESPAWNED && (needsSqliteFlag || needsTypeStripFlag)) {
28
+ const nodeFlags = [
29
+ ...(needsSqliteFlag ? ["--experimental-sqlite"] : []),
30
+ ...(needsTypeStripFlag ? ["--experimental-strip-types"] : []),
31
+ ];
32
+ const result = spawnSync(
33
+ process.execPath,
34
+ [...nodeFlags, process.argv[1], ...process.argv.slice(2)],
35
+ { stdio: "inherit", env: { ...process.env, ROLL_SQLITE_RESPAWNED: "1" } },
36
+ );
37
+ process.exit(result.status ?? 0);
38
+ }
39
+
40
+ await import("../dist/cli/index.js");
@@ -1,3 +1,6 @@
1
+ import type { AgentSession } from "@roll-agent/runtime";
2
+ import type { ChatCommandResult } from "../../types/chat.ts";
3
+ export declare function runJsonTurn(session: AgentSession, message: string): Promise<ChatCommandResult>;
1
4
  declare const _default: import("citty").CommandDef<{
2
5
  message: {
3
6
  type: "positional";
@@ -8,10 +11,25 @@ declare const _default: import("citty").CommandDef<{
8
11
  type: "string";
9
12
  description: string;
10
13
  };
14
+ last: {
15
+ type: "boolean";
16
+ description: string;
17
+ default: false;
18
+ };
19
+ list: {
20
+ type: "boolean";
21
+ description: string;
22
+ default: false;
23
+ };
11
24
  json: {
12
25
  type: "boolean";
13
26
  description: string;
14
27
  default: false;
15
28
  };
29
+ server: {
30
+ type: "boolean";
31
+ description: string;
32
+ default: false;
33
+ };
16
34
  }>;
17
35
  export default _default;
@@ -1 +1 @@
1
- import{defineCommand as s}from"citty";import{log as e}from"../utils/output.js";const t="roll chat 仍处于 experimental 阶段,当前只提供命令骨架,不会执行会话编排、不会恢复 session,也不会隐式降级到 roll ask。";function o(){return{status:"unavailable",message:t}}function n(s){console.log(JSON.stringify(s,null,2))}export default s({meta:{description:"Experimental: 会话式入口骨架(当前不可用)"},args:{message:{type:"positional",description:"起始消息",required:!1},session:{type:"string",description:"继续已有会话的 session ID"},json:{type:"boolean",description:"JSON 格式输出",default:!1}},async run({args:s}){const t=o();s.json?n(t):e.warn(t.message),process.exitCode=1}});
1
+ import{defineCommand as e}from"citty";import{loadConfig as s}from"../../config/loader.js";import{createProviderModel as t}from"../../llm/providers.js";import{createInterface as o}from"node:readline/promises";import r from"chalk";import i from"cli-table3";import{log as n}from"../utils/output.js";import{ChatRenderer as a,clackConfirm as l}from"../utils/chat-renderer.js";function c(e,s){return new e.ConfigurableToolPolicy({defaultMode:s.runtime.approval.default,overrides:s.runtime.approval.overrides})}function d(e){const s=e.trim().replace(/\s+/g," ");return s.length>40?`${s.slice(0,39)}…`:s}function u(e){console.log(JSON.stringify(e,null,2))}function m(e){n.warn(`Agent "${e.agentName}" 启动失败:${e.message}`)}async function p(){return import("@roll-agent/runtime")}async function f(e){const s=e.runtime.provider??e.llm.defaultProvider,o=e.runtime.model??e.llm.defaultModel,r=e.llm.providers[s];if(!r)return n.error(`LLM provider "${s}" 未配置。请检查 roll.config.yaml`),void(process.exitCode=1);const i=await p(),{ConversationEngine:a,ThreadStore:l,RuntimeServer:d,createStdioConnection:u}=i,f=t(s,o,r.apiKey,r.baseUrl),g=new l(e.runtime.threadsDir),y=new a({config:e,model:f,store:g,policy:c(i,e),maxSteps:e.runtime.maxSteps,onAgentBootstrapIssue:m}),h=u(process.stdin,process.stdout),w=new d(y,h);h.onClose(()=>{w.abortAll(),y.dispose().catch(()=>{}).finally(()=>{g.close(),process.exit(0)})}),n.info("roll runtime-server 已启动(stdio JSON-RPC,等待客户端连接)")}async function g(e,s){const{ThreadStore:t}=await p(),o=new t(e.runtime.threadsDir);try{const e=o.listThreads();if(s){const s=e.map(e=>({...e,messageCount:o.countMessages(e.id)}));return void console.log(JSON.stringify(s,null,2))}if(0===e.length)return void console.log('暂无会话。用 `roll chat "<message>"` 开始一个。');const t=new i({head:["Session ID","标题","消息","更新时间"],style:{head:["cyan"]}});for(const s of e)t.push([s.id,s.title??"-",String(o.countMessages(s.id)),s.updatedAt]);console.log(t.toString())}finally{o.close()}}export async function runJsonTurn(e,s){const t=[],o=[],r=[];let i,n,a="";for await(const l of e.send(s))switch(l.type){case"text-delta":a+=l.delta;break;case"tool-call":t.push({summary:`${l.agentName}.${l.toolName}`,agentName:l.agentName,toolName:l.toolName});break;case"confirmation-required":r.push({summary:`${l.agentName}.${l.toolName}`,agentName:l.agentName,toolName:l.toolName}),e.reject(l.approvalId,"json 模式不支持交互确认");break;case"step-finish":o.push({finishReason:l.finishReason,...l.usage?{usage:l.usage}:{}});break;case"message-finish":n=l.totalUsage;break;case"error":i=l.message}return void 0!==i?{status:"failed",stage:"execute",message:i,sessionId:e.id}:r.length>0?{status:"needs_confirmation",sessionId:e.id,message:"存在需要确认的工具调用,请在交互模式下执行或显式批准",pendingActions:r}:{status:"completed",sessionId:e.id,output:a,steps:t,...o.length>0?{stepUsages:o}:{},...n?{totalUsage:n}:{}}}async function y(e,s,t){const i=o({input:process.stdin,output:process.stdout});i.on("SIGINT",()=>i.close());const l=new a(async e=>{const s=(await i.question(`${e} (y/N) `)).trim().toLowerCase();return"y"===s||"yes"===s});n.info("进入多轮对话(输入 exit / quit 或 Ctrl-C 退出)");let c=!t,u=!1;try{for(;;){let t;try{t=(await i.question(r.green("› "))).trim()}catch{break}if(0!==t.length){if("exit"===t||"quit"===t)break;c||(s.updateTitle(e.id,d(t)),c=!0),u=!0;for await(const s of e.send(t))await l.handle(s,e)}}}finally{i.close(),t&&!u&&0===s.countMessages(e.id)&&s.deleteThread(e.id)}}export default e({meta:{description:"会话式 AI 助手(多轮对话 + 自动工具调用)"},args:{message:{type:"positional",description:"起始消息",required:!1},session:{type:"string",description:"继续已有会话的 session ID"},last:{type:"boolean",description:"继续最近一个会话",default:!1},list:{type:"boolean",description:"列出已有会话",default:!1},json:{type:"boolean",description:"JSON 格式输出",default:!1},server:{type:"boolean",description:"以 JSON-RPC daemon 模式运行(stdio,供 GUI/前端接入)",default:!1}},async run({args:e}){const{config:o}=s();if(e.server)return void await f(o);if(e.list)return void await g(o,e.json);const r=o.runtime.provider??o.llm.defaultProvider,i=o.runtime.model??o.llm.defaultModel,h=o.llm.providers[r];if(!h)return n.error(`LLM provider "${r}" 未配置。请检查 roll.config.yaml`),void(process.exitCode=1);if(e.json&&!e.message)return n.error('--json 模式需要消息:roll chat "<message>" --json'),void(process.exitCode=1);const w=await p(),{ConversationEngine:v,ThreadStore:S}=w,N=t(r,i,h.apiKey,h.baseUrl),x=new S(o.runtime.threadsDir),C=new v({config:o,model:N,store:x,policy:c(w,o),maxSteps:o.runtime.maxSteps,onAgentBootstrapIssue:m});try{let s;if(e.session)s=await C.resumeSession(e.session);else if(e.last){const e=x.listThreads()[0];if(!e)return n.error('暂无可继续的会话,先用 `roll chat "<message>"` 开始一个'),void(process.exitCode=1);s=await C.resumeSession(e.id)}else s=await C.createSession(e.message?{title:d(e.message)}:{});if(e.json&&e.message){const t=await runJsonTurn(s,e.message);return u(t),void("completed"!==t.status&&(process.exitCode=1))}if(n.info(`会话 ${s.id}`),e.message){const t=new a(l);for await(const o of s.send(e.message))await t.handle(o,s)}else await y(s,x,!e.session&&!e.last)}catch(e){n.error(e instanceof Error?e.message:String(e)),process.exitCode=1}finally{await C.dispose(),x.close()}}});
@@ -88,6 +88,18 @@ export declare const CONFIG_GUIDANCE_ENTRIES: readonly [{
88
88
  readonly purpose: "`roll ask` 路由置信度低于该阈值时,倾向要求用户确认。";
89
89
  readonly defaultBehavior: "未配置时使用内置路由默认行为。";
90
90
  readonly example: "ask:\n confirm-threshold: 0.5";
91
+ }, {
92
+ readonly path: "runtime.approval.default";
93
+ readonly title: "Chat 工具确认默认策略";
94
+ readonly purpose: "`roll chat` 调用工具时的默认确认策略。`guarded` 使用内置读写启发式,`auto` 默认放行非破坏性工具,`deny` 默认拒绝。";
95
+ readonly defaultBehavior: "默认值为 `guarded`。此配置不影响 `roll ask`。" | "默认值为 `auto`。此配置不影响 `roll ask`。" | "默认值为 `deny`。此配置不影响 `roll ask`。";
96
+ readonly example: "runtime:\n approval:\n default: guarded" | "runtime:\n approval:\n default: auto" | "runtime:\n approval:\n default: deny";
97
+ }, {
98
+ readonly path: "runtime.approval.overrides";
99
+ readonly title: "Chat 工具确认精确覆盖";
100
+ readonly purpose: "`roll chat` 按完整 `agentName.toolName` 覆盖单个工具的确认策略。可选值为 `auto`、`confirm`、`deny`。";
101
+ readonly defaultBehavior: "未配置时回退到 `runtime.approval.default`。";
102
+ readonly example: "runtime:\n approval:\n overrides:\n browser-use-agent.zhipin_send_prepared_reply: confirm\n browser-use-agent.browser_status: auto";
91
103
  }, {
92
104
  readonly path: "browser.default-instance";
93
105
  readonly title: "默认浏览器实例";
@@ -1 +1 @@
1
- import{DEFAULT_CONFIG as e}from"../../config/defaults.js";import{normalizeUserPath as t}from"../../config/key-codec.js";export const INSTALL_SCENARIOS=["default-network","china-dev","private-registry","advanced"];export const CONFIG_GUIDANCE_ENTRIES=[{path:"llm.default-provider",title:"默认 LLM Provider",purpose:"`roll ask` / `roll chat` 默认使用的模型提供商。",defaultBehavior:`默认值为 \`${e.llm.defaultProvider}\`。`,example:`llm:\n default-provider: ${e.llm.defaultProvider}`,setupCommand:"roll config setup llm"},{path:"llm.default-model",title:"默认 LLM Model",purpose:"`roll ask` / `roll chat` 默认使用的模型名称。",defaultBehavior:`默认值为 \`${e.llm.defaultModel}\`。`,example:`llm:\n default-model: ${e.llm.defaultModel}`,setupCommand:"roll config setup llm"},{path:"llm.providers.<provider>.api-key",title:"Provider API Key",purpose:"指定 LLM provider 的 API key。可以写真实 key,也可以写 `${ENV_VAR}` 占位符。",example:"llm:\n providers:\n anthropic:\n api-key: ${ANTHROPIC_API_KEY}",setupCommand:"roll config setup llm"},{path:"llm.providers.<provider>.base-url",title:"Provider Base URL",purpose:"指定 LLM provider 的自定义 API base URL,通常只在代理、兼容网关或私有部署时需要。",defaultBehavior:"不配置时使用 provider SDK 默认地址。",example:"llm:\n providers:\n openai:\n base-url: https://api.openai.com/v1",setupCommand:"roll config setup llm"},{path:"install.registry",title:"npm Registry",purpose:"`roll agent install` / `roll update` 查询和安装 npm 包时使用的 registry。",defaultBehavior:"不配置时走 npm 自身默认源;Roll 不做隐式镜像 fallback。",example:"install:\n registry: https://registry.npmmirror.com",setupCommand:"roll config setup install"},{path:"install.fetch-retries",title:"npm Fetch Retries",purpose:"透传给 npm 的 `--fetch-retries`,同时影响 Roll 层整体网络重试次数。",defaultBehavior:`默认值为 \`${e.install.fetchRetries}\`。`,example:`install:\n fetch-retries: ${e.install.fetchRetries}`,setupCommand:"roll config setup install"},{path:"install.prefer-offline",title:"Prefer Offline",purpose:"安装时是否附加 `--prefer-offline`。",defaultBehavior:`默认值为 \`${e.install.preferOffline}\`,避免更新时复用过期 npm 元数据。`,example:`install:\n prefer-offline: ${e.install.preferOffline}`,setupCommand:"roll config setup install"},{path:"install.network-timeout-ms",title:"Install Network Timeout",purpose:"单次 npm install 命令的超时时间,单位毫秒。",defaultBehavior:`默认值为 \`${e.install.networkTimeoutMs}\`,即 120 秒。`,example:`install:\n network-timeout-ms: ${e.install.networkTimeoutMs}`,setupCommand:"roll config setup install"},{path:"agents.data-dir",title:"Agent 数据目录",purpose:"Roll 持久化已注册 Agent、PID、日志和 runtime sidecar 的目录。",defaultBehavior:`默认值为 \`${e.agents.dataDir}\`。`,example:`agents:\n data-dir: ${e.agents.dataDir}`},{path:"agents.env.<agent-name>",title:"Agent 环境变量",purpose:"按 Agent 名称声明注入到子 Agent 进程的环境变量。",defaultBehavior:"未配置时只会继承当前 shell 环境变量;core-managed Agent 需要重启才会看到新值。",example:"agents:\n env:\n browser-use-agent:\n REPLY_AUTHORITY_URL: https://example.com",setupCommand:"roll config setup agent <agent-name>"},{path:"ask.confirm-threshold",title:"Ask 确认阈值",purpose:"`roll ask` 路由置信度低于该阈值时,倾向要求用户确认。",defaultBehavior:"未配置时使用内置路由默认行为。",example:"ask:\n confirm-threshold: 0.5"},{path:"browser.default-instance",title:"默认浏览器实例",purpose:"多浏览器实例配置下,未显式指定 browserInstance 时使用的默认实例。",defaultBehavior:"不配置且只有一个实例时可自动推断;多个实例时工具可能返回 needs_input。",example:"browser:\n default-instance: boss-a"},{path:"browser.instances",title:"浏览器实例声明",purpose:"声明 browser-use-agent 可使用的浏览器 profile、CDP 端口、会话目录和业务归因信息。",defaultBehavior:"不配置时 browser-use-agent 使用 legacy 单实例环境变量路径。",example:"browser:\n instances:\n boss-a:\n platform: zhipin\n mode: managed-cdp\n cdp-port: 9222\n user-data-dir: ~/.roll-agent/browser/boss-a"}];export function listConfigGuidanceEntries(){return CONFIG_GUIDANCE_ENTRIES}export function findConfigGuidance(e){const r=t(e.split(".")).join(".");return CONFIG_GUIDANCE_ENTRIES.find(e=>l(e.path,r))}function l(e,l){const r=t(e.split(".")).join(".");if(r===l)return!0;const a=r.split("."),n=l.split(".");return a.length===n.length&&a.every((e,t)=>e.startsWith("<")||e===n[t])}export function flattenAgentEnvDeclarations(e){return[...(e?.required??[]).map(e=>({...e,required:!0})),...(e?.optional??[]).map(e=>({...e,required:!1}))]}export function isSecretEnvName(e){return/(?:TOKEN|KEY|SECRET|PASSWORD)/iu.test(e)}
1
+ import{DEFAULT_CONFIG as e}from"../../config/defaults.js";import{normalizeUserPath as t}from"../../config/key-codec.js";export const INSTALL_SCENARIOS=["default-network","china-dev","private-registry","advanced"];export const CONFIG_GUIDANCE_ENTRIES=[{path:"llm.default-provider",title:"默认 LLM Provider",purpose:"`roll ask` / `roll chat` 默认使用的模型提供商。",defaultBehavior:`默认值为 \`${e.llm.defaultProvider}\`。`,example:`llm:\n default-provider: ${e.llm.defaultProvider}`,setupCommand:"roll config setup llm"},{path:"llm.default-model",title:"默认 LLM Model",purpose:"`roll ask` / `roll chat` 默认使用的模型名称。",defaultBehavior:`默认值为 \`${e.llm.defaultModel}\`。`,example:`llm:\n default-model: ${e.llm.defaultModel}`,setupCommand:"roll config setup llm"},{path:"llm.providers.<provider>.api-key",title:"Provider API Key",purpose:"指定 LLM provider 的 API key。可以写真实 key,也可以写 `${ENV_VAR}` 占位符。",example:"llm:\n providers:\n anthropic:\n api-key: ${ANTHROPIC_API_KEY}",setupCommand:"roll config setup llm"},{path:"llm.providers.<provider>.base-url",title:"Provider Base URL",purpose:"指定 LLM provider 的自定义 API base URL,通常只在代理、兼容网关或私有部署时需要。",defaultBehavior:"不配置时使用 provider SDK 默认地址。",example:"llm:\n providers:\n openai:\n base-url: https://api.openai.com/v1",setupCommand:"roll config setup llm"},{path:"install.registry",title:"npm Registry",purpose:"`roll agent install` / `roll update` 查询和安装 npm 包时使用的 registry。",defaultBehavior:"不配置时走 npm 自身默认源;Roll 不做隐式镜像 fallback。",example:"install:\n registry: https://registry.npmmirror.com",setupCommand:"roll config setup install"},{path:"install.fetch-retries",title:"npm Fetch Retries",purpose:"透传给 npm 的 `--fetch-retries`,同时影响 Roll 层整体网络重试次数。",defaultBehavior:`默认值为 \`${e.install.fetchRetries}\`。`,example:`install:\n fetch-retries: ${e.install.fetchRetries}`,setupCommand:"roll config setup install"},{path:"install.prefer-offline",title:"Prefer Offline",purpose:"安装时是否附加 `--prefer-offline`。",defaultBehavior:`默认值为 \`${e.install.preferOffline}\`,避免更新时复用过期 npm 元数据。`,example:`install:\n prefer-offline: ${e.install.preferOffline}`,setupCommand:"roll config setup install"},{path:"install.network-timeout-ms",title:"Install Network Timeout",purpose:"单次 npm install 命令的超时时间,单位毫秒。",defaultBehavior:`默认值为 \`${e.install.networkTimeoutMs}\`,即 120 秒。`,example:`install:\n network-timeout-ms: ${e.install.networkTimeoutMs}`,setupCommand:"roll config setup install"},{path:"agents.data-dir",title:"Agent 数据目录",purpose:"Roll 持久化已注册 Agent、PID、日志和 runtime sidecar 的目录。",defaultBehavior:`默认值为 \`${e.agents.dataDir}\`。`,example:`agents:\n data-dir: ${e.agents.dataDir}`},{path:"agents.env.<agent-name>",title:"Agent 环境变量",purpose:"按 Agent 名称声明注入到子 Agent 进程的环境变量。",defaultBehavior:"未配置时只会继承当前 shell 环境变量;core-managed Agent 需要重启才会看到新值。",example:"agents:\n env:\n browser-use-agent:\n REPLY_AUTHORITY_URL: https://example.com",setupCommand:"roll config setup agent <agent-name>"},{path:"ask.confirm-threshold",title:"Ask 确认阈值",purpose:"`roll ask` 路由置信度低于该阈值时,倾向要求用户确认。",defaultBehavior:"未配置时使用内置路由默认行为。",example:"ask:\n confirm-threshold: 0.5"},{path:"runtime.approval.default",title:"Chat 工具确认默认策略",purpose:"`roll chat` 调用工具时的默认确认策略。`guarded` 使用内置读写启发式,`auto` 默认放行非破坏性工具,`deny` 默认拒绝。",defaultBehavior:`默认值为 \`${e.runtime.approval.default}\`。此配置不影响 \`roll ask\`。`,example:`runtime:\n approval:\n default: ${e.runtime.approval.default}`},{path:"runtime.approval.overrides",title:"Chat 工具确认精确覆盖",purpose:"`roll chat` 按完整 `agentName.toolName` 覆盖单个工具的确认策略。可选值为 `auto`、`confirm`、`deny`。",defaultBehavior:"未配置时回退到 `runtime.approval.default`。",example:"runtime:\n approval:\n overrides:\n browser-use-agent.zhipin_send_prepared_reply: confirm\n browser-use-agent.browser_status: auto"},{path:"browser.default-instance",title:"默认浏览器实例",purpose:"多浏览器实例配置下,未显式指定 browserInstance 时使用的默认实例。",defaultBehavior:"不配置且只有一个实例时可自动推断;多个实例时工具可能返回 needs_input。",example:"browser:\n default-instance: boss-a"},{path:"browser.instances",title:"浏览器实例声明",purpose:"声明 browser-use-agent 可使用的浏览器 profile、CDP 端口、会话目录和业务归因信息。",defaultBehavior:"不配置时 browser-use-agent 使用 legacy 单实例环境变量路径。",example:"browser:\n instances:\n boss-a:\n platform: zhipin\n mode: managed-cdp\n cdp-port: 9222\n user-data-dir: ~/.roll-agent/browser/boss-a"}];export function listConfigGuidanceEntries(){return CONFIG_GUIDANCE_ENTRIES}export function findConfigGuidance(e){const r=t(e.split(".")).join(".");return CONFIG_GUIDANCE_ENTRIES.find(e=>l(e.path,r))}function l(e,l){const r=t(e.split(".")).join(".");if(r===l)return!0;const a=r.split("."),n=l.split(".");return a.length===n.length&&a.every((e,t)=>e.startsWith("<")||e===n[t])}export function flattenAgentEnvDeclarations(e){return[...(e?.required??[]).map(e=>({...e,required:!0})),...(e?.optional??[]).map(e=>({...e,required:!1}))]}export function isSecretEnvName(e){return/(?:TOKEN|KEY|SECRET|PASSWORD)/iu.test(e)}
@@ -0,0 +1,15 @@
1
+ import type { SessionEvent } from "@roll-agent/runtime";
2
+ export interface ChatApprover {
3
+ approve(approvalId: string): void;
4
+ reject(approvalId: string, reason?: string): void;
5
+ }
6
+ export type ChatConfirm = (message: string) => Promise<boolean>;
7
+ export declare const clackConfirm: ChatConfirm;
8
+ export declare class ChatRenderer {
9
+ private readonly confirm;
10
+ private readonly spinners;
11
+ private streaming;
12
+ constructor(confirm: ChatConfirm);
13
+ handle(event: SessionEvent, approver: ChatApprover): Promise<void>;
14
+ private flushLine;
15
+ }
@@ -0,0 +1 @@
1
+ import t from"chalk";import{confirm as s,isCancel as e}from"@clack/prompts";import{createSpinner as o,log as n,redactToolArgsForLog as r}from"./output.js";export const clackConfirm=async t=>{const o=await s({message:t});return!e(o)&&!0===o};function a(t){const s=JSON.stringify(r(t));return s.length>80?`${s.slice(0,79)}…`:s}function i(t){const s=[];return void 0!==t.inputTokens&&s.push(`in ${String(t.inputTokens)}`),void 0!==t.outputTokens&&s.push(`out ${String(t.outputTokens)}`),void 0!==t.totalTokens&&s.push(`total ${String(t.totalTokens)}`),s.join(" · ")}export class ChatRenderer{confirm;spinners=new Map;streaming=!1;constructor(t){this.confirm=t}async handle(s,e){switch(s.type){case"text-delta":process.stdout.write(s.delta),this.streaming=!0;break;case"tool-call":{this.flushLine();const e=o(`${t.cyan(`${s.agentName}.${s.toolName}`)} ${t.gray(a(s.input))}`);e.start(),this.spinners.set(s.toolCallId,e);break}case"tool-result":{const t=this.spinners.get(s.toolCallId);t&&(s.isError?t.fail():t.succeed(),this.spinners.delete(s.toolCallId));break}case"confirmation-required":{this.flushLine();const t=s.reason?`(${s.reason})`:"";await this.confirm(`执行 ${s.agentName}.${s.toolName}${t}?`)?e.approve(s.approvalId):e.reject(s.approvalId,"用户取消");break}case"error":this.flushLine(),n.error(s.message);break;case"message-finish":this.flushLine(),s.totalUsage&&n.debug(`tokens: ${i(s.totalUsage)}`)}}flushLine(){this.streaming&&(process.stdout.write("\n"),this.streaming=!1)}}
@@ -1 +1 @@
1
- export const LLM_PROVIDER_OPTIONS=["anthropic","openai","qwen","deepseek"];export const DEFAULT_LLM_PROVIDER="anthropic";export const DEFAULT_LLM_MODELS={anthropic:"claude-sonnet-4-6",openai:"gpt-5.5",qwen:"qwen3.6-plus",deepseek:"deepseek-v4-flash"};export const DEFAULT_CONFIG={llm:{defaultProvider:"anthropic",defaultModel:DEFAULT_LLM_MODELS.anthropic,providers:{}},ask:{},agents:{dataDir:"~/.roll-agent/agents"},install:{fetchRetries:3,preferOffline:!1,networkTimeoutMs:12e4},browser:{instances:{}}};export const CONFIG_FILE_NAMES=["roll.config.yaml","roll.config.yml"];
1
+ export const LLM_PROVIDER_OPTIONS=["anthropic","openai","qwen","deepseek"];export const DEFAULT_LLM_PROVIDER="anthropic";export const DEFAULT_LLM_MODELS={anthropic:"claude-sonnet-4-6",openai:"gpt-5.5",qwen:"qwen3.6-plus",deepseek:"deepseek-v4-flash"};export const DEFAULT_CONFIG={llm:{defaultProvider:"anthropic",defaultModel:DEFAULT_LLM_MODELS.anthropic,providers:{}},ask:{},runtime:{maxSteps:16,threadsDir:"~/.roll-agent/threads",approval:{default:"guarded",overrides:{}}},agents:{dataDir:"~/.roll-agent/agents"},install:{fetchRetries:3,preferOffline:!1,networkTimeoutMs:12e4},browser:{instances:{}}};export const CONFIG_FILE_NAMES=["roll.config.yaml","roll.config.yml"];
@@ -1 +1 @@
1
- const e={kind:"leaf"},r={kind:"object",fields:{apiKey:e,baseUrl:e}},o={kind:"object",fields:{platform:e,mode:e,headless:e,cdpUrl:e,cdpHost:e,cdpPort:e,channel:e,executablePath:e,userDataDir:e,sessionsDir:e,args:e,profileName:e,profileColor:e,windowBounds:{kind:"object",fields:{x:e,y:e,width:e,height:e}},trackingAgentId:e}};export const CONFIG_KEY_CODEC={kind:"object",fields:{llm:{kind:"object",fields:{defaultProvider:e,defaultModel:e,providers:{kind:"record",value:r}}},ask:{kind:"object",fields:{llmModel:e,confirmThreshold:e}},agents:{kind:"object",fields:{dataDir:e,env:{kind:"record",value:{kind:"record",value:e}}}},install:{kind:"object",fields:{registry:e,fetchRetries:e,preferOffline:e,networkTimeoutMs:e}},browser:{kind:"object",fields:{defaultInstance:e,instances:{kind:"record",value:o}}}}};export function kebabToCamel(e){return e.replace(/-([a-z])/g,(e,r)=>r.toUpperCase())}export function camelToKebab(e){return e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}function t(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}export function decodeFromYaml(r,o=CONFIG_KEY_CODEC){if(Array.isArray(r))return r.map(e=>decodeFromYaml(e,o));if(!t(r))return r;if("object"===o.kind){const t={};for(const[n,i]of Object.entries(r)){const r=kebabToCamel(n),d=o.fields[r]??e;t[r]=decodeFromYaml(i,d)}return t}if("record"===o.kind){const e={};for(const[t,n]of Object.entries(r))e[t]=decodeFromYaml(n,o.value);return e}return r}function n(r,o){const t=[];let n=CONFIG_KEY_CODEC;for(const i of r)if("object"===n.kind){const r=kebabToCamel(i),d=n.fields[r];d?(t.push("camel"===o?r:camelToKebab(r)),n=d):(t.push(i),n=e)}else"record"===n.kind?(t.push(i),n=n.value):t.push(i);return t}export function encodePathToYaml(e){return n(e,"kebab")}export function normalizeUserPath(e){return n(e,"camel")}
1
+ const e={kind:"leaf"},r={kind:"object",fields:{apiKey:e,baseUrl:e}},o={kind:"object",fields:{platform:e,mode:e,headless:e,cdpUrl:e,cdpHost:e,cdpPort:e,channel:e,executablePath:e,userDataDir:e,sessionsDir:e,args:e,profileName:e,profileColor:e,windowBounds:{kind:"object",fields:{x:e,y:e,width:e,height:e}},trackingAgentId:e}};export const CONFIG_KEY_CODEC={kind:"object",fields:{llm:{kind:"object",fields:{defaultProvider:e,defaultModel:e,providers:{kind:"record",value:r}}},ask:{kind:"object",fields:{llmModel:e,confirmThreshold:e}},runtime:{kind:"object",fields:{provider:e,model:e,maxSteps:e,threadsDir:e,approval:{kind:"object",fields:{default:e,overrides:{kind:"record",value:e}}}}},agents:{kind:"object",fields:{dataDir:e,env:{kind:"record",value:{kind:"record",value:e}}}},install:{kind:"object",fields:{registry:e,fetchRetries:e,preferOffline:e,networkTimeoutMs:e}},browser:{kind:"object",fields:{defaultInstance:e,instances:{kind:"record",value:o}}}}};export function kebabToCamel(e){return e.replace(/-([a-z])/g,(e,r)=>r.toUpperCase())}export function camelToKebab(e){return e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}function t(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}export function decodeFromYaml(r,o=CONFIG_KEY_CODEC){if(Array.isArray(r))return r.map(e=>decodeFromYaml(e,o));if(!t(r))return r;if("object"===o.kind){const t={};for(const[n,i]of Object.entries(r)){const r=kebabToCamel(n),d=o.fields[r]??e;t[r]=decodeFromYaml(i,d)}return t}if("record"===o.kind){const e={};for(const[t,n]of Object.entries(r))e[t]=decodeFromYaml(n,o.value);return e}return r}function n(r,o){const t=[];let n=CONFIG_KEY_CODEC;for(const i of r)if("object"===n.kind){const r=kebabToCamel(i),d=n.fields[r];d?(t.push("camel"===o?r:camelToKebab(r)),n=d):(t.push(i),n=e)}else"record"===n.kind?(t.push(i),n=n.value):t.push(i);return t}export function encodePathToYaml(e){return n(e,"kebab")}export function normalizeUserPath(e){return n(e,"camel")}
@@ -1 +1 @@
1
- import{readFileSync as n,existsSync as r}from"node:fs";import{resolve as t}from"node:path";import{parse as o}from"yaml";import{agentsConfigSchema as e,installConfigSchema as i,rollConfigSchema as s}from"./schema.js";import{DEFAULT_CONFIG as a,CONFIG_FILE_NAMES as f}from"./defaults.js";import{decodeFromYaml as c}from"./key-codec.js";import{detectKnownConfigMigrations as u,formatConfigMigrationError as l}from"./migration.js";function g(n){if("string"==typeof n)return n.replace(/\$\{([^}]+)\}/g,(n,r)=>process.env[r]??n);if(Array.isArray(n))return n.map(g);if(d(n)){const r={};for(const[t,o]of Object.entries(n))r[t]=g(o);return r}return n}function d(n){return"object"==typeof n&&null!==n&&!Array.isArray(n)}function p(n){return d(n)&&"number"==typeof n.line&&"number"==typeof n.col}function h(n){return n instanceof Error&&"linePos"in n&&Array.isArray(n.linePos)&&n.linePos.length>0&&n.linePos.every(p)}function m(n,r){const t=`Invalid YAML syntax in config file: ${n}`;if(!(r instanceof Error))return`${t}\n${String(r)}`;if(h(r)){const[n]=r.linePos;if(n)return`${t} at line ${n.line}, column ${n.col}\n${r.message}`}return`${t}\n${r.message}`}function w(n){let o=t(n);const e=t("/");for(;o!==e;){for(const n of f){const e=t(o,n);if(r(e))return e}const n=t(o,"..");if(n===o)break;o=n}}function v(n){if(n.startsWith("~/")){const r=process.env.HOME??process.env.USERPROFILE??"";return t(r,n.slice(2))}return n}function $(n){return{...n,agents:{...n.agents,dataDir:v(n.agents.dataDir)},browser:{...n.browser,instances:Object.fromEntries(Object.entries(n.browser.instances).map(([n,r])=>[n,{...r,userDataDir:v(r.userDataDir),...void 0!==r.sessionsDir?{sessionsDir:v(r.sessionsDir)}:{}}]))}}}const C={notFound:"not-found",valid:"valid",needsMigration:"needs-migration",invalid:"invalid"};export function parseConfigDocument(n,r){let t;try{t=o(n)}catch(n){throw new Error(m(r,n),{cause:n instanceof Error?n:void 0})}if(!d(t))throw new Error(`Invalid config file: ${r} (expected YAML object)`);return t}function P(n,r,t={}){const o=parseConfigDocument(n,r),e=u(o,t);if(e.needsMigration)throw new Error(l(r,e));return o}export function validateConfigText(n,r){const t=P(n,r),o=g(c(t));if(!d(o))throw new Error(`Invalid config file: ${r} (expected YAML object)`);const e=j(a,o),i=s.safeParse(e);if(!i.success){const n=i.error.issues.map(n=>` - ${n.path.join(".")}: ${n.message}`).join("\n");throw new Error(`Config validation failed (${r}):\n${n}`)}return $(i.data)}function E(n,r){const t=P(n,r,{scope:"agents"}),o=g(c(t));if(!d(o))throw new Error(`Invalid config file: ${r} (expected YAML object)`);const i=d(o.agents)?o.agents:{},s=j(a.agents,i),f=e.safeParse(s);if(!f.success){const n=f.error.issues.map(n=>` - agents.${n.path.join(".")}: ${n.message}`).join("\n");throw new Error(`Config validation failed (${r}):\n${n}`)}return{...f.data,dataDir:v(f.data.dataDir)}}function y(n,r){const t=parseConfigDocument(n,r),o=g(c(t));if(!d(o))throw new Error(`Invalid config file: ${r} (expected YAML object)`);const e=d(o.install)?o.install:{},s=j(a.install,e),f=i.safeParse(s);if(!f.success){const n=f.error.issues.map(n=>` - install.${n.path.join(".")}: ${n.message}`).join("\n");throw new Error(`Config validation failed (${r}):\n${n}`)}return f.data}export function loadInstallConfig(t={}){const o=resolveConfigPath(t);if(!o)return{installConfig:a.install,configPath:void 0};if(!r(o))throw new Error(`Config file not found: ${o}`);return{installConfig:y(n(o,"utf-8"),o),configPath:o}}export function loadConfig(t={}){const o=resolveConfigPath(t);if(!o)return{config:$(a),configPath:void 0};if(!r(o))throw new Error(`Config file not found: ${o}`);return{config:validateConfigText(n(o,"utf-8"),o),configPath:o}}export function loadAgentsConfig(t={}){const o=resolveConfigPath(t);if(!o)return{agentsConfig:{...a.agents,dataDir:v(a.agents.dataDir)},configPath:void 0};if(!r(o))throw new Error(`Config file not found: ${o}`);return{agentsConfig:E(n(o,"utf-8"),o),configPath:o}}export function resolveConfigPath(n={}){const{configPath:r,cwd:t=process.cwd()}=n;return r??w(t)}export function inspectConfigFile(t={}){const o=resolveConfigPath(t);if(!o)return{status:C.notFound,configPath:void 0};if(!r(o))return{status:C.invalid,configPath:o,raw:"",error:new Error(`Config file not found: ${o}`)};const e=n(o,"utf-8");let i;try{i=parseConfigDocument(e,o)}catch(n){return{status:C.invalid,configPath:o,raw:e,error:n instanceof Error?n:new Error(String(n))}}const s=u(i);if(s.needsMigration)return{status:C.needsMigration,configPath:o,raw:e,report:s};try{return{status:C.valid,configPath:o,config:validateConfigText(e,o)}}catch(n){return{status:C.invalid,configPath:o,raw:e,error:n instanceof Error?n:new Error(String(n))}}}function j(n,r){const t={...n};for(const[o,e]of Object.entries(r)){const r=n[o];"object"!=typeof e||null===e||Array.isArray(e)||"object"!=typeof r||null===r||Array.isArray(r)?t[o]=e:t[o]=j(r,e)}return t}
1
+ import{readFileSync as n,existsSync as r}from"node:fs";import{resolve as t}from"node:path";import{parse as o}from"yaml";import{agentsConfigSchema as e,installConfigSchema as i,rollConfigSchema as s}from"./schema.js";import{DEFAULT_CONFIG as a,CONFIG_FILE_NAMES as f}from"./defaults.js";import{decodeFromYaml as c}from"./key-codec.js";import{detectKnownConfigMigrations as u,formatConfigMigrationError as l}from"./migration.js";function g(n){if("string"==typeof n)return n.replace(/\$\{([^}]+)\}/g,(n,r)=>process.env[r]??n);if(Array.isArray(n))return n.map(g);if(d(n)){const r={};for(const[t,o]of Object.entries(n))r[t]=g(o);return r}return n}function d(n){return"object"==typeof n&&null!==n&&!Array.isArray(n)}function p(n){return d(n)&&"number"==typeof n.line&&"number"==typeof n.col}function h(n){return n instanceof Error&&"linePos"in n&&Array.isArray(n.linePos)&&n.linePos.length>0&&n.linePos.every(p)}function m(n,r){const t=`Invalid YAML syntax in config file: ${n}`;if(!(r instanceof Error))return`${t}\n${String(r)}`;if(h(r)){const[n]=r.linePos;if(n)return`${t} at line ${n.line}, column ${n.col}\n${r.message}`}return`${t}\n${r.message}`}function w(n){let o=t(n);const e=t("/");for(;o!==e;){for(const n of f){const e=t(o,n);if(r(e))return e}const n=t(o,"..");if(n===o)break;o=n}}function v(n){if(n.startsWith("~/")){const r=process.env.HOME??process.env.USERPROFILE??"";return t(r,n.slice(2))}return n}function $(n){return{...n,agents:{...n.agents,dataDir:v(n.agents.dataDir)},runtime:{...n.runtime,threadsDir:v(n.runtime.threadsDir)},browser:{...n.browser,instances:Object.fromEntries(Object.entries(n.browser.instances).map(([n,r])=>[n,{...r,userDataDir:v(r.userDataDir),...void 0!==r.sessionsDir?{sessionsDir:v(r.sessionsDir)}:{}}]))}}}const C={notFound:"not-found",valid:"valid",needsMigration:"needs-migration",invalid:"invalid"};export function parseConfigDocument(n,r){let t;try{t=o(n)}catch(n){throw new Error(m(r,n),{cause:n instanceof Error?n:void 0})}if(!d(t))throw new Error(`Invalid config file: ${r} (expected YAML object)`);return t}function P(n,r,t={}){const o=parseConfigDocument(n,r),e=u(o,t);if(e.needsMigration)throw new Error(l(r,e));return o}export function validateConfigText(n,r){const t=P(n,r),o=g(c(t));if(!d(o))throw new Error(`Invalid config file: ${r} (expected YAML object)`);const e=j(a,o),i=s.safeParse(e);if(!i.success){const n=i.error.issues.map(n=>` - ${n.path.join(".")}: ${n.message}`).join("\n");throw new Error(`Config validation failed (${r}):\n${n}`)}return $(i.data)}function E(n,r){const t=P(n,r,{scope:"agents"}),o=g(c(t));if(!d(o))throw new Error(`Invalid config file: ${r} (expected YAML object)`);const i=d(o.agents)?o.agents:{},s=j(a.agents,i),f=e.safeParse(s);if(!f.success){const n=f.error.issues.map(n=>` - agents.${n.path.join(".")}: ${n.message}`).join("\n");throw new Error(`Config validation failed (${r}):\n${n}`)}return{...f.data,dataDir:v(f.data.dataDir)}}function y(n,r){const t=parseConfigDocument(n,r),o=g(c(t));if(!d(o))throw new Error(`Invalid config file: ${r} (expected YAML object)`);const e=d(o.install)?o.install:{},s=j(a.install,e),f=i.safeParse(s);if(!f.success){const n=f.error.issues.map(n=>` - install.${n.path.join(".")}: ${n.message}`).join("\n");throw new Error(`Config validation failed (${r}):\n${n}`)}return f.data}export function loadInstallConfig(t={}){const o=resolveConfigPath(t);if(!o)return{installConfig:a.install,configPath:void 0};if(!r(o))throw new Error(`Config file not found: ${o}`);return{installConfig:y(n(o,"utf-8"),o),configPath:o}}export function loadConfig(t={}){const o=resolveConfigPath(t);if(!o)return{config:$(a),configPath:void 0};if(!r(o))throw new Error(`Config file not found: ${o}`);return{config:validateConfigText(n(o,"utf-8"),o),configPath:o}}export function loadAgentsConfig(t={}){const o=resolveConfigPath(t);if(!o)return{agentsConfig:{...a.agents,dataDir:v(a.agents.dataDir)},configPath:void 0};if(!r(o))throw new Error(`Config file not found: ${o}`);return{agentsConfig:E(n(o,"utf-8"),o),configPath:o}}export function resolveConfigPath(n={}){const{configPath:r,cwd:t=process.cwd()}=n;return r??w(t)}export function inspectConfigFile(t={}){const o=resolveConfigPath(t);if(!o)return{status:C.notFound,configPath:void 0};if(!r(o))return{status:C.invalid,configPath:o,raw:"",error:new Error(`Config file not found: ${o}`)};const e=n(o,"utf-8");let i;try{i=parseConfigDocument(e,o)}catch(n){return{status:C.invalid,configPath:o,raw:e,error:n instanceof Error?n:new Error(String(n))}}const s=u(i);if(s.needsMigration)return{status:C.needsMigration,configPath:o,raw:e,report:s};try{return{status:C.valid,configPath:o,config:validateConfigText(e,o)}}catch(n){return{status:C.invalid,configPath:o,raw:e,error:n instanceof Error?n:new Error(String(n))}}}function j(n,r){const t={...n};for(const[o,e]of Object.entries(r)){const r=n[o];"object"!=typeof e||null===e||Array.isArray(e)||"object"!=typeof r||null===r||Array.isArray(r)?t[o]=e:t[o]=j(r,e)}return t}
@@ -22,13 +22,13 @@ export declare const BrowserSecurityDiagnosticSchema: z.ZodObject<{
22
22
  domainAllowlist: string[];
23
23
  maxPageContentBytes: number;
24
24
  maxSnapshotNodes: number;
25
- actionPolicy: "log" | "deny" | "confirm";
25
+ actionPolicy: "deny" | "confirm" | "log";
26
26
  foregroundPolicy: "never" | "when-minimized" | "always";
27
27
  }, {
28
28
  domainAllowlist: string[];
29
29
  maxPageContentBytes: number;
30
30
  maxSnapshotNodes: number;
31
- actionPolicy: "log" | "deny" | "confirm";
31
+ actionPolicy: "deny" | "confirm" | "log";
32
32
  foregroundPolicy?: "never" | "when-minimized" | "always" | undefined;
33
33
  }>;
34
34
  export declare const BrowserUseToolPolicyDiagnosticSchema: z.ZodObject<{
@@ -36,18 +36,18 @@ export declare const BrowserUseToolPolicyDiagnosticSchema: z.ZodObject<{
36
36
  tools: z.ZodRecord<z.ZodString, z.ZodObject<{
37
37
  policy: z.ZodEnum<["log", "deny", "confirm"]>;
38
38
  }, "strip", z.ZodTypeAny, {
39
- policy: "log" | "deny" | "confirm";
39
+ policy: "deny" | "confirm" | "log";
40
40
  }, {
41
- policy: "log" | "deny" | "confirm";
41
+ policy: "deny" | "confirm" | "log";
42
42
  }>>;
43
43
  }, "strip", z.ZodTypeAny, {
44
44
  tools: Record<string, {
45
- policy: "log" | "deny" | "confirm";
45
+ policy: "deny" | "confirm" | "log";
46
46
  }>;
47
47
  approvalTtlMs: number;
48
48
  }, {
49
49
  tools: Record<string, {
50
- policy: "log" | "deny" | "confirm";
50
+ policy: "deny" | "confirm" | "log";
51
51
  }>;
52
52
  approvalTtlMs: number;
53
53
  }>;
@@ -164,13 +164,13 @@ export declare const AgentRuntimeEnvDiagnosticPayloadSchema: z.ZodObject<{
164
164
  domainAllowlist: string[];
165
165
  maxPageContentBytes: number;
166
166
  maxSnapshotNodes: number;
167
- actionPolicy: "log" | "deny" | "confirm";
167
+ actionPolicy: "deny" | "confirm" | "log";
168
168
  foregroundPolicy: "never" | "when-minimized" | "always";
169
169
  }, {
170
170
  domainAllowlist: string[];
171
171
  maxPageContentBytes: number;
172
172
  maxSnapshotNodes: number;
173
- actionPolicy: "log" | "deny" | "confirm";
173
+ actionPolicy: "deny" | "confirm" | "log";
174
174
  foregroundPolicy?: "never" | "when-minimized" | "always" | undefined;
175
175
  }>>;
176
176
  toolPolicy: z.ZodOptional<z.ZodObject<{
@@ -178,18 +178,18 @@ export declare const AgentRuntimeEnvDiagnosticPayloadSchema: z.ZodObject<{
178
178
  tools: z.ZodRecord<z.ZodString, z.ZodObject<{
179
179
  policy: z.ZodEnum<["log", "deny", "confirm"]>;
180
180
  }, "strip", z.ZodTypeAny, {
181
- policy: "log" | "deny" | "confirm";
181
+ policy: "deny" | "confirm" | "log";
182
182
  }, {
183
- policy: "log" | "deny" | "confirm";
183
+ policy: "deny" | "confirm" | "log";
184
184
  }>>;
185
185
  }, "strip", z.ZodTypeAny, {
186
186
  tools: Record<string, {
187
- policy: "log" | "deny" | "confirm";
187
+ policy: "deny" | "confirm" | "log";
188
188
  }>;
189
189
  approvalTtlMs: number;
190
190
  }, {
191
191
  tools: Record<string, {
192
- policy: "log" | "deny" | "confirm";
192
+ policy: "deny" | "confirm" | "log";
193
193
  }>;
194
194
  approvalTtlMs: number;
195
195
  }>>;
@@ -316,12 +316,12 @@ export declare const AgentRuntimeEnvDiagnosticPayloadSchema: z.ZodObject<{
316
316
  domainAllowlist: string[];
317
317
  maxPageContentBytes: number;
318
318
  maxSnapshotNodes: number;
319
- actionPolicy: "log" | "deny" | "confirm";
319
+ actionPolicy: "deny" | "confirm" | "log";
320
320
  foregroundPolicy: "never" | "when-minimized" | "always";
321
321
  } | undefined;
322
322
  toolPolicy?: {
323
323
  tools: Record<string, {
324
- policy: "log" | "deny" | "confirm";
324
+ policy: "deny" | "confirm" | "log";
325
325
  }>;
326
326
  approvalTtlMs: number;
327
327
  } | undefined;
@@ -360,12 +360,12 @@ export declare const AgentRuntimeEnvDiagnosticPayloadSchema: z.ZodObject<{
360
360
  domainAllowlist: string[];
361
361
  maxPageContentBytes: number;
362
362
  maxSnapshotNodes: number;
363
- actionPolicy: "log" | "deny" | "confirm";
363
+ actionPolicy: "deny" | "confirm" | "log";
364
364
  foregroundPolicy?: "never" | "when-minimized" | "always" | undefined;
365
365
  } | undefined;
366
366
  toolPolicy?: {
367
367
  tools: Record<string, {
368
- policy: "log" | "deny" | "confirm";
368
+ policy: "deny" | "confirm" | "log";
369
369
  }>;
370
370
  approvalTtlMs: number;
371
371
  } | undefined;
@@ -63,6 +63,50 @@ export declare const askConfigSchema: z.ZodObject<{
63
63
  llmModel?: string | undefined;
64
64
  confirmThreshold?: number | undefined;
65
65
  }>;
66
+ export declare const runtimeApprovalConfigSchema: z.ZodObject<{
67
+ default: z.ZodDefault<z.ZodEnum<["guarded", "auto", "deny"]>>;
68
+ overrides: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<["auto", "confirm", "deny"]>>>;
69
+ }, "strip", z.ZodTypeAny, {
70
+ default: "guarded" | "auto" | "deny";
71
+ overrides: Record<string, "auto" | "deny" | "confirm">;
72
+ }, {
73
+ default?: "guarded" | "auto" | "deny" | undefined;
74
+ overrides?: Record<string, "auto" | "deny" | "confirm"> | undefined;
75
+ }>;
76
+ export declare const runtimeConfigSchema: z.ZodObject<{
77
+ provider: z.ZodOptional<z.ZodString>;
78
+ model: z.ZodOptional<z.ZodString>;
79
+ maxSteps: z.ZodDefault<z.ZodNumber>;
80
+ threadsDir: z.ZodDefault<z.ZodString>;
81
+ approval: z.ZodDefault<z.ZodObject<{
82
+ default: z.ZodDefault<z.ZodEnum<["guarded", "auto", "deny"]>>;
83
+ overrides: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<["auto", "confirm", "deny"]>>>;
84
+ }, "strip", z.ZodTypeAny, {
85
+ default: "guarded" | "auto" | "deny";
86
+ overrides: Record<string, "auto" | "deny" | "confirm">;
87
+ }, {
88
+ default?: "guarded" | "auto" | "deny" | undefined;
89
+ overrides?: Record<string, "auto" | "deny" | "confirm"> | undefined;
90
+ }>>;
91
+ }, "strip", z.ZodTypeAny, {
92
+ maxSteps: number;
93
+ threadsDir: string;
94
+ approval: {
95
+ default: "guarded" | "auto" | "deny";
96
+ overrides: Record<string, "auto" | "deny" | "confirm">;
97
+ };
98
+ provider?: string | undefined;
99
+ model?: string | undefined;
100
+ }, {
101
+ provider?: string | undefined;
102
+ model?: string | undefined;
103
+ maxSteps?: number | undefined;
104
+ threadsDir?: string | undefined;
105
+ approval?: {
106
+ default?: "guarded" | "auto" | "deny" | undefined;
107
+ overrides?: Record<string, "auto" | "deny" | "confirm"> | undefined;
108
+ } | undefined;
109
+ }>;
66
110
  export declare const agentsConfigSchema: z.ZodObject<{
67
111
  dataDir: z.ZodString;
68
112
  /** per-agent 环境变量:键为 agent name,值为 key-value 对 */
@@ -470,6 +514,40 @@ export declare const rollConfigSchema: z.ZodObject<{
470
514
  llmModel?: string | undefined;
471
515
  confirmThreshold?: number | undefined;
472
516
  }>;
517
+ runtime: z.ZodDefault<z.ZodObject<{
518
+ provider: z.ZodOptional<z.ZodString>;
519
+ model: z.ZodOptional<z.ZodString>;
520
+ maxSteps: z.ZodDefault<z.ZodNumber>;
521
+ threadsDir: z.ZodDefault<z.ZodString>;
522
+ approval: z.ZodDefault<z.ZodObject<{
523
+ default: z.ZodDefault<z.ZodEnum<["guarded", "auto", "deny"]>>;
524
+ overrides: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<["auto", "confirm", "deny"]>>>;
525
+ }, "strip", z.ZodTypeAny, {
526
+ default: "guarded" | "auto" | "deny";
527
+ overrides: Record<string, "auto" | "deny" | "confirm">;
528
+ }, {
529
+ default?: "guarded" | "auto" | "deny" | undefined;
530
+ overrides?: Record<string, "auto" | "deny" | "confirm"> | undefined;
531
+ }>>;
532
+ }, "strip", z.ZodTypeAny, {
533
+ maxSteps: number;
534
+ threadsDir: string;
535
+ approval: {
536
+ default: "guarded" | "auto" | "deny";
537
+ overrides: Record<string, "auto" | "deny" | "confirm">;
538
+ };
539
+ provider?: string | undefined;
540
+ model?: string | undefined;
541
+ }, {
542
+ provider?: string | undefined;
543
+ model?: string | undefined;
544
+ maxSteps?: number | undefined;
545
+ threadsDir?: string | undefined;
546
+ approval?: {
547
+ default?: "guarded" | "auto" | "deny" | undefined;
548
+ overrides?: Record<string, "auto" | "deny" | "confirm"> | undefined;
549
+ } | undefined;
550
+ }>>;
473
551
  agents: z.ZodObject<{
474
552
  dataDir: z.ZodString;
475
553
  /** per-agent 环境变量:键为 agent name,值为 key-value 对 */
@@ -760,6 +838,16 @@ export declare const rollConfigSchema: z.ZodObject<{
760
838
  baseUrl?: string | undefined;
761
839
  }>;
762
840
  };
841
+ runtime: {
842
+ maxSteps: number;
843
+ threadsDir: string;
844
+ approval: {
845
+ default: "guarded" | "auto" | "deny";
846
+ overrides: Record<string, "auto" | "deny" | "confirm">;
847
+ };
848
+ provider?: string | undefined;
849
+ model?: string | undefined;
850
+ };
763
851
  agents: {
764
852
  dataDir: string;
765
853
  env?: Record<string, Record<string, string>> | undefined;
@@ -812,7 +900,19 @@ export declare const rollConfigSchema: z.ZodObject<{
812
900
  trackingAgentId?: string | undefined;
813
901
  }> | undefined;
814
902
  } | undefined;
903
+ runtime?: {
904
+ provider?: string | undefined;
905
+ model?: string | undefined;
906
+ maxSteps?: number | undefined;
907
+ threadsDir?: string | undefined;
908
+ approval?: {
909
+ default?: "guarded" | "auto" | "deny" | undefined;
910
+ overrides?: Record<string, "auto" | "deny" | "confirm"> | undefined;
911
+ } | undefined;
912
+ } | undefined;
815
913
  }>;
816
914
  export type RollConfig = z.infer<typeof rollConfigSchema>;
915
+ export type RuntimeConfig = z.infer<typeof runtimeConfigSchema>;
916
+ export type RuntimeApprovalConfig = z.infer<typeof runtimeApprovalConfigSchema>;
817
917
  export type BrowserConfig = z.infer<typeof browserConfigSchema>;
818
918
  export type InstallConfig = z.infer<typeof installConfigSchema>;
@@ -1 +1 @@
1
- import{z as e}from"zod";const o=["zhipin","yupao"],t=["managed-cdp","remote-cdp","existing-session"],n=["chrome","chromium","msedge"],s=e.string().trim().regex(/^#[\da-fA-F]{6}$/,"profileColor must be a hex RGB color such as #2563EB").transform(e=>e.toUpperCase());export const browserWindowBoundsSchema=e.object({x:e.number().int().optional(),y:e.number().int().optional(),width:e.number().int().positive().optional(),height:e.number().int().positive().optional()});export const providerConfigSchema=e.object({apiKey:e.string(),baseUrl:e.string().optional()});export const llmConfigSchema=e.object({defaultProvider:e.string(),defaultModel:e.string(),providers:e.record(e.string(),providerConfigSchema)});export const askConfigSchema=e.object({llmModel:e.string().optional(),confirmThreshold:e.number().optional()});export const agentsConfigSchema=e.object({dataDir:e.string(),env:e.record(e.string(),e.record(e.string(),e.string())).optional()});export const installConfigSchema=e.object({registry:e.string().trim().url().optional(),fetchRetries:e.number().int().min(0).max(10).default(3),preferOffline:e.boolean().default(!1),networkTimeoutMs:e.number().int().min(1e4).default(12e4)});export const browserInstanceConfigSchema=e.object({platform:e.enum(o).optional(),mode:e.enum(t).default("managed-cdp"),headless:e.boolean().optional(),cdpUrl:e.string().optional(),cdpHost:e.string().default("127.0.0.1"),cdpPort:e.number().int().min(1).max(65535).optional(),channel:e.enum(n).default("chrome"),executablePath:e.string().optional(),userDataDir:e.string().trim().min(1),sessionsDir:e.string().trim().min(1).optional(),args:e.array(e.string()).optional(),profileName:e.string().trim().min(1).optional(),profileColor:s.optional(),windowBounds:browserWindowBoundsSchema.optional(),trackingAgentId:e.string().trim().min(1).optional()}).superRefine((o,t)=>{"managed-cdp"===o.mode&&void 0===o.cdpPort&&t.addIssue({code:e.ZodIssueCode.custom,path:["cdpPort"],message:"managed-cdp browser instance requires cdpPort"}),"remote-cdp"!==o.mode&&"existing-session"!==o.mode||void 0!==o.cdpUrl||t.addIssue({code:e.ZodIssueCode.custom,path:["cdpUrl"],message:`${o.mode} browser instance requires cdpUrl`})});export const browserConfigSchema=e.object({defaultInstance:e.string().trim().min(1).optional(),instances:e.record(e.string(),browserInstanceConfigSchema).default({})}).superRefine((o,t)=>{const n=Object.entries(o.instances);void 0!==o.defaultInstance&&void 0===o.instances[o.defaultInstance]&&t.addIssue({code:e.ZodIssueCode.custom,path:["defaultInstance"],message:`defaultInstance "${o.defaultInstance}" is not declared in browser.instances`});const s=new Map,r=new Map;for(const[o,i]of n){if(void 0!==i.cdpPort){const n=s.get(i.cdpPort);void 0!==n?t.addIssue({code:e.ZodIssueCode.custom,path:["instances",o,"cdpPort"],message:`cdpPort ${String(i.cdpPort)} is already used by browser instance "${n}"`}):s.set(i.cdpPort,o)}const n=r.get(i.userDataDir);void 0!==n?t.addIssue({code:e.ZodIssueCode.custom,path:["instances",o,"userDataDir"],message:`userDataDir is already used by browser instance "${n}"`}):r.set(i.userDataDir,o)}});export const rollConfigSchema=e.object({llm:llmConfigSchema,ask:askConfigSchema,agents:agentsConfigSchema,install:installConfigSchema.default({}),browser:browserConfigSchema.default({})});
1
+ import{z as e}from"zod";const o=["zhipin","yupao"],t=["managed-cdp","remote-cdp","existing-session"],n=["chrome","chromium","msedge"],r=["guarded","auto","deny"],s=["auto","confirm","deny"],i=e.string().trim().regex(/^#[\da-fA-F]{6}$/,"profileColor must be a hex RGB color such as #2563EB").transform(e=>e.toUpperCase());export const browserWindowBoundsSchema=e.object({x:e.number().int().optional(),y:e.number().int().optional(),width:e.number().int().positive().optional(),height:e.number().int().positive().optional()});export const providerConfigSchema=e.object({apiKey:e.string(),baseUrl:e.string().optional()});export const llmConfigSchema=e.object({defaultProvider:e.string(),defaultModel:e.string(),providers:e.record(e.string(),providerConfigSchema)});export const askConfigSchema=e.object({llmModel:e.string().optional(),confirmThreshold:e.number().optional()});export const runtimeApprovalConfigSchema=e.object({default:e.enum(r).default("guarded"),overrides:e.record(e.string(),e.enum(s)).default({})});export const runtimeConfigSchema=e.object({provider:e.string().optional(),model:e.string().optional(),maxSteps:e.number().int().min(1).default(16),threadsDir:e.string().default("~/.roll-agent/threads"),approval:runtimeApprovalConfigSchema.default({})});export const agentsConfigSchema=e.object({dataDir:e.string(),env:e.record(e.string(),e.record(e.string(),e.string())).optional()});export const installConfigSchema=e.object({registry:e.string().trim().url().optional(),fetchRetries:e.number().int().min(0).max(10).default(3),preferOffline:e.boolean().default(!1),networkTimeoutMs:e.number().int().min(1e4).default(12e4)});export const browserInstanceConfigSchema=e.object({platform:e.enum(o).optional(),mode:e.enum(t).default("managed-cdp"),headless:e.boolean().optional(),cdpUrl:e.string().optional(),cdpHost:e.string().default("127.0.0.1"),cdpPort:e.number().int().min(1).max(65535).optional(),channel:e.enum(n).default("chrome"),executablePath:e.string().optional(),userDataDir:e.string().trim().min(1),sessionsDir:e.string().trim().min(1).optional(),args:e.array(e.string()).optional(),profileName:e.string().trim().min(1).optional(),profileColor:i.optional(),windowBounds:browserWindowBoundsSchema.optional(),trackingAgentId:e.string().trim().min(1).optional()}).superRefine((o,t)=>{"managed-cdp"===o.mode&&void 0===o.cdpPort&&t.addIssue({code:e.ZodIssueCode.custom,path:["cdpPort"],message:"managed-cdp browser instance requires cdpPort"}),"remote-cdp"!==o.mode&&"existing-session"!==o.mode||void 0!==o.cdpUrl||t.addIssue({code:e.ZodIssueCode.custom,path:["cdpUrl"],message:`${o.mode} browser instance requires cdpUrl`})});export const browserConfigSchema=e.object({defaultInstance:e.string().trim().min(1).optional(),instances:e.record(e.string(),browserInstanceConfigSchema).default({})}).superRefine((o,t)=>{const n=Object.entries(o.instances);void 0!==o.defaultInstance&&void 0===o.instances[o.defaultInstance]&&t.addIssue({code:e.ZodIssueCode.custom,path:["defaultInstance"],message:`defaultInstance "${o.defaultInstance}" is not declared in browser.instances`});const r=new Map,s=new Map;for(const[o,i]of n){if(void 0!==i.cdpPort){const n=r.get(i.cdpPort);void 0!==n?t.addIssue({code:e.ZodIssueCode.custom,path:["instances",o,"cdpPort"],message:`cdpPort ${String(i.cdpPort)} is already used by browser instance "${n}"`}):r.set(i.cdpPort,o)}const n=s.get(i.userDataDir);void 0!==n?t.addIssue({code:e.ZodIssueCode.custom,path:["instances",o,"userDataDir"],message:`userDataDir is already used by browser instance "${n}"`}):s.set(i.userDataDir,o)}});export const rollConfigSchema=e.object({llm:llmConfigSchema,ask:askConfigSchema,runtime:runtimeConfigSchema.default({}),agents:agentsConfigSchema,install:installConfigSchema.default({}),browser:browserConfigSchema.default({})});
@@ -7,6 +7,15 @@ export interface ChatStepSummary {
7
7
  readonly agentName?: string;
8
8
  readonly toolName?: string;
9
9
  }
10
+ export interface ChatTokenUsage {
11
+ readonly inputTokens?: number;
12
+ readonly outputTokens?: number;
13
+ readonly totalTokens?: number;
14
+ }
15
+ export interface ChatStepUsage {
16
+ readonly finishReason: string;
17
+ readonly usage?: ChatTokenUsage;
18
+ }
10
19
  export interface ChatInputRequirement {
11
20
  readonly name: string;
12
21
  readonly description: string;
@@ -25,6 +34,8 @@ export interface ChatCompletedResult {
25
34
  readonly sessionId: string;
26
35
  readonly output: string;
27
36
  readonly steps: ReadonlyArray<ChatStepSummary>;
37
+ readonly stepUsages?: ReadonlyArray<ChatStepUsage>;
38
+ readonly totalUsage?: ChatTokenUsage;
28
39
  }
29
40
  export interface ChatNeedsInputResult {
30
41
  readonly status: "needs_input";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roll-agent/core",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,6 +14,34 @@
14
14
  ".": {
15
15
  "types": "./dist/cli/index.d.ts",
16
16
  "default": "./dist/cli/index.js"
17
+ },
18
+ "./mcp/*": {
19
+ "types": "./dist/mcp/*.d.ts",
20
+ "default": "./dist/mcp/*.js"
21
+ },
22
+ "./llm/*": {
23
+ "types": "./dist/llm/*.d.ts",
24
+ "default": "./dist/llm/*.js"
25
+ },
26
+ "./registry/*": {
27
+ "types": "./dist/registry/*.d.ts",
28
+ "default": "./dist/registry/*.js"
29
+ },
30
+ "./tool-runtime/*": {
31
+ "types": "./dist/tool-runtime/*.d.ts",
32
+ "default": "./dist/tool-runtime/*.js"
33
+ },
34
+ "./config/*": {
35
+ "types": "./dist/config/*.d.ts",
36
+ "default": "./dist/config/*.js"
37
+ },
38
+ "./types/*": {
39
+ "types": "./dist/types/*.d.ts",
40
+ "default": "./dist/types/*.js"
41
+ },
42
+ "./cli/utils/*": {
43
+ "types": "./dist/cli/utils/*.d.ts",
44
+ "default": "./dist/cli/utils/*.js"
17
45
  }
18
46
  },
19
47
  "bin": {
@@ -44,17 +72,18 @@
44
72
  "gray-matter": "^4.0.3",
45
73
  "ora": "^8.1.0",
46
74
  "yaml": "^2.7.0",
47
- "zod": "^3.25.76"
75
+ "zod": "^3.25.76",
76
+ "@roll-agent/runtime": "0.1.0"
48
77
  },
49
78
  "devDependencies": {
50
79
  "@types/node": "^22.0.0"
51
80
  },
52
81
  "scripts": {
53
- "dev": "node --experimental-strip-types src/cli/index.ts",
82
+ "dev": "node --experimental-strip-types --experimental-sqlite src/cli/index.ts",
54
83
  "build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/obfuscate.mjs",
55
84
  "typecheck": "tsc --noEmit",
56
- "test": "node --experimental-strip-types --test \"src/**/*.test.ts\"",
57
- "test:e2e": "node --experimental-strip-types --test \"src/**/*.e2e.ts\"",
85
+ "test": "node --experimental-strip-types --experimental-sqlite --test \"src/**/*.test.ts\"",
86
+ "test:e2e": "node --experimental-strip-types --experimental-sqlite --test \"src/**/*.e2e.ts\"",
58
87
  "clean": "rm -rf dist"
59
88
  }
60
89
  }