@roll-agent/runtime 0.18.1 → 0.19.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.
@@ -0,0 +1,188 @@
1
+ import type { TriggerSpec } from "./trigger.ts";
2
+ export declare const SCHEDULE_STATUSES: {
3
+ readonly active: "active";
4
+ readonly paused: "paused";
5
+ };
6
+ export type ScheduleStatus = (typeof SCHEDULE_STATUSES)[keyof typeof SCHEDULE_STATUSES];
7
+ export declare const INVOCATION_MODES: {
8
+ readonly scheduled: "scheduled";
9
+ readonly manual: "manual";
10
+ };
11
+ export type InvocationMode = (typeof INVOCATION_MODES)[keyof typeof INVOCATION_MODES];
12
+ export declare const INVOCATION_STATUSES: {
13
+ readonly pending: "pending";
14
+ readonly claimed: "claimed";
15
+ readonly running: "running";
16
+ readonly retry: "retry";
17
+ readonly completed: "completed";
18
+ readonly needsConfirmation: "needs_confirmation";
19
+ readonly failed: "failed";
20
+ };
21
+ export type InvocationStatus = (typeof INVOCATION_STATUSES)[keyof typeof INVOCATION_STATUSES];
22
+ export declare const INVOCATION_LIVE_STATUSES: readonly ["pending", "claimed", "running", "retry"];
23
+ export type CompleteInvocationStatus = typeof INVOCATION_STATUSES.completed | typeof INVOCATION_STATUSES.needsConfirmation;
24
+ export declare const INVOCATION_FAILURE_OUTCOMES: {
25
+ readonly retryScheduled: "retry-scheduled";
26
+ readonly terminal: "terminal";
27
+ readonly terminalPaused: "terminal-paused";
28
+ readonly lostClaim: "lost-claim";
29
+ readonly treeUnsettled: "tree-unsettled";
30
+ };
31
+ export type InvocationFailureOutcome = (typeof INVOCATION_FAILURE_OUTCOMES)[keyof typeof INVOCATION_FAILURE_OUTCOMES];
32
+ export declare const INVOCATION_TERMINAL_STATUSES: readonly ["completed", "needs_confirmation", "failed"];
33
+ export declare const CANCEL_INVOCATION_OUTCOMES: {
34
+ readonly cancelled: "cancelled";
35
+ readonly notFound: "not-found";
36
+ readonly terminal: "terminal";
37
+ readonly ownershipChanged: "ownership-changed";
38
+ readonly executorAlive: "executor-alive";
39
+ readonly executorUnknown: "executor-unknown";
40
+ readonly treeUnsettled: "tree-unsettled";
41
+ };
42
+ export type CancelInvocationOutcome = (typeof CANCEL_INVOCATION_OUTCOMES)[keyof typeof CANCEL_INVOCATION_OUTCOMES];
43
+ export interface CancelInvocationOptions {
44
+ readonly abandon?: boolean;
45
+ readonly expectedOwnershipToken?: string;
46
+ readonly expectedAttempt?: number;
47
+ readonly expectedClaimedBy?: string;
48
+ }
49
+ export declare const TRACKED_LEADER_STATES: {
50
+ readonly alive: "alive";
51
+ readonly exited: "exited";
52
+ readonly unknown: "unknown";
53
+ };
54
+ export type TrackedLeaderState = (typeof TRACKED_LEADER_STATES)[keyof typeof TRACKED_LEADER_STATES];
55
+ export interface PersistedTrackedGroup {
56
+ readonly pgid: number;
57
+ readonly leaderState: TrackedLeaderState;
58
+ readonly startToken?: string;
59
+ }
60
+ export interface FinalizeCancellationTree {
61
+ readonly trackedGroups?: readonly PersistedTrackedGroup[];
62
+ readonly unsettled: boolean;
63
+ readonly survivorPids?: readonly number[];
64
+ }
65
+ export interface FinalizeCancellationInput {
66
+ readonly id: string;
67
+ readonly reason: string;
68
+ readonly nowMs: number;
69
+ readonly expectedAttempt: number;
70
+ readonly expectedClaimedBy?: string;
71
+ readonly expectedOwnershipToken?: string;
72
+ readonly tree?: FinalizeCancellationTree;
73
+ readonly abandon?: boolean;
74
+ }
75
+ export interface RemoveScheduleOptions {
76
+ readonly abandon?: boolean;
77
+ }
78
+ export declare const EXECUTOR_LIVENESS: {
79
+ readonly alive: "alive";
80
+ readonly descendants: "descendants-alive";
81
+ readonly dead: "dead";
82
+ readonly unknown: "unknown";
83
+ };
84
+ export type ExecutorLiveness = (typeof EXECUTOR_LIVENESS)[keyof typeof EXECUTOR_LIVENESS];
85
+ export interface ExecutorIdentity {
86
+ readonly pid: number;
87
+ readonly startToken: string;
88
+ }
89
+ export type ExecutorLivenessProbe = (executor: ExecutorIdentity) => ExecutorLiveness;
90
+ export declare const COMPLETE_INVOCATION_OUTCOMES: {
91
+ readonly written: "written";
92
+ readonly lostClaim: "lost-claim";
93
+ readonly treeUnsettled: "tree-unsettled";
94
+ };
95
+ export type CompleteInvocationOutcome = (typeof COMPLETE_INVOCATION_OUTCOMES)[keyof typeof COMPLETE_INVOCATION_OUTCOMES];
96
+ export declare const INVOCATION_TREE_LIVENESS: {
97
+ readonly settled: "settled";
98
+ readonly unsettled: "unsettled";
99
+ readonly unavailable: "unavailable";
100
+ };
101
+ export type InvocationTreeLiveness = (typeof INVOCATION_TREE_LIVENESS)[keyof typeof INVOCATION_TREE_LIVENESS];
102
+ export interface ScheduleRecord {
103
+ readonly id: string;
104
+ readonly name: string;
105
+ readonly prompt: string;
106
+ readonly cwd: string;
107
+ readonly trigger: TriggerSpec;
108
+ readonly status: ScheduleStatus;
109
+ readonly authorityDigest: string | undefined;
110
+ readonly maxRunMs: number | undefined;
111
+ readonly nextRunAtMs: number | undefined;
112
+ readonly lastRunAtMs: number | undefined;
113
+ readonly lastError: string | undefined;
114
+ readonly createdAtMs: number;
115
+ readonly updatedAtMs: number;
116
+ }
117
+ export interface InvocationRecord {
118
+ readonly id: string;
119
+ readonly scheduleId: string;
120
+ readonly mode: InvocationMode;
121
+ readonly status: InvocationStatus;
122
+ readonly scheduledForMs: number;
123
+ readonly attempt: number;
124
+ readonly maxAttempts: number;
125
+ readonly executor: ExecutorIdentity | undefined;
126
+ readonly claimedBy: string | undefined;
127
+ readonly leaseUntilMs: number | undefined;
128
+ readonly retryAtMs: number | undefined;
129
+ readonly threadId: string | undefined;
130
+ readonly outputExcerpt: string | undefined;
131
+ readonly error: string | undefined;
132
+ readonly pendingActions: readonly string[];
133
+ readonly treeTrackedPgids: readonly number[];
134
+ readonly treeTrackedGroups: readonly PersistedTrackedGroup[];
135
+ readonly treeUnsettled: boolean;
136
+ readonly treeSurvivorPids: readonly number[];
137
+ readonly createdAtMs: number;
138
+ readonly startedAtMs: number | undefined;
139
+ readonly finishedAtMs: number | undefined;
140
+ }
141
+ export type InvocationTreeLivenessProbe = (record: InvocationRecord) => InvocationTreeLiveness;
142
+ export interface ClaimedInvocation {
143
+ readonly invocation: InvocationRecord;
144
+ readonly schedule: ScheduleRecord;
145
+ readonly ownershipToken: string;
146
+ }
147
+ export interface CreateScheduleInput {
148
+ readonly name: string;
149
+ readonly prompt: string;
150
+ readonly cwd: string;
151
+ readonly trigger: TriggerSpec;
152
+ readonly fireImmediately?: boolean;
153
+ readonly authorityDigest?: string;
154
+ readonly maxRunMs?: number;
155
+ }
156
+ export interface EnqueueManualInvocationOptions {
157
+ readonly maxAttempts?: number;
158
+ }
159
+ export interface FailInvocationOptions {
160
+ readonly terminal?: boolean;
161
+ }
162
+ export interface CompleteInvocationInput {
163
+ readonly id: string;
164
+ readonly ownershipToken: string;
165
+ readonly status: CompleteInvocationStatus;
166
+ readonly nowMs: number;
167
+ readonly threadId?: string;
168
+ readonly outputExcerpt?: string;
169
+ readonly pendingActions?: readonly string[];
170
+ }
171
+ export interface RecordInvocationTreeInput {
172
+ readonly id: string;
173
+ readonly ownershipToken: string;
174
+ readonly trackedGroups?: readonly PersistedTrackedGroup[];
175
+ readonly unsettled: boolean;
176
+ readonly survivorPids?: readonly number[];
177
+ readonly error?: string;
178
+ }
179
+ export declare const SCHEDULE_STORE_ERROR_CODES: {
180
+ readonly limitReached: "schedule_limit_reached";
181
+ readonly notFound: "schedule_not_found";
182
+ readonly invalid: "schedule_invalid";
183
+ };
184
+ export type ScheduleStoreErrorCode = (typeof SCHEDULE_STORE_ERROR_CODES)[keyof typeof SCHEDULE_STORE_ERROR_CODES];
185
+ export declare class ScheduleStoreError extends Error {
186
+ readonly code: ScheduleStoreErrorCode;
187
+ constructor(code: ScheduleStoreErrorCode, message: string);
188
+ }
@@ -0,0 +1 @@
1
+ export const SCHEDULE_STATUSES={active:"active",paused:"paused"};export const INVOCATION_MODES={scheduled:"scheduled",manual:"manual"};export const INVOCATION_STATUSES={pending:"pending",claimed:"claimed",running:"running",retry:"retry",completed:"completed",needsConfirmation:"needs_confirmation",failed:"failed"};export const INVOCATION_LIVE_STATUSES=[INVOCATION_STATUSES.pending,INVOCATION_STATUSES.claimed,INVOCATION_STATUSES.running,INVOCATION_STATUSES.retry];export const INVOCATION_FAILURE_OUTCOMES={retryScheduled:"retry-scheduled",terminal:"terminal",terminalPaused:"terminal-paused",lostClaim:"lost-claim",treeUnsettled:"tree-unsettled"};export const INVOCATION_TERMINAL_STATUSES=[INVOCATION_STATUSES.completed,INVOCATION_STATUSES.needsConfirmation,INVOCATION_STATUSES.failed];export const CANCEL_INVOCATION_OUTCOMES={cancelled:"cancelled",notFound:"not-found",terminal:"terminal",ownershipChanged:"ownership-changed",executorAlive:"executor-alive",executorUnknown:"executor-unknown",treeUnsettled:"tree-unsettled"};export const TRACKED_LEADER_STATES={alive:"alive",exited:"exited",unknown:"unknown"};export const EXECUTOR_LIVENESS={alive:"alive",descendants:"descendants-alive",dead:"dead",unknown:"unknown"};export const COMPLETE_INVOCATION_OUTCOMES={written:"written",lostClaim:"lost-claim",treeUnsettled:"tree-unsettled"};export const INVOCATION_TREE_LIVENESS={settled:"settled",unsettled:"unsettled",unavailable:"unavailable"};export const SCHEDULE_STORE_ERROR_CODES={limitReached:"schedule_limit_reached",notFound:"schedule_not_found",invalid:"schedule_invalid"};export class ScheduleStoreError extends Error{code;constructor(e,t){super(t),this.name="ScheduleStoreError",this.code=e}}
@@ -1,3 +1,4 @@
1
+ import type { ChildProcess } from "node:child_process";
1
2
  import { type ToolSet } from "ai";
