@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
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { EnvironmentId, HarnessId } from './ids.js';
|
|
2
|
+
import type { SessionRecord } from './record.js';
|
|
3
|
+
import type { DerivedSessionState, SessionStreamEnvelope } from './derived-state.js';
|
|
4
|
+
import type {
|
|
5
|
+
SessionEvent,
|
|
6
|
+
SessionEventActor,
|
|
7
|
+
SessionEventPayload,
|
|
8
|
+
SessionEventType,
|
|
9
|
+
} from './events.js';
|
|
10
|
+
|
|
11
|
+
/** 分页结果:items + 下一页游标(缺省表示无更多数据)。 */
|
|
12
|
+
export interface Page<TItem> {
|
|
13
|
+
items: TItem[];
|
|
14
|
+
nextCursor?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class SessionEventAppendConflictError extends Error {
|
|
18
|
+
constructor(message: string) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = 'SessionEventAppendConflictError';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ─── SessionStore 公共接口(scope-free)──────────────────────────────────
|
|
25
|
+
//
|
|
26
|
+
// SessionStore 是 protocol 的 scope-free 会话存储契约:方法签名不含隔离维度,
|
|
27
|
+
// 隔离由实现封装在实例内部(如 @ct-agents/session 的 ScopedSessionStore 在构造期绑定 scope,
|
|
28
|
+
// 方法内部用 RLS 执行)。这样通用运行时(harness / orchestrator)面向本接口编程时无需感知隔离键。
|
|
29
|
+
|
|
30
|
+
/** 创建会话输入:harness / environment 以 pin(id + version)固化,业务信息进 metadata。 */
|
|
31
|
+
export type CreateSessionInput = {
|
|
32
|
+
appId?: string;
|
|
33
|
+
parentSessionId?: string;
|
|
34
|
+
rootSessionId?: string;
|
|
35
|
+
spawnedByEventId?: string;
|
|
36
|
+
harnessId: HarnessId;
|
|
37
|
+
harnessVersion: number;
|
|
38
|
+
environmentId: EnvironmentId;
|
|
39
|
+
environmentUpdatedAt: string;
|
|
40
|
+
title: string;
|
|
41
|
+
metadata: Record<string, unknown>;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 追加事件输入。泛型 `TType` 在编译期保证 type / payload 配对;
|
|
46
|
+
* `idempotencyKey` 让重复追加幂等(同 key 返回已存在事件,不产生新 seq)。
|
|
47
|
+
*/
|
|
48
|
+
export type AppendEventInput<TType extends SessionEventType = SessionEventType> = {
|
|
49
|
+
eventId?: string;
|
|
50
|
+
sessionId: string;
|
|
51
|
+
type: TType;
|
|
52
|
+
turnId?: string;
|
|
53
|
+
parentEventId?: string;
|
|
54
|
+
actor: SessionEventActor;
|
|
55
|
+
payload: SessionEventPayload<TType>;
|
|
56
|
+
idempotencyKey?: string;
|
|
57
|
+
/**
|
|
58
|
+
* 写入前置条件:当同一 session、同一 parentEventId 下已存在这些类型的子事件时拒绝追加。
|
|
59
|
+
*
|
|
60
|
+
* 用于把「某个父事件只能拥有一个终态/解决事件」这类状态机防线下沉到 store 事务内,
|
|
61
|
+
* 避免 runtime 在 check-then-append 间遇到并发窗口。
|
|
62
|
+
*/
|
|
63
|
+
rejectIfParentHasTypes?: SessionEventType[];
|
|
64
|
+
/**
|
|
65
|
+
* 写入前置条件:当同一 session、指定事件下已存在这些类型的子事件时拒绝追加。
|
|
66
|
+
*
|
|
67
|
+
* 用于同时防守非当前 parent 的状态,例如基于 action 写工具超时失败时,
|
|
68
|
+
* 在同一事务内确认该 action 尚未被 `user.action.resolved` 解决。
|
|
69
|
+
*/
|
|
70
|
+
rejectIfEventHasChildren?: Array<{
|
|
71
|
+
eventId: string;
|
|
72
|
+
types: SessionEventType[];
|
|
73
|
+
}>;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/** 读取事件输入:支持按 seq 区间、limit 分页、类型过滤与正倒序。 */
|
|
77
|
+
export type GetEventsInput = {
|
|
78
|
+
sessionId: string;
|
|
79
|
+
eventIds?: string[];
|
|
80
|
+
parentEventId?: string;
|
|
81
|
+
fromSeq?: number;
|
|
82
|
+
toSeq?: number;
|
|
83
|
+
limit?: number;
|
|
84
|
+
order?: 'asc' | 'desc';
|
|
85
|
+
type?: SessionEventType[];
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/** 订阅事件流输入:从 fromSeq 起 replay 历史并持续推送新事件,signal 终止时停止。 */
|
|
89
|
+
export type StreamEventsInput = {
|
|
90
|
+
sessionId: string;
|
|
91
|
+
fromSeq?: number;
|
|
92
|
+
signal?: AbortSignal;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/** 列出会话输入;默认只返回顶层未删除 session。 */
|
|
96
|
+
export type ListSessionsInput = {
|
|
97
|
+
includeDeleted?: boolean;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 会话存储 SPI(scope-free)。
|
|
102
|
+
*
|
|
103
|
+
* 持久化 session 元数据与 append-only 事件日志,并提供派生状态计算与事件流订阅。
|
|
104
|
+
* 由 apps/api 装配具体实现注入(生产用 ScopedSessionStore,测试用 InMemorySessionStore);
|
|
105
|
+
* 运行时面向本接口编程,不依赖具体实现类。
|
|
106
|
+
*/
|
|
107
|
+
export interface SessionStore {
|
|
108
|
+
/** 创建一条新会话记录。 */
|
|
109
|
+
create(input: CreateSessionInput): Promise<SessionRecord>;
|
|
110
|
+
/** 按 id 读取会话;不存在返回 null。 */
|
|
111
|
+
get(sessionId: string): Promise<SessionRecord | null>;
|
|
112
|
+
/** 枚举当前 scope 内的顶层会话;默认不包含软删除记录。 */
|
|
113
|
+
list(input?: ListSessionsInput): Promise<SessionRecord[]>;
|
|
114
|
+
/**
|
|
115
|
+
* 追加一条事件到会话日志。seq 单调递增;idempotencyKey 命中时返回已存在事件。
|
|
116
|
+
* 写入口不再做运行时封闭校验——type / payload 由泛型在编译期保证配对。
|
|
117
|
+
*/
|
|
118
|
+
appendEvent<TType extends SessionEventType>(
|
|
119
|
+
input: AppendEventInput<TType>,
|
|
120
|
+
): Promise<SessionEvent<TType>>;
|
|
121
|
+
/** 按 seq 区间 / 类型 / 分页读取事件。 */
|
|
122
|
+
getEvents(input: GetEventsInput): Promise<Page<SessionEvent>>;
|
|
123
|
+
/** 由事件日志派生当前会话状态(status / pending action / open tool call 等)。 */
|
|
124
|
+
getDerivedState(sessionId: string): Promise<DerivedSessionState>;
|
|
125
|
+
/** 订阅事件流:先 replay fromSeq 之后的历史,再持续推送新事件。 */
|
|
126
|
+
streamEvents(input: StreamEventsInput): AsyncIterable<SessionStreamEnvelope>;
|
|
127
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { DerivedSessionState, SessionStreamEnvelope } from './session/derived-state.js';
|
|
2
|
+
import type {
|
|
3
|
+
SessionEvent,
|
|
4
|
+
SessionEventPayload,
|
|
5
|
+
SessionEventType,
|
|
6
|
+
UserInterruptPayload,
|
|
7
|
+
UserMessagePayload,
|
|
8
|
+
} from './session/events.js';
|
|
9
|
+
import type { EnvironmentId, HarnessId } from './session/ids.js';
|
|
10
|
+
import type { SessionRecord } from './session/record.js';
|
|
11
|
+
import type { Page } from './session/store.js';
|
|
12
|
+
|
|
13
|
+
/** BFF / 消费端创建 managed session 的请求体。 */
|
|
14
|
+
export interface CreateManagedSessionRequest {
|
|
15
|
+
harnessId: HarnessId | string;
|
|
16
|
+
harnessVersion?: number;
|
|
17
|
+
environmentId: EnvironmentId | string;
|
|
18
|
+
title: string;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
idempotencyKey?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** managed session 创建响应。 */
|
|
24
|
+
export interface CreateManagedSessionResponse {
|
|
25
|
+
session: SessionRecord;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 向 session 追加用户消息并触发 wake 的请求体。 */
|
|
29
|
+
export interface SendManagedSessionMessageRequest {
|
|
30
|
+
content: UserMessagePayload['content'];
|
|
31
|
+
idempotencyKey?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 追加用户消息后的响应。 */
|
|
35
|
+
export interface SendManagedSessionMessageResponse {
|
|
36
|
+
event: SessionEvent<'user.message'>;
|
|
37
|
+
wakeAccepted: boolean;
|
|
38
|
+
wakeReason?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** 批量追加事件请求,用于平台 API 与 SDK 的通用事件写入口。 */
|
|
42
|
+
export interface SessionEventAppendRequest {
|
|
43
|
+
events: Array<{
|
|
44
|
+
type: SessionEventType;
|
|
45
|
+
payload: SessionEventPayload;
|
|
46
|
+
turnId?: string;
|
|
47
|
+
parentEventId?: string;
|
|
48
|
+
}>;
|
|
49
|
+
idempotencyKey?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** 批量追加事件响应。 */
|
|
53
|
+
export interface SessionEventAppendResponse {
|
|
54
|
+
events: SessionEvent[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 查询 session 列表的 wire 参数。 */
|
|
58
|
+
export interface ListManagedSessionsRequest {
|
|
59
|
+
appId?: string;
|
|
60
|
+
rootSessionId?: string;
|
|
61
|
+
status?: DerivedSessionState['status'];
|
|
62
|
+
limit?: number;
|
|
63
|
+
cursor?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** session 列表响应。 */
|
|
67
|
+
export type ListManagedSessionsResponse = Page<SessionRecord>;
|
|
68
|
+
|
|
69
|
+
/** 查询 session 事件的 wire 参数。 */
|
|
70
|
+
export interface ListManagedSessionEventsRequest {
|
|
71
|
+
fromSeq?: number;
|
|
72
|
+
toSeq?: number;
|
|
73
|
+
limit?: number;
|
|
74
|
+
order?: 'asc' | 'desc';
|
|
75
|
+
type?: SessionEventType[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** session 事件列表响应。 */
|
|
79
|
+
export type ListManagedSessionEventsResponse = Page<SessionEvent>;
|
|
80
|
+
|
|
81
|
+
/** SSE 事件流 envelope,直接复用 session store 的流式 envelope。 */
|
|
82
|
+
export type ManagedSessionStreamEnvelope = SessionStreamEnvelope;
|
|
83
|
+
|
|
84
|
+
/** 读取派生态响应。 */
|
|
85
|
+
export interface GetManagedSessionStateResponse {
|
|
86
|
+
state: DerivedSessionState;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** 中断 session 请求。 */
|
|
90
|
+
export interface InterruptManagedSessionRequest extends UserInterruptPayload {
|
|
91
|
+
idempotencyKey?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** 中断 session 响应。 */
|
|
95
|
+
export interface InterruptManagedSessionResponse {
|
|
96
|
+
accepted: boolean;
|
|
97
|
+
reason?: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** 解决待处理 action 请求。 */
|
|
101
|
+
export interface ResolveManagedSessionActionRequest {
|
|
102
|
+
result: unknown;
|
|
103
|
+
idempotencyKey?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** 解决待处理 action 响应。 */
|
|
107
|
+
export interface ResolveManagedSessionActionResponse {
|
|
108
|
+
event: SessionEvent<'user.action.resolved'>;
|
|
109
|
+
wakeAccepted: boolean;
|
|
110
|
+
wakeReason?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** 删除 session 请求。 */
|
|
114
|
+
export interface DeleteManagedSessionRequest {
|
|
115
|
+
idempotencyKey?: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** 删除 session 响应。 */
|
|
119
|
+
export interface DeleteManagedSessionResponse {
|
|
120
|
+
event: SessionEvent<'session.deleted'>;
|
|
121
|
+
}
|
package/src/skill.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Store } from './store.js';
|
|
2
|
+
|
|
3
|
+
// Skill 以 name 为唯一标识符(主键):不再有独立 id 字段,也不带 `skill:` 前缀。
|
|
4
|
+
export type SkillView = {
|
|
5
|
+
appId?: string | null;
|
|
6
|
+
name: string;
|
|
7
|
+
version: number;
|
|
8
|
+
description: string;
|
|
9
|
+
instructions: string;
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
createdAt?: string;
|
|
12
|
+
updatedAt?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type CreateSkillInput = {
|
|
16
|
+
appId?: string | null;
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
instructions: string;
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// name 为主键、创建后不可改,仅作为定位键;description/instructions/enabled 可更新。
|
|
24
|
+
export type UpdateSkillInput = {
|
|
25
|
+
name: string;
|
|
26
|
+
baseVersion: number;
|
|
27
|
+
description?: string;
|
|
28
|
+
instructions?: string;
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type SkillListQuery = {
|
|
33
|
+
appId?: string;
|
|
34
|
+
includeGlobal?: boolean;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export interface SkillStore extends Store<SkillView> {
|
|
38
|
+
list(query?: SkillListQuery): Promise<SkillView[]>;
|
|
39
|
+
create(input: CreateSkillInput): Promise<SkillView>;
|
|
40
|
+
update(input: UpdateSkillInput): Promise<SkillView>;
|
|
41
|
+
delete(name: string): Promise<void>;
|
|
42
|
+
}
|
package/src/store.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 跨包共享的最小 Store 契约。
|
|
3
|
+
*
|
|
4
|
+
* 只抽离所有 store 真实共有的读取能力,写操作仍由各自领域 store 自行声明。
|
|
5
|
+
*/
|
|
6
|
+
export interface Store<TRecord, TKey = string> {
|
|
7
|
+
/** 按主键读取单条记录;不存在返回 null。 */
|
|
8
|
+
get(key: TKey): Promise<TRecord | null>;
|
|
9
|
+
/** 枚举全部记录(具体过滤 / 排序由实现决定)。 */
|
|
10
|
+
list(): Promise<TRecord[]>;
|
|
11
|
+
}
|
package/src/tool-exec.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { SessionEvent } from './session/index.js';
|
|
2
|
+
import type { ToolEffect, ToolExecutionStatus } from './tool.js';
|
|
3
|
+
import type { ExecutionScope } from './session/index.js';
|
|
4
|
+
import type { EnvironmentExecutionMode } from './environment.js';
|
|
5
|
+
|
|
6
|
+
export type ToolWorkItemMode = 'execute' | 'resume';
|
|
7
|
+
|
|
8
|
+
export type ToolResumeWorkPayload = {
|
|
9
|
+
actionEvent: SessionEvent<'agent.action.requested'>;
|
|
10
|
+
resolvedEvent: SessionEvent<'user.action.resolved'>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 平台下发给业务 worker 的一次 tool 调用。
|
|
15
|
+
*
|
|
16
|
+
* 该类型是跨进程执行边界的核心契约:平台只保存 opaque 输入与资源绑定信息,
|
|
17
|
+
* worker 侧用本地 ToolHandler 的 inputSchema 再做运行时校验,并在业务进程内访问资源和密钥。
|
|
18
|
+
*/
|
|
19
|
+
export interface ToolWorkItem {
|
|
20
|
+
/** 工作项幂等键,同时作为 lease / complete 的稳定关联标识。 */
|
|
21
|
+
workId: string;
|
|
22
|
+
/** 应用路由键,由消费端 key 或环境密钥派生。 */
|
|
23
|
+
appId: string;
|
|
24
|
+
/** 环境路由键,worker 只能认领自己环境下的工作项。 */
|
|
25
|
+
environmentId: string;
|
|
26
|
+
/**
|
|
27
|
+
* 工作项创建时的执行面快照。
|
|
28
|
+
*
|
|
29
|
+
* environment 配置之后可能被切换;claim/reclaim 必须按该快照过滤,避免平台 hosted
|
|
30
|
+
* worker 认领历史 self-hosted 工作项,或接入方 worker 认领 hosted 工作项。
|
|
31
|
+
*/
|
|
32
|
+
executionMode?: EnvironmentExecutionMode;
|
|
33
|
+
sessionId: string;
|
|
34
|
+
rootSessionId: string;
|
|
35
|
+
scope: ExecutionScope;
|
|
36
|
+
/** 对应已经落库的 tool.called 事件,用于回写 terminal 事件和 effects parent。 */
|
|
37
|
+
toolCallEventId: string;
|
|
38
|
+
toolName: string;
|
|
39
|
+
/**
|
|
40
|
+
* 工作项模式。缺省为 execute,保持旧 worker item 兼容;resume 用于把人工审批后的
|
|
41
|
+
* 真实恢复执行重新交回业务 worker,而不是在平台进程调用 catalog stub handler。
|
|
42
|
+
*/
|
|
43
|
+
mode?: ToolWorkItemMode;
|
|
44
|
+
/** 平台不解释输入结构,worker 侧按 handler.inputSchema 校验。 */
|
|
45
|
+
input: unknown;
|
|
46
|
+
/** resume 工作项携带的 action 请求与解决事件快照。 */
|
|
47
|
+
resume?: ToolResumeWorkPayload;
|
|
48
|
+
/** 环境资源绑定快照。这里只保存绑定信息,不保存业务数据本体或密钥。 */
|
|
49
|
+
resourceBindings?: Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* worker 回传给平台的 tool 执行结果。
|
|
54
|
+
*
|
|
55
|
+
* 保留 modelResult 与 effects 双通道:modelResult 回喂模型,effects 由平台作为 opaque
|
|
56
|
+
* session 事件写入日志,业务 UI 事件不会被塞进模型上下文。
|
|
57
|
+
*/
|
|
58
|
+
export interface ToolExecutionResult {
|
|
59
|
+
workId: string;
|
|
60
|
+
status: ToolExecutionStatus;
|
|
61
|
+
modelResult?: unknown;
|
|
62
|
+
effects?: ToolEffect[];
|
|
63
|
+
error?: {
|
|
64
|
+
code?: string;
|
|
65
|
+
message: string;
|
|
66
|
+
retryable?: boolean;
|
|
67
|
+
details?: unknown;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ToolDescriptor } from './tool.js';
|
|
2
|
+
|
|
3
|
+
/** app 内唯一的 tool 名称。全局内置 tool 在引用处用 appId=null 表达。 */
|
|
4
|
+
export type AppScopedToolName = string;
|
|
5
|
+
|
|
6
|
+
/** harness 引用 tool 时使用的归属信息。null 表示平台全局内置 tool。 */
|
|
7
|
+
export interface AppScopedToolReference {
|
|
8
|
+
appId: string | null;
|
|
9
|
+
toolName: AppScopedToolName;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ToolOrigin = 'platform_builtin' | 'app_catalog' | 'sub_agent';
|
|
13
|
+
|
|
14
|
+
/** worker 上报的 resource implementation 描述;不允许提交平台内部 origin/capability。 */
|
|
15
|
+
export type EnvironmentResourceImplementationRegistrationDto = {
|
|
16
|
+
id: string;
|
|
17
|
+
title: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
optionsSchema?: Record<string, unknown>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** worker 上报的 public resource slot 描述;只描述业务自有 app catalog resource。 */
|
|
23
|
+
export type EnvironmentResourceSlotRegistrationDto = {
|
|
24
|
+
name: string;
|
|
25
|
+
title: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
implementations?: EnvironmentResourceImplementationRegistrationDto[];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** 环境密钥上报的 public body:appId 只能由鉴权 scope 派生,不能由请求体覆盖。 */
|
|
31
|
+
export interface ToolRegistrationRequestBody {
|
|
32
|
+
tools: ToolDescriptor[];
|
|
33
|
+
resourceSlots?: EnvironmentResourceSlotRegistrationDto[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** worker 连接平台时上报的 tool 目录。平台按 appId + toolName 幂等覆盖。 */
|
|
37
|
+
export interface ToolRegistrationCommand {
|
|
38
|
+
appId: string;
|
|
39
|
+
tools: ToolDescriptor[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** 平台 DB 中持久化的 tool 描述符快照。descriptor 仍以代码上报为唯一真相源。 */
|
|
43
|
+
export interface PersistedToolDescriptor {
|
|
44
|
+
appId: string;
|
|
45
|
+
toolName: string;
|
|
46
|
+
descriptor: ToolDescriptor;
|
|
47
|
+
origin: ToolOrigin;
|
|
48
|
+
updatedAt: string;
|
|
49
|
+
}
|