@ct-agents/protocol 0.0.1
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/package.json +14 -0
- package/src/context-resolve.ts +109 -0
- package/src/environment.ts +406 -0
- package/src/harness.ts +134 -0
- package/src/index.ts +19 -0
- package/src/memory.ts +57 -0
- package/src/orchestrator.ts +128 -0
- package/src/prompt.ts +122 -0
- package/src/session/derived-state.ts +20 -0
- package/src/session/events.ts +298 -0
- package/src/session/ids.ts +12 -0
- package/src/session/index.ts +6 -0
- package/src/session/record.ts +32 -0
- package/src/session/scope.ts +6 -0
- package/src/session/store.ts +127 -0
- package/src/session-api.ts +121 -0
- package/src/skill.ts +42 -0
- package/src/store.ts +11 -0
- package/src/tool-exec.ts +69 -0
- package/src/tool-registry.ts +49 -0
- package/src/tool.ts +275 -0
- package/src/work-queue.ts +113 -0
package/package.json
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { EnvironmentExecutionMode } from './environment.js';
|
|
2
|
+
import type { ExecutionScope } from './session/scope.js';
|
|
3
|
+
import type { SessionEvent } from './session/events.js';
|
|
4
|
+
|
|
5
|
+
export type ContextResolveRequest = {
|
|
6
|
+
kind: 'prompt_context';
|
|
7
|
+
keys: string[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type ContextResolveWorkItem = {
|
|
11
|
+
workId: string;
|
|
12
|
+
leaseId?: string;
|
|
13
|
+
appId: string;
|
|
14
|
+
environmentId: string;
|
|
15
|
+
executionMode?: EnvironmentExecutionMode;
|
|
16
|
+
sessionId: string;
|
|
17
|
+
rootSessionId: string;
|
|
18
|
+
scope: ExecutionScope;
|
|
19
|
+
request: ContextResolveRequest;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type ContextResolveResult =
|
|
23
|
+
| {
|
|
24
|
+
workId: string;
|
|
25
|
+
leaseId?: string;
|
|
26
|
+
status: 'completed';
|
|
27
|
+
snapshot: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
| {
|
|
30
|
+
workId: string;
|
|
31
|
+
leaseId?: string;
|
|
32
|
+
status: 'failed';
|
|
33
|
+
error: {
|
|
34
|
+
message: string;
|
|
35
|
+
code?: string;
|
|
36
|
+
retryable?: boolean;
|
|
37
|
+
details?: unknown;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type ContextResolveQueueClaimInput = {
|
|
42
|
+
appId: string;
|
|
43
|
+
environmentId: string;
|
|
44
|
+
executionMode?: EnvironmentExecutionMode;
|
|
45
|
+
max?: number;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type ContextResolveQueueReclaimInput = {
|
|
49
|
+
appId: string;
|
|
50
|
+
environmentId: string;
|
|
51
|
+
executionMode?: EnvironmentExecutionMode;
|
|
52
|
+
olderThanMs: number;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type ContextResolveQueueStatsInput = {
|
|
56
|
+
appId: string;
|
|
57
|
+
environmentId: string;
|
|
58
|
+
executionMode?: EnvironmentExecutionMode;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type CompleteContextResolveScope = {
|
|
62
|
+
appId: string;
|
|
63
|
+
environmentId: string;
|
|
64
|
+
executionMode?: EnvironmentExecutionMode;
|
|
65
|
+
leaseId?: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type ContextResolveStats = {
|
|
69
|
+
depth: number;
|
|
70
|
+
pending: number;
|
|
71
|
+
oldestQueuedAt: string | null;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export interface ContextResolveQueue {
|
|
75
|
+
enqueue(item: ContextResolveWorkItem): Promise<void>;
|
|
76
|
+
claim(input: ContextResolveQueueClaimInput): Promise<ContextResolveWorkItem[]>;
|
|
77
|
+
complete(result: ContextResolveResult, scope?: CompleteContextResolveScope): Promise<boolean>;
|
|
78
|
+
reclaim(input: ContextResolveQueueReclaimInput): Promise<number>;
|
|
79
|
+
stats(input: ContextResolveQueueStatsInput): Promise<ContextResolveStats>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type ClaimedContextResolveWorkItem = {
|
|
83
|
+
item: ContextResolveWorkItem;
|
|
84
|
+
result: ContextResolveResult;
|
|
85
|
+
settlementAttempts?: number;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export type ContextResolveSettlementFailureInput = {
|
|
89
|
+
workId: string;
|
|
90
|
+
error: {
|
|
91
|
+
message: string;
|
|
92
|
+
code?: string;
|
|
93
|
+
details?: unknown;
|
|
94
|
+
};
|
|
95
|
+
deadLetter?: boolean;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export interface ContextResolveSettlementStore {
|
|
99
|
+
claimSettlable(input?: { max?: number }): Promise<ClaimedContextResolveWorkItem[]>;
|
|
100
|
+
markSettled(workId: string): Promise<boolean>;
|
|
101
|
+
markSettlementFailed(input: ContextResolveSettlementFailureInput): Promise<boolean>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type ContextSnapshotLookup = {
|
|
105
|
+
findResolvedContext(input: {
|
|
106
|
+
events: SessionEvent[];
|
|
107
|
+
request: ContextResolveRequest;
|
|
108
|
+
}): Record<string, unknown> | null;
|
|
109
|
+
};
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SessionEvent,
|
|
3
|
+
SessionRecord,
|
|
4
|
+
ToolErrorPayload,
|
|
5
|
+
ToolOrphanedReason,
|
|
6
|
+
} from './session/index.js';
|
|
7
|
+
import type { SkillView } from './skill.js';
|
|
8
|
+
import type {
|
|
9
|
+
AddMemoryInput,
|
|
10
|
+
DeleteMemoryInput,
|
|
11
|
+
MemoryListItem,
|
|
12
|
+
MemoryRecord,
|
|
13
|
+
UpdateMemoryInput,
|
|
14
|
+
} from './memory.js';
|
|
15
|
+
import type { ToolExecutionResult } from './tool-exec.js';
|
|
16
|
+
import type { SessionStore } from './session/store.js';
|
|
17
|
+
import type { Store } from './store.js';
|
|
18
|
+
import type { HarnessInfo } from './harness.js';
|
|
19
|
+
import type { ToolHandler } from './tool.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 运行时 schema 校验契约。
|
|
23
|
+
*
|
|
24
|
+
* protocol 只要求实现能完成严格解析与宽松解析,不绑定 zod 等具体校验库;
|
|
25
|
+
* 工具入参、工具结果与资源 options 都通过这个结构化契约保持可替换。
|
|
26
|
+
*/
|
|
27
|
+
export interface SchemaContract<T> {
|
|
28
|
+
/**
|
|
29
|
+
* 严格解析:输入不合法时抛错,合法时返回解析后的强类型值。
|
|
30
|
+
*
|
|
31
|
+
* 用于「数据已通过其它约束、只需窄化为强类型」的 fail-fast 边界,
|
|
32
|
+
* 例如工具入参在执行前的校验。
|
|
33
|
+
*/
|
|
34
|
+
parse(value: unknown): T;
|
|
35
|
+
/**
|
|
36
|
+
* 宽松解析:永不抛错,返回带 `success` 标记的结果对象。
|
|
37
|
+
*
|
|
38
|
+
* 用于「需收集全部校验问题而非首个即中断」的边界,例如环境资源配置表单的
|
|
39
|
+
* 字段级校验明细。`error` 为 `unknown`:协议不绑定具体校验库的错误结构,
|
|
40
|
+
* 由实现侧(当前统一为 zod)自行解释其 issues。
|
|
41
|
+
*/
|
|
42
|
+
safeParse(value: unknown): { success: true; data: T } | { success: false; error: unknown };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 数据库资源能力 SPI。工具通过 {@link ResourceSlots.database} 访问,
|
|
47
|
+
* 具体实现由宿主应用注入。`options.readOnly === true` 是强契约:
|
|
48
|
+
* 实现必须使用只读账号、只读事务或等价机制强制只读,不能只依赖工具层 SQL 判定。
|
|
49
|
+
*/
|
|
50
|
+
export interface Database {
|
|
51
|
+
query(sql: string, params?: readonly unknown[], options?: { readOnly?: boolean; maxRows?: number; timeoutMs?: number }): Promise<unknown>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Skill 运行时资源能力。
|
|
56
|
+
*
|
|
57
|
+
* 只暴露当前 session/harness 可见的 Skill 视图;app/global 过滤、harness 关联过滤等控制面规则
|
|
58
|
+
* 由装配层在构造 resource 时绑定,工具层不读取 appId/includeGlobal 等具名 scope 参数。
|
|
59
|
+
*/
|
|
60
|
+
export type RuntimeSkillView = Pick<SkillView, 'name' | 'description' | 'instructions' | 'version' | 'enabled'>;
|
|
61
|
+
|
|
62
|
+
export interface SkillResource {
|
|
63
|
+
list(): Promise<Array<Pick<RuntimeSkillView, 'name' | 'description' | 'version'>>>;
|
|
64
|
+
get(name: string): Promise<RuntimeSkillView | null>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Memory 运行时资源能力。
|
|
69
|
+
*
|
|
70
|
+
* 访问范围由实现层构造期绑定;工具只按 name 读写当前可见 memory,不感知 partition/owner/appId。
|
|
71
|
+
*/
|
|
72
|
+
export interface MemoryResource {
|
|
73
|
+
get(name: string): Promise<MemoryRecord | null>;
|
|
74
|
+
add(input: AddMemoryInput): Promise<MemoryRecord>;
|
|
75
|
+
update(input: UpdateMemoryInput): Promise<MemoryRecord>;
|
|
76
|
+
delete(input: DeleteMemoryInput): Promise<MemoryRecord>;
|
|
77
|
+
list(): Promise<MemoryListItem[]>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** 沙箱命令执行结果。 */
|
|
81
|
+
export interface ExecResult {
|
|
82
|
+
stdout: string;
|
|
83
|
+
stderr: string;
|
|
84
|
+
exitCode: number;
|
|
85
|
+
timedOut: boolean;
|
|
86
|
+
stdoutTruncated?: boolean;
|
|
87
|
+
stderrTruncated?: boolean;
|
|
88
|
+
totalStdoutBytes?: number;
|
|
89
|
+
totalStderrBytes?: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 已就绪的会话沙箱句柄。
|
|
94
|
+
*
|
|
95
|
+
* 所有 fs/bash 类工具都经同一个会话级 sandbox 进入隔离环境;权限、路径 jail 与审批仍由工具层处理。
|
|
96
|
+
*/
|
|
97
|
+
export interface SandboxHandle {
|
|
98
|
+
readonly sessionId: string;
|
|
99
|
+
readonly workspaceRoot: string;
|
|
100
|
+
readonly isolated: boolean;
|
|
101
|
+
exec(command: string, opts?: { timeoutMs?: number; maxOutputBytes?: number }): Promise<ExecResult>;
|
|
102
|
+
readFile(path: string, opts?: { offset?: number; limit?: number; maxBytes?: number }): Promise<string>;
|
|
103
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
104
|
+
dispose(): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 沙箱驱动 SPI。
|
|
109
|
+
*
|
|
110
|
+
* 协议只描述按 session 启动沙箱,不携带 partition/owner 等隔离维度;具体实现若需要更多隔离信息,
|
|
111
|
+
* 应在 worker 实现层从不透明 scope 或本地配置中获取。
|
|
112
|
+
*/
|
|
113
|
+
export interface Sandbox {
|
|
114
|
+
readonly kind: 'local' | 'docker' | 'fargate';
|
|
115
|
+
readonly isolated: boolean;
|
|
116
|
+
launch(input: { sessionId: string }): Promise<SandboxHandle>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** 平台内置 slot 名保留字:业务 catalog 不得声明同名 slot。 */
|
|
120
|
+
export const BUILTIN_SLOT_NAMES = ['database', 'sandbox', 'skills', 'memory'] as const;
|
|
121
|
+
export type BuiltinSlotName = (typeof BUILTIN_SLOT_NAMES)[number];
|
|
122
|
+
|
|
123
|
+
/** 内置 slot 的能力类型映射,供 SDK builder 做强类型推导。 */
|
|
124
|
+
export type BuiltinSlots = {
|
|
125
|
+
database: Database;
|
|
126
|
+
sandbox: SandboxHandle;
|
|
127
|
+
skills: SkillResource;
|
|
128
|
+
memory: MemoryResource;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 已实例化的资源集合:按环境配置注入工具执行上下文。
|
|
133
|
+
* 协议只固定少数通用槽位,自定义 slot 以不透明实例透传,运行时不感知业务类型。
|
|
134
|
+
*/
|
|
135
|
+
export interface ResourceSlots {
|
|
136
|
+
database?: Database;
|
|
137
|
+
sandbox?: SandboxHandle;
|
|
138
|
+
skills?: SkillResource;
|
|
139
|
+
memory?: MemoryResource;
|
|
140
|
+
[slot: string]: unknown;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** 资源工厂:按 options 实例化一个资源(同步或异步)。 */
|
|
144
|
+
export type ResourceFactory<TResource, TOptions extends Record<string, unknown>> = (
|
|
145
|
+
options: TOptions,
|
|
146
|
+
) => Promise<TResource> | TResource;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 资源的单个实现定义。
|
|
150
|
+
*
|
|
151
|
+
* 一个 resource slot 可有多个 implementation(不同后端)。
|
|
152
|
+
* `optionsSchema` 是运行时 parse 契约,`optionsJsonSchema` 是 catalog/Console 展示契约。
|
|
153
|
+
* 二者位于不同边界:运行时只读取 `optionsSchema`,控制面只投影 `optionsJsonSchema`。
|
|
154
|
+
*/
|
|
155
|
+
export type ResourceImplementationDefinition<TResource, TOptions extends Record<string, unknown>> = {
|
|
156
|
+
id: string;
|
|
157
|
+
title?: string;
|
|
158
|
+
description?: string;
|
|
159
|
+
/**
|
|
160
|
+
* 平台内置实现可声明允许绑定的环境执行面。缺省表示不限制 hosted/self_hosted;
|
|
161
|
+
* 只有显式声明数组时才按当前 Environment executionMode 做准入校验。
|
|
162
|
+
*/
|
|
163
|
+
supportedExecutionModes?: EnvironmentExecutionMode[];
|
|
164
|
+
optionsSchema: SchemaContract<TOptions>;
|
|
165
|
+
optionsJsonSchema?: Record<string, unknown>;
|
|
166
|
+
factory: ResourceFactory<TResource, TOptions>;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 资源定义:一个 resource id + 其可选的多个实现。
|
|
171
|
+
*
|
|
172
|
+
* 由 apps/api 在装配时注册到 {@link ResourceRegistry};运行时按 EnvironmentConfig
|
|
173
|
+
* 选择的 implementationId 实例化对应实现。
|
|
174
|
+
*/
|
|
175
|
+
export type ResourceDefinition<TResource, TOptions extends Record<string, unknown>> = {
|
|
176
|
+
id: string;
|
|
177
|
+
title?: string;
|
|
178
|
+
description?: string;
|
|
179
|
+
implementations?: Array<ResourceImplementationDefinition<TResource, Record<string, unknown>>>;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* 资源注册中心:登记所有可用 resource 定义,供环境实例化与配置 UI 查询。
|
|
184
|
+
*
|
|
185
|
+
* 由 apps/api 装配时填充具体实现;运行时面向本接口编程。
|
|
186
|
+
*/
|
|
187
|
+
export interface ResourceRegistry {
|
|
188
|
+
/** 注册一个 resource 定义(含其实现列表)。 */
|
|
189
|
+
register<TResource, TOptions extends Record<string, unknown>>(
|
|
190
|
+
definition: ResourceDefinition<TResource, TOptions>,
|
|
191
|
+
): void;
|
|
192
|
+
/** 按 resource id 查找定义;不存在返回 undefined。 */
|
|
193
|
+
get(id: string): ResourceDefinition<unknown, Record<string, unknown>> | undefined;
|
|
194
|
+
/** 枚举全部 resource 定义,供配置后台 catalog 展示。 */
|
|
195
|
+
list(): Array<ResourceDefinition<unknown, Record<string, unknown>>>;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/** 环境配置中单个资源的绑定:slot 名由外层 key 提供,值只保存选定 implementation 与 options。 */
|
|
199
|
+
export type EnvironmentResourceConfig = {
|
|
200
|
+
implementationId?: string;
|
|
201
|
+
options: Record<string, unknown>;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export type EnvironmentExecutionMode = 'hosted' | 'self_hosted';
|
|
205
|
+
|
|
206
|
+
export type EnvironmentHostedPolicy = {
|
|
207
|
+
actionTimeoutMs?: number;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/** 资源槽位 DTO:把 slot 及 implementation options JSON Schema 组织成配置 UI 可消费的结构。 */
|
|
211
|
+
export type EnvironmentResourceSlotDto = {
|
|
212
|
+
name: string;
|
|
213
|
+
origin?: 'platform_builtin' | 'app_catalog';
|
|
214
|
+
title: string;
|
|
215
|
+
description?: string;
|
|
216
|
+
implementations?: Array<{
|
|
217
|
+
id: string;
|
|
218
|
+
origin?: 'platform_builtin' | 'app_catalog';
|
|
219
|
+
title: string;
|
|
220
|
+
description?: string;
|
|
221
|
+
supportedExecutionModes?: EnvironmentExecutionMode[];
|
|
222
|
+
optionsSchema?: Record<string, unknown>;
|
|
223
|
+
}>;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 环境配置:一组 resource 绑定 + 启用状态,带乐观锁版本号。
|
|
228
|
+
* 多 harness 可共享同一 environment;配置变更通过版本号做乐观并发控制。
|
|
229
|
+
*/
|
|
230
|
+
export type EnvironmentConfig = {
|
|
231
|
+
appId?: string;
|
|
232
|
+
id: string;
|
|
233
|
+
version: number;
|
|
234
|
+
name: string;
|
|
235
|
+
executionMode: EnvironmentExecutionMode;
|
|
236
|
+
hostedPolicy?: EnvironmentHostedPolicy;
|
|
237
|
+
resources: Partial<Record<string, EnvironmentResourceConfig>>;
|
|
238
|
+
enabled: boolean;
|
|
239
|
+
createdAt?: string;
|
|
240
|
+
updatedAt?: string;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export type EnvironmentConfigKey = string | { appId?: string; id: string };
|
|
244
|
+
export type EnvironmentConfigListQuery = { appId?: string };
|
|
245
|
+
|
|
246
|
+
/** 保存环境配置输入;`baseVersion` 用于乐观锁冲突检测。 */
|
|
247
|
+
export type SaveEnvironmentConfigInput = {
|
|
248
|
+
appId?: string;
|
|
249
|
+
id: string;
|
|
250
|
+
name: string;
|
|
251
|
+
executionMode: EnvironmentExecutionMode;
|
|
252
|
+
hostedPolicy?: EnvironmentHostedPolicy;
|
|
253
|
+
resources: Partial<Record<string, EnvironmentResourceConfig>>;
|
|
254
|
+
baseVersion?: number;
|
|
255
|
+
enabled?: boolean;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 环境配置存储 SPI:扩展基础 {@link Store} 增加 save / delete。
|
|
260
|
+
* 具体实现(PostgresEnvironmentConfigStore 等)由 apps/api 装配注入。
|
|
261
|
+
*/
|
|
262
|
+
export interface EnvironmentConfigStore extends Store<EnvironmentConfig, EnvironmentConfigKey> {
|
|
263
|
+
list(query?: EnvironmentConfigListQuery): Promise<EnvironmentConfig[]>;
|
|
264
|
+
save(input: SaveEnvironmentConfigInput): Promise<EnvironmentConfig>;
|
|
265
|
+
delete(key: EnvironmentConfigKey): Promise<void>;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* 已实例化的环境容器:环境 id + 实例化后的资源集合。
|
|
270
|
+
* orchestrator 在 wake 时据此把 resources 注入 harness / 工具上下文。
|
|
271
|
+
*/
|
|
272
|
+
export type EnvironmentContainer<TResources extends object = object> = {
|
|
273
|
+
environmentId: string;
|
|
274
|
+
environmentUpdatedAt?: string;
|
|
275
|
+
resources: TResources;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/** 执行工具的输入:目标工具、入参、归属会话与资源、以及 provider 原生 toolCallId。 */
|
|
279
|
+
export type ExecuteToolInput = {
|
|
280
|
+
sessionStore: SessionStore;
|
|
281
|
+
session: SessionRecord;
|
|
282
|
+
harness?: HarnessInfo;
|
|
283
|
+
executionMode: EnvironmentExecutionMode;
|
|
284
|
+
hostedPolicy?: EnvironmentHostedPolicy;
|
|
285
|
+
localToolNames?: readonly string[];
|
|
286
|
+
resources?: ResourceSlots;
|
|
287
|
+
resourceBindings?: Record<string, unknown>;
|
|
288
|
+
toolName: string;
|
|
289
|
+
input: unknown;
|
|
290
|
+
providerToolCallId?: string;
|
|
291
|
+
actor: SessionEvent<'tool.called'>['actor'];
|
|
292
|
+
turnId?: string;
|
|
293
|
+
abortSignal?: AbortSignal;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/** 结清已解决 action 的输入:对一组已 resolved 的 action 事件恢复执行对应工具。 */
|
|
297
|
+
export type SettleResolvedActionsInput = {
|
|
298
|
+
sessionStore: SessionStore;
|
|
299
|
+
session: SessionRecord;
|
|
300
|
+
harness?: HarnessInfo;
|
|
301
|
+
executionMode: EnvironmentExecutionMode;
|
|
302
|
+
hostedPolicy?: EnvironmentHostedPolicy;
|
|
303
|
+
localToolNames?: readonly string[];
|
|
304
|
+
resources?: ResourceSlots;
|
|
305
|
+
resourceBindings?: Record<string, unknown>;
|
|
306
|
+
actionEventIds: string[];
|
|
307
|
+
abortSignal?: AbortSignal;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/** 收口悬空 tool 调用的输入:把无终态的 tool.called 标记为 orphaned 失败。 */
|
|
311
|
+
export type FailOpenToolCallsInput = {
|
|
312
|
+
sessionStore: SessionStore;
|
|
313
|
+
session: SessionRecord;
|
|
314
|
+
events: SessionEvent[];
|
|
315
|
+
toolCallEventIds: string[];
|
|
316
|
+
reason: ToolOrphanedReason;
|
|
317
|
+
observedAt: string;
|
|
318
|
+
state?: unknown;
|
|
319
|
+
abortSignal?: AbortSignal;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
/** 应用业务 worker 已完成的 tool work item 结果。 */
|
|
323
|
+
export type ApplyCompletedWorkInput = {
|
|
324
|
+
sessionStore: SessionStore;
|
|
325
|
+
session: SessionRecord;
|
|
326
|
+
handler: ToolHandler<unknown, unknown>;
|
|
327
|
+
toolCallEvent: SessionEvent<'tool.called'>;
|
|
328
|
+
result: ToolExecutionResult;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
/** 工具执行完成结果:携带落库后的 modelResult。 */
|
|
332
|
+
export type EnvironmentToolCompletedResult = {
|
|
333
|
+
status: 'completed';
|
|
334
|
+
toolCallEventId: string;
|
|
335
|
+
modelResult: unknown;
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
/** 工具执行暂停结果:列出本次产生的 action 事件 id,等待用户解决。 */
|
|
339
|
+
export type EnvironmentToolPausedResult = {
|
|
340
|
+
status: 'paused';
|
|
341
|
+
toolCallEventId: string;
|
|
342
|
+
actionEventIds: string[];
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
/** 工具已进入业务 worker 队列,当前模型轮需要等待回传后再恢复。 */
|
|
346
|
+
export type EnvironmentToolQueuedResult = {
|
|
347
|
+
status: 'queued';
|
|
348
|
+
toolCallEventId: string;
|
|
349
|
+
workId: string;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/** 工具执行失败结果:携带结构化错误。 */
|
|
353
|
+
export type EnvironmentToolFailedResult = {
|
|
354
|
+
status: 'failed';
|
|
355
|
+
/** 调用前校验失败时尚未写 `tool.called`,因此可能不存在。 */
|
|
356
|
+
toolCallEventId?: string;
|
|
357
|
+
error: ToolErrorPayload;
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
/** 工具执行结果:完成 / 暂停 / 入队等待 / 失败。 */
|
|
361
|
+
export type EnvironmentToolResult =
|
|
362
|
+
| EnvironmentToolCompletedResult
|
|
363
|
+
| EnvironmentToolPausedResult
|
|
364
|
+
| EnvironmentToolQueuedResult
|
|
365
|
+
| EnvironmentToolFailedResult;
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* action 结清结果:本地工具会完成或失败;业务 worker 工具会重新入队 resume,
|
|
369
|
+
* 待 worker 在真实资源上下文中执行后再由持久队列结算终态。
|
|
370
|
+
*/
|
|
371
|
+
export type SettledActionResult =
|
|
372
|
+
| EnvironmentToolCompletedResult
|
|
373
|
+
| EnvironmentToolFailedResult
|
|
374
|
+
| EnvironmentToolQueuedResult;
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* 工具执行边界(核心 SPI)。
|
|
378
|
+
*
|
|
379
|
+
* Environment 是「工具生命周期事件的唯一写入者」:它调度工具 handler、把 handler 返回的
|
|
380
|
+
* effects 与结果按序落库为 session 事件(tool.called → effects → tool.completed/failed/interrupted)。
|
|
381
|
+
* 工具 handler 本身不直接写事件,全部经 Environment 中转,保证事件顺序与一致性。
|
|
382
|
+
*
|
|
383
|
+
* 由 apps/api 装配具体实现(DefaultEnvironment)注入 orchestrator;运行时面向本接口编程。
|
|
384
|
+
*/
|
|
385
|
+
export interface Environment {
|
|
386
|
+
/**
|
|
387
|
+
* 执行一次工具调用:校验入参 → 写 tool.called → 调 handler → 落库 effects 与终态事件。
|
|
388
|
+
* 返回三态结果;暂停态携带 action 事件 id 交还 harness。
|
|
389
|
+
*/
|
|
390
|
+
executeTool(input: ExecuteToolInput): Promise<EnvironmentToolResult>;
|
|
391
|
+
/**
|
|
392
|
+
* 结清已解决的 action:对每个 resolved 的 action 恢复执行其工具(调 handler.resume),
|
|
393
|
+
* 把恢复结果落库为终态事件。返回每个 action 的结清结果。
|
|
394
|
+
*/
|
|
395
|
+
settleResolvedActions(input: SettleResolvedActionsInput): Promise<SettledActionResult[]>;
|
|
396
|
+
/**
|
|
397
|
+
* 应用业务 worker 回传的完成结果:写 effects 与终态事件,或在 paused 时写 pending action。
|
|
398
|
+
* 该入口供持久化 work queue 的结算 runner 使用。
|
|
399
|
+
*/
|
|
400
|
+
applyCompletedWork(input: ApplyCompletedWorkInput): Promise<EnvironmentToolResult>;
|
|
401
|
+
/**
|
|
402
|
+
* 收口悬空 tool 调用:把指定 tool.called(无终态、无未决 action)标记为 orphaned 失败。
|
|
403
|
+
* 由 orchestrator 的 stale-running reconciler 在会话恢复时调用,防止事件流残留开口调用。
|
|
404
|
+
*/
|
|
405
|
+
failOpenToolCalls(input: FailOpenToolCallsInput): Promise<void>;
|
|
406
|
+
}
|
package/src/harness.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DerivedSessionState,
|
|
3
|
+
HarnessTrigger,
|
|
4
|
+
SessionEvent,
|
|
5
|
+
SessionEventPayload,
|
|
6
|
+
SessionEventType,
|
|
7
|
+
SessionRecord,
|
|
8
|
+
StopReason,
|
|
9
|
+
} from './session/index.js';
|
|
10
|
+
import type { AppendEventInput, GetEventsInput, SessionStore } from './session/store.js';
|
|
11
|
+
import type { ResourceSlots, Environment, EnvironmentExecutionMode, EnvironmentHostedPolicy } from './environment.js';
|
|
12
|
+
import type { ToolDescriptor } from './tool.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 模型上下文的内容 part。
|
|
16
|
+
*
|
|
17
|
+
* 除了用户输入的 text/image/document(来自 user.message)外,投影会把 assistant 轮的
|
|
18
|
+
* 结构化产出也表达为 part,供各 provider adapter 组装成原生 block:
|
|
19
|
+
* - `tool-call`:assistant 发起的工具调用(Anthropic tool_use / OpenAI function_call)。
|
|
20
|
+
* `toolCallId` 用于和 `tool-result` 配对;若 provider 返回了原生 id,则必须使用原生 id,
|
|
21
|
+
* 否则回退到 session events 里 tool.called 的事件 id。
|
|
22
|
+
* - `tool-result`:工具执行结果(Anthropic tool_result / OpenAI function_call_output)。
|
|
23
|
+
* - `reasoning`:assistant 的推理/thinking。`providerState` 是 provider 原生续传状态,协议层不解释。
|
|
24
|
+
* 回放时同一 model turn 的并行 tool_use 与其共享 thinking 块必须聚合进同一条 assistant 消息
|
|
25
|
+
* (见 buildModelContextFromSessionEvents),否则部分 provider 会拒绝跨轮续传。
|
|
26
|
+
* - `json`:通用结构化文本兜底(如工具结果的非原生表达)。
|
|
27
|
+
*/
|
|
28
|
+
export type ModelContextContent =
|
|
29
|
+
| SessionEventPayload<'user.message'>['content'][number]
|
|
30
|
+
| { type: 'json'; value: unknown }
|
|
31
|
+
| { type: 'tool-call'; toolCallId: string; toolName: string; input: unknown }
|
|
32
|
+
| { type: 'tool-result'; toolCallId: string; toolName: string; result: unknown }
|
|
33
|
+
| { type: 'reasoning'; text: string; providerState?: Record<string, unknown> };
|
|
34
|
+
|
|
35
|
+
export type ModelContextMessage = {
|
|
36
|
+
role: 'user' | 'assistant' | 'tool' | 'system';
|
|
37
|
+
toolName?: string;
|
|
38
|
+
content: ModelContextContent[];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type ModelContext = {
|
|
42
|
+
messages: ModelContextMessage[];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type BoundRuntimeSession = {
|
|
46
|
+
record: SessionRecord;
|
|
47
|
+
store: SessionStore;
|
|
48
|
+
appendEvent<TType extends SessionEventType>(
|
|
49
|
+
input: Omit<AppendEventInput<TType>, 'sessionId'>,
|
|
50
|
+
): Promise<SessionEvent<TType>>;
|
|
51
|
+
getEvents(input?: Omit<GetEventsInput, 'sessionId'>): Promise<SessionEvent[]>;
|
|
52
|
+
getDerivedState(): Promise<DerivedSessionState>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type HarnessInfo = {
|
|
56
|
+
id: string;
|
|
57
|
+
version: number;
|
|
58
|
+
name?: string;
|
|
59
|
+
metadata: Record<string, unknown>;
|
|
60
|
+
contextResolve?: {
|
|
61
|
+
keys: string[];
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type RunUntilIdleInput = {
|
|
66
|
+
session: BoundRuntimeSession;
|
|
67
|
+
harness?: HarnessInfo & { model?: string };
|
|
68
|
+
modelOverride?: HarnessModelPort;
|
|
69
|
+
systemPrompt: string;
|
|
70
|
+
executionMode: EnvironmentExecutionMode;
|
|
71
|
+
hostedPolicy?: EnvironmentHostedPolicy;
|
|
72
|
+
localToolNames?: readonly string[];
|
|
73
|
+
resources: ResourceSlots;
|
|
74
|
+
resourceBindings?: Record<string, unknown>;
|
|
75
|
+
tools: ToolDescriptor[];
|
|
76
|
+
maxModelTurns?: number;
|
|
77
|
+
trigger: HarnessTrigger;
|
|
78
|
+
abortSignal?: AbortSignal;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type RunUntilIdleResult = {
|
|
82
|
+
status: 'idle' | 'queued' | 'aborted' | 'failed';
|
|
83
|
+
reason?: string;
|
|
84
|
+
retryable?: boolean;
|
|
85
|
+
fatal?: boolean;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export interface Harness {
|
|
89
|
+
runUntilIdle(input: RunUntilIdleInput): Promise<RunUntilIdleResult>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type HarnessModelToolCall = {
|
|
93
|
+
// 模型 provider 返回的原生工具调用 id。Anthropic thinking signature 依赖原样回传同轮
|
|
94
|
+
// tool_use block,因此该 id 必须跨 Environment 落库并参与后续 tool_result 配对。
|
|
95
|
+
providerToolCallId?: string;
|
|
96
|
+
toolName: string;
|
|
97
|
+
input: unknown;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type HarnessModelRunInput = {
|
|
101
|
+
context: ModelContext;
|
|
102
|
+
systemPrompt: string;
|
|
103
|
+
tools: ToolDescriptor[];
|
|
104
|
+
abortSignal?: AbortSignal;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export type HarnessModelRunResult =
|
|
108
|
+
| {
|
|
109
|
+
type: 'assistant_message';
|
|
110
|
+
text: string;
|
|
111
|
+
reasoningText?: string;
|
|
112
|
+
reasoningDisplayMode?: 'summary' | 'full';
|
|
113
|
+
// provider 原生续传状态,供 Harness 落库并在后续轮回传。
|
|
114
|
+
providerState?: Record<string, unknown>;
|
|
115
|
+
}
|
|
116
|
+
| {
|
|
117
|
+
type: 'tool_calls';
|
|
118
|
+
toolCalls: HarnessModelToolCall[];
|
|
119
|
+
// 模型可能在同一轮里既输出叙述文本/推理,又发起工具调用(Claude / DeepSeek 等
|
|
120
|
+
// 常见此行为)。这些字段携带同轮产出的助手文本,供 Harness 在执行工具前落库为
|
|
121
|
+
// assistant.output.* 事件;缺省(仅纯工具调用)时为 undefined。
|
|
122
|
+
text?: string;
|
|
123
|
+
reasoningText?: string;
|
|
124
|
+
reasoningDisplayMode?: 'summary' | 'full';
|
|
125
|
+
// provider 原生续传状态。工具轮尤其关键,部分 provider 要求下一轮带回原生状态。
|
|
126
|
+
providerState?: Record<string, unknown>;
|
|
127
|
+
}
|
|
128
|
+
| {
|
|
129
|
+
type: 'end_turn';
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export interface HarnessModelPort {
|
|
133
|
+
run(input: HarnessModelRunInput): Promise<HarnessModelRunResult>;
|
|
134
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@ct-agents/protocol` 是通用 Agent 协议与运行时契约包。
|
|
3
|
+
*
|
|
4
|
+
* 应用、前端和后端 packages 都可以从该根入口引入需要的 DTO、事件、配置类型与 SPI。
|
|
5
|
+
*/
|
|
6
|
+
export * from './store.js';
|
|
7
|
+
export * from './session/index.js';
|
|
8
|
+
export * from './session-api.js';
|
|
9
|
+
export * from './tool.js';
|
|
10
|
+
export * from './tool-exec.js';
|
|
11
|
+
export * from './tool-registry.js';
|
|
12
|
+
export * from './work-queue.js';
|
|
13
|
+
export * from './context-resolve.js';
|
|
14
|
+
export * from './environment.js';
|
|
15
|
+
export * from './harness.js';
|
|
16
|
+
export * from './orchestrator.js';
|
|
17
|
+
export * from './prompt.js';
|
|
18
|
+
export * from './skill.js';
|
|
19
|
+
export * from './memory.js';
|