2
3
  import { z } from "zod";
3
4
  import type { SessionEvent } from "../types/events.ts";
@@ -20,6 +21,7 @@ export interface SessionBashSettings {
20
21
  readonly maxModelOutputChars: number;
21
22
  readonly profile: ShellProfile;
22
23
  readonly env?: NodeJS.ProcessEnv;
24
+ readonly onCommandSpawn?: (child: ChildProcess) => void;
23
25
  }
24
26
  export interface BashToolContext extends ToolBridgeContext {
25
27
  readonly emitEvent?: (event: SessionEvent) => void;
@@ -1 +1 @@
1
- import{resolve as o}from"node:path";import{tool as t}from"ai";import{z as e}from"zod";import{CLASSIFICATION_ANNOTATIONS as r}from"../types/command-classification.js";import{runBashCommand as n}from"../bash/exec.js";import{withAutoApprovedShellEnv as a}from"../bash/clean-env.js";import{formatBashResult as i}from"../bash/format-result.js";import{isWithinWorkdirRoot as s}from"../bash/workdir.js";import{boundedIntParam as l,describeZodIssues as m}from"./bounded-param.js";import{gateToolCall as c}from"./build-tools.js";import{ToolRegistry as u}from"./naming.js";import{TOOL_OUTCOME_KINDS as d,failedToolResult as p,toolResultToModelOutput as f}from"./normalize-result.js";import{OPAQUE_SIDE_EFFECT_RESOURCE as h,TOOL_RESOURCE_ACCESS_MODES as w,executeCoordinatedTool as x}from"./tool-execution-coordinator.js";import{shellCommandExplanationSchema as k}from"./shell-explanation.js";export const BASH_TOOL_AGENT_NAME="roll";export const BASH_TOOL_NAME="bash";export const BASH_TOOL_ID="roll__bash";export const POWERSHELL_TOOL_NAME="powershell";export const POWERSHELL_TOOL_ID="roll__powershell";const _=256,v=4096;function M(o){return"known-safe"===o||"dangerous"===o?o:"unknown"}const b=l({name:"timeout_ms",min:1,max:6e5,description:"超时毫秒数,上限受 maxTimeoutMs 与 turnTimeoutMs 约束;长脚本请显式调大",defaultNote:"默认 10000"}),y=l({name:"max_output_chars",min:1e3,max:2e5,description:"本次返回输出的字符预算;控制输出量请用本参数,不要自接 head/tail 管道",defaultNote:"默认继承配置"}),O=e.object({command:e.string().min(1).describe("要执行的 shell 命令(单字符串,由当前 shell 后端执行)"),explanation:k.optional(),workdir:e.string().min(1).optional().describe("工作目录绝对路径,默认为 roll chat 当前目录。不要在 command 里用 cd,改用本字段"),timeout_ms:b.schema,max_output_chars:y.schema});function g(o){const t=O.safeParse(o);return t.success?t.data:void 0}function j(o,t,e){const r=o.emitEvent;if(!r)return;let n=0;return(o,a)=>{if(n>=_)return;n+=1;const i=a.length>v?a.slice(0,v):a;r({type:"tool-output-delta",toolCallId:t,agentName:"roll",toolName:e,stream:o,delta:i})}}async function T(o,t,e,r,n,a){if(o.policy)return c(o,"roll",t,e,n,{includePolicyReason:"dangerous"===r,...void 0!==a?{explanation:a}:{}});const i=await o.requestApproval({agentName:"roll",toolName:t,input:e,reason:"dangerous"===r?"破坏性操作":void 0,...void 0!==a?{explanation:a}:{}});return i.approved?void 0:p(d.userRejected,"已取消执行"+(i.reason?`: ${i.reason}`:""),i.reason?{reason:i.reason}:{})}export function buildBashToolset(e,l,c,u={}){const k=e.profile.toolName,_=l.register("roll",k),v=u.classifier??e.profile,b=u.exec??n,y=e.env??process.env,E=t=>({workdir:o(e.workdir,t.workdir??"."),timeoutMs:Math.min(t.timeout_ms??e.defaultTimeoutMs,e.maxTimeoutMs,e.turnTimeoutMs)}),N=(o,t)=>{const{workdir:n,timeoutMs:a}=E(o),i=s(e.workdir,n)?t??v.classify(o.command,n):"unknown";return{workdir:n,timeoutMs:a,classification:i,annotations:r[i]}},S={prepare:async(o,t)=>{const e=O.safeParse(o);if(!e.success)return p(d.invalidInput,m(e.error,o)??"参数校验失败: command 必须为非空字符串,explanation 最多 100 字符",{raw:o});const r=e.data,n=N(r,M(t));return T(c,k,{command:r.command,workdir:n.workdir,timeout_ms:n.timeoutMs},n.classification,n.annotations,r.explanation)},resources:(o,t)=>{const r=g(o);if(void 0===r)return[{key:h,mode:w.write},{key:`shell:${e.workdir}`,mode:w.write}];const n=N(r,M(t)),a=!0===n.annotations.readOnlyHint&&!0!==n.annotations.destructiveHint,i={key:`shell:${n.workdir}`,mode:a?w.read:w.write};return a?[i]:[{key:h,mode:w.write},i]},captureExecutionState:o=>{const t=g(o);return void 0===t?"unknown":N(t).classification},revalidateExecution:(o,t)=>{const e=g(o);if("known-safe"===t&&void 0!==e)return"known-safe"===N(e).classification?void 0:p(d.toolFailed,"shell 命令的安全条件在准入后发生变化,已在执行前阻止;请重新提交以重新确认")}};return c.coordinator?.register(_,S),{[_]:t({description:"在当前 shell 后端中执行一条命令并返回输出。需确认的命令继承 roll 进程环境;自动批准的 known-safe 命令使用隔离的系统 PATH 与 shell 环境。总是用 workdir 参数设置工作目录,不要用 cd。输出量用 max_output_chars 控制,不要自接 head/tail 管道(管道退出码反映整条管道)。",inputSchema:O,toModelOutput:({output:o})=>f(o),execute:async(o,t)=>{const{workdir:r,timeoutMs:n}=E(o);return x(c.coordinator,S,_,t.toolCallId,o,t.abortSignal,async s=>{const l=j(c,t.toolCallId,k),m=await b({command:o.command,workdir:r,timeoutMs:n,maxCaptureBytes:e.maxCaptureBytes,profile:e.profile,env:"known-safe"===s?a(y):y,...t.abortSignal?{abortSignal:t.abortSignal}:{},...l?{onDelta:l}:{}});return i({result:m,maxModelOutputChars:o.max_output_chars??e.maxModelOutputChars})})}})}}
1
+ import{resolve as o}from"node:path";import{tool as t}from"ai";import{z as e}from"zod";import{CLASSIFICATION_ANNOTATIONS as r}from"../types/command-classification.js";import{runBashCommand as n}from"../bash/exec.js";import{withAutoApprovedShellEnv as a}from"../bash/clean-env.js";import{formatBashResult as i}from"../bash/format-result.js";import{isWithinWorkdirRoot as s}from"../bash/workdir.js";import{boundedIntParam as l,describeZodIssues as m}from"./bounded-param.js";import{gateToolCall as c}from"./build-tools.js";import{ToolRegistry as u}from"./naming.js";import{TOOL_OUTCOME_KINDS as d,failedToolResult as p,toolResultToModelOutput as f}from"./normalize-result.js";import{OPAQUE_SIDE_EFFECT_RESOURCE as w,TOOL_RESOURCE_ACCESS_MODES as h,executeCoordinatedTool as x}from"./tool-execution-coordinator.js";import{shellCommandExplanationSchema as k}from"./shell-explanation.js";export const BASH_TOOL_AGENT_NAME="roll";export const BASH_TOOL_NAME="bash";export const BASH_TOOL_ID="roll__bash";export const POWERSHELL_TOOL_NAME="powershell";export const POWERSHELL_TOOL_ID="roll__powershell";const _=256,v=4096;function M(o){return"known-safe"===o||"dangerous"===o?o:"unknown"}const b=l({name:"timeout_ms",min:1,max:6e5,description:"超时毫秒数,上限受 maxTimeoutMs 与 turnTimeoutMs 约束;长脚本请显式调大",defaultNote:"默认 10000"}),y=l({name:"max_output_chars",min:1e3,max:2e5,description:"本次返回输出的字符预算;控制输出量请用本参数,不要自接 head/tail 管道",defaultNote:"默认继承配置"}),O=e.object({command:e.string().min(1).describe("要执行的 shell 命令(单字符串,由当前 shell 后端执行)"),explanation:k.optional(),workdir:e.string().min(1).optional().describe("工作目录绝对路径,默认为 roll chat 当前目录。不要在 command 里用 cd,改用本字段"),timeout_ms:b.schema,max_output_chars:y.schema});function g(o){const t=O.safeParse(o);return t.success?t.data:void 0}function S(o,t,e){const r=o.emitEvent;if(!r)return;let n=0;return(o,a)=>{if(n>=_)return;n+=1;const i=a.length>v?a.slice(0,v):a;r({type:"tool-output-delta",toolCallId:t,agentName:"roll",toolName:e,stream:o,delta:i})}}async function j(o,t,e,r,n,a){if(o.policy)return c(o,"roll",t,e,n,{includePolicyReason:"dangerous"===r,...void 0!==a?{explanation:a}:{}});const i=await o.requestApproval({agentName:"roll",toolName:t,input:e,reason:"dangerous"===r?"破坏性操作":void 0,...void 0!==a?{explanation:a}:{}});return i.approved?void 0:p(d.userRejected,"已取消执行"+(i.reason?`: ${i.reason}`:""),i.reason?{reason:i.reason}:{})}export function buildBashToolset(e,l,c,u={}){const k=e.profile.toolName,_=l.register("roll",k),v=u.classifier??e.profile,b=u.exec??n,y=e.env??process.env,T=t=>({workdir:o(e.workdir,t.workdir??"."),timeoutMs:Math.min(t.timeout_ms??e.defaultTimeoutMs,e.maxTimeoutMs,e.turnTimeoutMs)}),E=(o,t)=>{const{workdir:n,timeoutMs:a}=T(o),i=s(e.workdir,n)?t??v.classify(o.command,n):"unknown";return{workdir:n,timeoutMs:a,classification:i,annotations:r[i]}},N={prepare:async(o,t)=>{const e=O.safeParse(o);if(!e.success)return p(d.invalidInput,m(e.error,o)??"参数校验失败: command 必须为非空字符串,explanation 最多 100 字符",{raw:o});const r=e.data,n=E(r,M(t));return j(c,k,{command:r.command,workdir:n.workdir,timeout_ms:n.timeoutMs},n.classification,n.annotations,r.explanation)},resources:(o,t)=>{const r=g(o);if(void 0===r)return[{key:w,mode:h.write},{key:`shell:${e.workdir}`,mode:h.write}];const n=E(r,M(t)),a=!0===n.annotations.readOnlyHint&&!0!==n.annotations.destructiveHint,i={key:`shell:${n.workdir}`,mode:a?h.read:h.write};return a?[i]:[{key:w,mode:h.write},i]},captureExecutionState:o=>{const t=g(o);return void 0===t?"unknown":E(t).classification},revalidateExecution:(o,t)=>{const e=g(o);if("known-safe"===t&&void 0!==e)return"known-safe"===E(e).classification?void 0:p(d.toolFailed,"shell 命令的安全条件在准入后发生变化,已在执行前阻止;请重新提交以重新确认")}};return c.coordinator?.register(_,N),{[_]:t({description:"在当前 shell 后端中执行一条命令并返回输出。需确认的命令继承 roll 进程环境;自动批准的 known-safe 命令使用隔离的系统 PATH 与 shell 环境。总是用 workdir 参数设置工作目录,不要用 cd。输出量用 max_output_chars 控制,不要自接 head/tail 管道(管道退出码反映整条管道)。",inputSchema:O,toModelOutput:({output:o})=>f(o),execute:async(o,t)=>{const{workdir:r,timeoutMs:n}=T(o);return x(c.coordinator,N,_,t.toolCallId,o,t.abortSignal,async s=>{const l=S(c,t.toolCallId,k),m=await b({command:o.command,workdir:r,timeoutMs:n,maxCaptureBytes:e.maxCaptureBytes,profile:e.profile,env:"known-safe"===s?a(y):y,...t.abortSignal?{abortSignal:t.abortSignal}:{},...l?{onDelta:l}:{},...e.onCommandSpawn?{onSpawn:e.onCommandSpawn}:{}});return i({result:m,maxModelOutputChars:o.max_output_chars??e.maxModelOutputChars})})}})}}
@@ -0,0 +1,19 @@
1
+ import { type ToolSet } from "ai";
2
+ import type { ScheduleToolPort } from "@roll-agent/core/scheduler-host/schedule-tool-binding";
3
+ import { type ToolBridgeContext } from "./build-tools.ts";
4
+ import type { ToolRegistry } from "./naming.ts";
5
+ export declare const SCHEDULE_TOOL_AGENT_NAME = "roll";
6
+ export declare const SCHEDULE_CREATE_TOOL_NAME = "schedule_create";
7
+ export declare const SCHEDULE_LIST_TOOL_NAME = "schedule_list";
8
+ export declare const SCHEDULE_CREATE_TOOL_ID = "roll__schedule_create";
9
+ export declare const SCHEDULE_LIST_TOOL_ID = "roll__schedule_list";
10
+ export interface ScheduleToolDeps {
11
+ readonly port: ScheduleToolPort;
12
+ readonly sessionCwd: string;
13
+ readonly includeCreate?: boolean;
14
+ }
15
+ export interface ScheduleToolset {
16
+ readonly createTools: ToolSet;
17
+ readonly listTools: ToolSet;
18
+ }
19
+ export declare function buildScheduleToolset(deps: ScheduleToolDeps, registry: ToolRegistry, ctx: ToolBridgeContext): ScheduleToolset;
@@ -0,0 +1 @@
1
+ import{tool as e}from"ai";import{z as t}from"zod";import{gateToolCall as r}from"./build-tools.js";import{TOOL_OUTCOME_KINDS as n,failedToolResult as i,successfulToolResult as s,toolResultToModelOutput as o}from"./normalize-result.js";import{TOOL_RESOURCE_ACCESS_MODES as a,executeCoordinatedTool as c}from"./tool-execution-coordinator.js";export const SCHEDULE_TOOL_AGENT_NAME="roll";export const SCHEDULE_CREATE_TOOL_NAME="schedule_create";export const SCHEDULE_LIST_TOOL_NAME="schedule_list";export const SCHEDULE_CREATE_TOOL_ID="roll__schedule_create";export const SCHEDULE_LIST_TOOL_ID="roll__schedule_list";const l=t.object({name:t.string().trim().min(1).max(120).describe("任务名称(确认界面与任务列表中展示)"),prompt:t.string().trim().min(1).max(4e3).describe("每次触发时交给新一轮 chat 的任务描述,写清要做什么"),every:t.string().trim().min(1).describe("触发间隔,格式 <数字><s|m|h|d>,如 30m、2h、1d(最短 60s,最长 365d)"),cwd:t.string().optional().describe("任务运行的工作目录;省略时使用当前会话工作目录,相对路径相对会话目录解析"),maxRun:t.string().optional().describe("单次运行时长上限,格式同 every(60s..24h;省略时默认 1 小时)")}),u=t.object({status:t.enum(["all","active","paused"]).optional().describe("按状态过滤,省略时返回全部"),offset:t.number().int().min(0).optional().describe("分页偏移,默认 0"),limit:t.number().int().min(1).max(100).optional().describe("最多返回条数,默认 50")}),d=Symbol("schedule-invalid-input");function m(e){return"object"==typeof e&&null!==e&&e.kind===d}function p(e){return"object"==typeof e&&null!==e&&!1===e.ok}function g(e){const t="invalid_input"===e.code||"schedule_invalid"===e.code||"schedule_trigger_invalid"===e.code?n.invalidInput:n.toolFailed;return i(t,e.message,{raw:e})}function $(e){return 0===e.warnings.length?"":`\n${e.warnings.map(e=>`注意:${e.message}。`).join("\n")}`}function f(e){return{name:e.name,prompt:e.prompt,every:e.everyDisplay,cwd:e.cwd,maxRun:e.maxRunDisplay,firstRunAt:_(e.firstRunAt),lifecycle:"会持续运行,直到暂停或删除;创建时记录当前权限边界",...e.readiness.warnings.length>0?{serviceStatus:e.readiness.warnings.map(e=>e.message).join(";")}:{}}}function _(e){const t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString()}export function buildScheduleToolset(t,h,y){const v=t.includeCreate??!0?h.register("roll","schedule_create"):void 0,x=h.register("roll","schedule_list"),E={captureExecutionState:e=>{const r=l.safeParse(e);return r.success?t.port.captureCreate(r.data,t.sessionCwd):{kind:d,message:`参数校验失败: ${r.error.issues.map(e=>e.message).join("; ")}`}},prepare:async(e,t)=>{if(m(t))return i(n.invalidInput,t.message);if(p(t))return g(t);const s=t;return r(y,"roll","schedule_create",f(s),void 0,{explanation:`将登记定时任务「${s.name}」(${s.everyDisplay})`})},resources:()=>[{key:"schedule-ledger",mode:a.write}]};void 0!==v&&y.coordinator?.register(v,E);const b={prepare:async e=>r(y,"roll","schedule_list",e??{},{readOnlyHint:!0}),resources:()=>[{key:"schedule-ledger",mode:a.read}]};y.coordinator?.register(x,b);return{createTools:void 0===v?{}:{[v]:e({description:"登记一个按固定间隔重复运行的定时任务:到点后由 roll 调度器发起新一轮无人值守 chat 执行 prompt。创建前会向用户展示完整参数并请求确认,不要在调用前重复询问。仅支持固定间隔(every),不支持一次性时间点、cron 表达式或时区。",inputSchema:l,toModelOutput:({output:e})=>o(e),execute:async(e,r)=>c(y.coordinator,E,v,r.toolCallId,e,r.abortSignal,async e=>{if(m(e))return i(n.invalidInput,e.message);if(p(e))return g(e);const r=e,o=await t.port.create(r);if(p(o))return g(o);const a=o.schedule,c=o.created?`已登记定时任务 "${a.name}"(${a.trigger.display},单次上限 ${a.maxRun.display})。`:o.reauthorized?`已存在相同定义的任务 "${a.name}"(id ${a.id}),未重复创建;已按当前权限边界重新授权。`:`已存在相同定义的任务 "${a.name}"(id ${a.id}),未重复创建。`,l=void 0===a.nextRunAt?"":`下次运行约 ${_(a.nextRunAt)}。`,u=o.readiness.automaticRunsReady?"":$(o.readiness)||"\n注意:调度服务未就绪,任务不会自动执行。";return s(`${c}${l}${u}`,{raw:o})})})},listTools:{[x]:e({description:"列出当前配置下已登记的定时任务(含状态、频率、下次运行时间与最近错误摘要)。创建前可用它确认是否已有同类任务。",inputSchema:u,toModelOutput:({output:e})=>o(e),execute:async(e,r)=>c(y.coordinator,b,x,r.toolCallId,e,r.abortSignal,async()=>{const r=u.safeParse(e??{});if(!r.success)return i(n.invalidInput,`参数校验失败: ${r.error.issues.map(e=>e.message).join("; ")}`);const o=await t.port.list(r.data,t.sessionCwd);if(p(o))return g(o);if(0===o.total)return s(`暂无定时任务。${$(o.readiness)}`.trim(),{raw:o});const a=o.schedules.map(e=>{const t="active"===e.status&&void 0!==e.nextRunAt?`,下次 ${_(e.nextRunAt)}`:"",r=void 0===e.lastError?"":`,最近错误:${e.lastError}`;return`- ${e.name}(${e.status},${e.trigger}${t})id=${e.id}\n 内容:${e.promptExcerpt}${r}`}),c=o.hasMore?`\n共 ${String(o.total)} 个,仅显示 ${String(o.schedules.length)} 个;用 offset/limit 翻页。`:"";return s(`${a.join("\n")}${c}${$(o.readiness)}`,{raw:o})})})}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roll-agent/runtime",
3
- "version": "0.18.1",
3
+ "version": "0.19.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,17 +26,17 @@
26
26
  "node": ">=22.6.0"
27
27
  },
28
28
  "dependencies": {
29
- "@ai-sdk/provider": "^4.0.1",
30
- "@modelcontextprotocol/sdk": "^1.12.0",
29
+ "@ai-sdk/provider": "^4.0.7",
30
+ "@modelcontextprotocol/sdk": "^1.30.0",
31
31
  "@vscode/ripgrep": "1.18.0",
32
- "ai": "^7.0.9",
32
+ "ai": "^7.0.65",
33
33
  "yaml": "2.8.3",
34
34
  "zod": "^3.25.76",
35
35
  "@roll-agent/protocol": "0.6.1"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^22.0.0",
39
- "@roll-agent/core": "0.34.1"
39
+ "@roll-agent/core": "0.35.0"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/obfuscate.mjs",