@cjhyy/code-shell-pet 0.8.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.
Files changed (74) hide show
  1. package/README.md +99 -0
  2. package/dist/capability.d.ts +15 -0
  3. package/dist/capability.js +251 -0
  4. package/dist/current-time.d.ts +5 -0
  5. package/dist/current-time.js +43 -0
  6. package/dist/delegate-work.d.ts +7 -0
  7. package/dist/delegate-work.js +142 -0
  8. package/dist/delegation.d.ts +33 -0
  9. package/dist/delegation.js +6 -0
  10. package/dist/disclosure/catalog.d.ts +22 -0
  11. package/dist/disclosure/catalog.js +104 -0
  12. package/dist/disclosure/constants.d.ts +7 -0
  13. package/dist/disclosure/constants.js +7 -0
  14. package/dist/disclosure/jsonl.d.ts +9 -0
  15. package/dist/disclosure/jsonl.js +61 -0
  16. package/dist/disclosure/latest-result.d.ts +8 -0
  17. package/dist/disclosure/latest-result.js +35 -0
  18. package/dist/disclosure/search.d.ts +21 -0
  19. package/dist/disclosure/search.js +124 -0
  20. package/dist/disclosure/selector.d.ts +2 -0
  21. package/dist/disclosure/selector.js +5 -0
  22. package/dist/disclosure/todo-snapshot.d.ts +8 -0
  23. package/dist/disclosure/todo-snapshot.js +54 -0
  24. package/dist/follow-ups.d.ts +21 -0
  25. package/dist/follow-ups.js +102 -0
  26. package/dist/gateway.d.ts +41 -0
  27. package/dist/gateway.js +263 -0
  28. package/dist/host-actions.d.ts +51 -0
  29. package/dist/host-actions.js +405 -0
  30. package/dist/index.capability.d.ts +7 -0
  31. package/dist/index.capability.js +7 -0
  32. package/dist/index.d.ts +32 -0
  33. package/dist/index.disclosure.d.ts +13 -0
  34. package/dist/index.disclosure.js +13 -0
  35. package/dist/index.js +32 -0
  36. package/dist/index.protocol.d.ts +8 -0
  37. package/dist/index.protocol.js +8 -0
  38. package/dist/index.team.d.ts +2 -0
  39. package/dist/index.team.js +2 -0
  40. package/dist/long-task.d.ts +251 -0
  41. package/dist/long-task.js +795 -0
  42. package/dist/mobile-remote.d.ts +14 -0
  43. package/dist/mobile-remote.js +39 -0
  44. package/dist/outbound-message.d.ts +15 -0
  45. package/dist/outbound-message.js +127 -0
  46. package/dist/pending-decision-index.d.ts +24 -0
  47. package/dist/pending-decision-index.js +110 -0
  48. package/dist/profile.d.ts +27 -0
  49. package/dist/profile.js +120 -0
  50. package/dist/projection-extension.d.ts +8 -0
  51. package/dist/projection-extension.js +238 -0
  52. package/dist/protocol.d.ts +58 -0
  53. package/dist/protocol.js +15 -0
  54. package/dist/report-to-mimi.d.ts +7 -0
  55. package/dist/report-to-mimi.js +87 -0
  56. package/dist/run-params.d.ts +34 -0
  57. package/dist/run-params.js +387 -0
  58. package/dist/segment-closure.d.ts +52 -0
  59. package/dist/segment-closure.js +130 -0
  60. package/dist/session-control.d.ts +5 -0
  61. package/dist/session-control.js +46 -0
  62. package/dist/session-index.d.ts +22 -0
  63. package/dist/session-index.js +324 -0
  64. package/dist/sessions-tool.d.ts +21 -0
  65. package/dist/sessions-tool.js +145 -0
  66. package/dist/team.d.ts +13 -0
  67. package/dist/team.js +38 -0
  68. package/dist/tool-arguments.d.ts +1 -0
  69. package/dist/tool-arguments.js +10 -0
  70. package/dist/topic-segment.d.ts +55 -0
  71. package/dist/topic-segment.js +37 -0
  72. package/dist/types.d.ts +95 -0
  73. package/dist/types.js +1 -0
  74. package/package.json +56 -0
package/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # @cjhyy/code-shell-pet
2
+
3
+ CodeShell 的 Mimi(项目工作状态助理 / 桌面 Pet)领域能力包。它只依赖
4
+ `@cjhyy/code-shell-core/extension`,由宿主显式加载,不把 Pet 字面量和产品规则重新写回
5
+ core。
6
+
7
+ ## 为什么单独成包
8
+
9
+ Mimi 与普通 Work Session 的职责不同:
10
+
11
+ - Mimi 负责澄清、汇总、路由和跨 Workspace 协调;
12
+ - Work Session 负责读文件、运行工具和完成执行任务;
13
+ - Desktop 负责窗口、导航、磁盘会话目录和具体 UI。
14
+
15
+ 因此 Pet 包只拥有可跨宿主复用的领域规则和投影协议。Electron 窗口、React 组件、磁盘目录
16
+ 扫描等宿主能力留在 `packages/desktop`;通用引擎、工具系统和协议生命周期留在
17
+ `packages/core`。
18
+
19
+ ## 模块边界
20
+
21
+ | 模块 | 职责 |
22
+ | ----------------------------------------- | -------------------------------------------------------------------- |
23
+ | `capability.ts` | 唯一组合根:注册行为 profile、DelegateWork、投影 observer 和参数校验 |
24
+ | `profile.ts` | Mimi 的系统边界、允许工具和单轮委托服务 |
25
+ | `delegation.ts` / `delegate-work.ts` | Workspace、可复用 Session 与结构化委托工具 |
26
+ | `run-params.ts` | 宿主输入的有界校验、规范化和 fail-closed 快照 |
27
+ | `gateway.ts` / `host-actions.ts` | 渠道只读发现与当前会话回复,不接收任意 target |
28
+ | `outbound-message.ts` | 使用宿主不透明目标的主动消息请求;工具接受不等于发送成功 |
29
+ | `follow-ups.ts` | 读取/处理 Desktop 同一份“需要跟进”,不建立第二套 Todo |
30
+ | `sessions-tool.ts` / `session-control.ts` | Session 渐进读取与精确归档选择器 |
31
+ | `current-time.ts` | 宿主时间原子能力,不让 Mimi 猜 epoch 或调用 shell |
32
+ | `team.ts` | 历史兼容的数字人团队纯数据契约;Pet runtime 不消费 |
33
+ | `session-index.ts` | Work Session 的纯状态机与安全摘要 |
34
+ | `pending-decision-index.ts` | 跨 Session 待决策读模型,不持有 resolver 或原始问题正文 |
35
+ | `projection-extension.ts` | 把 core 的通用生命周期事件投影为 Pet snapshot/delta |
36
+ | `protocol.ts` / `types.ts` | Pet 专属 wire shape 和领域类型 |
37
+ | `long-task.ts` | 长程任务状态机、事件协议、恢复提示和有界经理上下文 |
38
+
39
+ 这种拆分让状态机、输入边界和协议适配可以分别测试,也让 Desktop 以后更换呈现方式时不必改
40
+ Mimi 的安全规则。
41
+
42
+ ## 公共入口
43
+
44
+ Pet 保持为一个 npm 包,不把 projection 和能力工厂继续拆成多个独立发布单元。新代码
45
+ 应按职责使用两个最小子入口:
46
+
47
+ ```ts
48
+ import { createPetCapability } from "@cjhyy/code-shell-pet/capability";
49
+ import {
50
+ PET_PROJECTION_DELTA_METHOD,
51
+ type PetProjectionDelta,
52
+ type PetProjectionSnapshotResult,
53
+ } from "@cjhyy/code-shell-pet/protocol";
54
+ ```
55
+
56
+ | 入口 | 稳定职责 | 不包含 |
57
+ | ---------------------------------- | ------------------------------------------- | --------------------------------------------- |
58
+ | `@cjhyy/code-shell-pet/capability` | 宿主组合用的 `createPetCapability` | prompt、工具实现、状态机 |
59
+ | `@cjhyy/code-shell-pet/protocol` | snapshot/delta 方法名和投影 wire types | `SessionIndex`、pending 状态机、observer 实现 |
60
+ | `@cjhyy/code-shell-pet/team` | 仅历史兼容;新消费方应使用 Desktop 共享契约 | Pet runtime 或 core 扩展逻辑 |
61
+
62
+ 根入口 `@cjhyy/code-shell-pet` 为现有 Desktop 和动态 capability loader 保持兼容;它不会被
63
+ 移除,但新消费方不应为了一个协议类型或团队 parser 依赖整个兼容 barrel。包不提供通配
64
+ deep import,未列出的源码模块均为内部实现。
65
+
66
+ ## 必须保持的约束
67
+
68
+ - Mimi 不能获得 Workspace 执行工具;执行工作只能通过 `DelegateWork`。
69
+ - Workspace 和可复用 Session 必须来自宿主本轮提供的闭集。
70
+ - Pet 不接收、保存或路由数字人 / 数字人团队 id;数字人工作归项目 Session。
71
+ - `pet` Session 不得出现在普通 Work Session 列表或待决策列表。
72
+ - 投影不得包含 resolver、工具参数、原始 AskUser 正文、命令或凭证。
73
+ - 畸形宿主输入必须隐藏委托能力或返回错误,不能降级为不受限执行。
74
+ - Pet 包不得运行时依赖 Desktop、TUI、Server、Web 或其他产品包。
75
+ - `DelegateWork` 的启动回执不等于完成;只有真实 Work Session 终态才能关闭长程任务。
76
+ - `GatewayReply`、`SendMessage` 和其他宿主动作只登记请求;成功/失败措辞只能来自宿主执行回执。
77
+ - “需要跟进”只有一个 canonical service 和 durable state;不得重新增加独立 Todo store/tool/UI。
78
+
79
+ ## 长程任务宿主契约
80
+
81
+ `long-task.ts` 只定义可移植的领域状态机。Desktop 当前负责把它接到真实运行时:
82
+
83
+ - 每次委派先原子写入 `pet/long-tasks.json`,再启动 Work Session;
84
+ - Work Session 使用 core Goal 模式持续执行,Pet 不复制第二套执行循环;
85
+ - 顶层 stream event 提供 checkpoint 和可信的 `goal_progress(met/exhausted)` 终态,Pet projection 提供待审批/断线状态;
86
+ - 暂停会持久化 Goal 的 paused 状态并停止当前 turn,恢复/重试继续同一 durable Session;
87
+ - 进程重启后,未观察到终态的任务进入 `interrupted`,用户可从原 Session 恢复;
88
+ - 终态先写入去重的工作记忆,再确认交付;中途崩溃会在启动时安全重放;
89
+ - 每个 Mimi turn 只注入有界 `longTasks` 摘要,不回灌完整 transcript 或工具参数。
90
+
91
+ ## 验证
92
+
93
+ ```bash
94
+ bun test packages/pet/src
95
+ bun run --cwd packages/pet build
96
+ bunx eslint packages/pet/src --max-warnings=0
97
+ ```
98
+
99
+ `build` 会先清空 `dist/`,避免删除或重命名源码后把陈旧产物发布出去。
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The Pet domain packaged as a self-contained core extension module.
3
+ *
4
+ * Hosts opt in explicitly:
5
+ * - desktop injects `CODE_SHELL_CAPABILITY_MODULES=@cjhyy/code-shell-pet#createPetCapability`
6
+ * into the stdio worker env (loaded by core's agent-server-stdio cli);
7
+ * - in-process hosts/tests pass `extensionModules: [createPetCapability()]`
8
+ * to EngineConfig / AgentServer options.
9
+ *
10
+ * Core itself is pet-free: behavior comes from the generic RunBehaviorProfile
11
+ * registry, protocol observation from ProtocolObserver, and the DelegateWork
12
+ * tool joins the composed tool catalog with full exposure metadata.
13
+ */
14
+ import type { ExtensionModule } from "@cjhyy/code-shell-core/extension";
15
+ export declare function createPetCapability(): ExtensionModule;
@@ -0,0 +1,251 @@
1
+ /**
2
+ * The Pet domain packaged as a self-contained core extension module.
3
+ *
4
+ * Hosts opt in explicitly:
5
+ * - desktop injects `CODE_SHELL_CAPABILITY_MODULES=@cjhyy/code-shell-pet#createPetCapability`
6
+ * into the stdio worker env (loaded by core's agent-server-stdio cli);
7
+ * - in-process hosts/tests pass `extensionModules: [createPetCapability()]`
8
+ * to EngineConfig / AgentServer options.
9
+ *
10
+ * Core itself is pet-free: behavior comes from the generic RunBehaviorProfile
11
+ * registry, protocol observation from ProtocolObserver, and the DelegateWork
12
+ * tool joins the composed tool catalog with full exposure metadata.
13
+ */
14
+ import { delegateWorkAvailability, delegateWorkTool, delegateWorkToolDef, rewriteDelegateWorkDef, } from "./delegate-work.js";
15
+ import { mobileRemoteAvailability, mobileRemoteTool, mobileRemoteToolDef, } from "./mobile-remote.js";
16
+ import { gatewayAvailability, gatewayTool, gatewayToolDef } from "./gateway.js";
17
+ import { sessionsAvailability, sessionsTool, sessionsToolDef } from "./sessions-tool.js";
18
+ import { currentTimeAvailability, currentTimeTool, currentTimeToolDef } from "./current-time.js";
19
+ import { manageSessionsAvailability, manageSessionsTool, manageSessionsToolDef, } from "./session-control.js";
20
+ import { followUpsAvailability, followUpsTool, followUpsToolDef, manageFollowUpAvailability, manageFollowUpTool, manageFollowUpToolDef, } from "./follow-ups.js";
21
+ import { rewriteSendMessageDef, sendMessageAvailability, sendMessageTool, sendMessageToolDef, } from "./outbound-message.js";
22
+ import { controlLongTaskAvailability, controlLongTaskTool, controlLongTaskToolDef, gatewayReplyAvailability, gatewayReplyTool, gatewayReplyToolDef, memoryAvailability, memoryTool, memoryToolDef, rewriteGatewayReplyDef, } from "./host-actions.js";
23
+ import { PET_BEHAVIOR_PROFILE } from "./profile.js";
24
+ import { createPetProjectionObserver, PET_HIDDEN_SESSION_KINDS } from "./projection-extension.js";
25
+ import { PET_REPORT_TO_MIMI_METHOD } from "./protocol.js";
26
+ import { reportToMimiAvailability, reportToMimiTool, reportToMimiToolDef, } from "./report-to-mimi.js";
27
+ import { validatePetRunParams } from "./run-params.js";
28
+ export function createPetCapability() {
29
+ let reportToMimi;
30
+ const createProtocolObserver = (host) => {
31
+ reportToMimi = (event) => host.notify(PET_REPORT_TO_MIMI_METHOD, event);
32
+ const projection = createPetProjectionObserver(host);
33
+ return {
34
+ ...projection,
35
+ onServerClose: () => {
36
+ reportToMimi = undefined;
37
+ projection.onServerClose?.();
38
+ },
39
+ };
40
+ };
41
+ return {
42
+ id: "pet",
43
+ behaviorProfiles: [PET_BEHAVIOR_PROFILE],
44
+ createProtocolObserver,
45
+ validateRunParams: validatePetRunParams,
46
+ hiddenSessionKinds: PET_HIDDEN_SESSION_KINDS,
47
+ catalogTools: [
48
+ {
49
+ definition: {
50
+ ...reportToMimiToolDef,
51
+ source: "builtin",
52
+ permissionDefault: "allow",
53
+ isReadOnly: false,
54
+ isConcurrencySafe: false,
55
+ },
56
+ execute: (args, ctx) => reportToMimiTool(args, ctx, reportToMimi),
57
+ exposure: {
58
+ presetTags: ["harness-min", "general"],
59
+ defaultPermissionRules: [{ tool: reportToMimiToolDef.name, decision: "allow" }],
60
+ availability: reportToMimiAvailability,
61
+ },
62
+ },
63
+ {
64
+ definition: {
65
+ ...delegateWorkToolDef,
66
+ source: "builtin",
67
+ permissionDefault: "allow",
68
+ isReadOnly: false,
69
+ isConcurrencySafe: false,
70
+ },
71
+ execute: delegateWorkTool,
72
+ exposure: {
73
+ // Byte-identical to the former builtin registration: HARNESS_TAGS
74
+ // preset tags + explicit allow rule + pet-profile availability and
75
+ // the per-turn workspace-enum rewrite.
76
+ presetTags: ["harness-min", "general"],
77
+ defaultPermissionRules: [{ tool: delegateWorkToolDef.name, decision: "allow" }],
78
+ availability: delegateWorkAvailability,
79
+ rewriteDefinition: rewriteDelegateWorkDef,
80
+ },
81
+ },
82
+ {
83
+ definition: {
84
+ ...mobileRemoteToolDef,
85
+ source: "builtin",
86
+ permissionDefault: "allow",
87
+ isReadOnly: false,
88
+ isConcurrencySafe: false,
89
+ },
90
+ execute: mobileRemoteTool,
91
+ exposure: {
92
+ presetTags: ["harness-min", "general"],
93
+ defaultPermissionRules: [{ tool: mobileRemoteToolDef.name, decision: "allow" }],
94
+ availability: mobileRemoteAvailability,
95
+ },
96
+ },
97
+ {
98
+ definition: {
99
+ ...controlLongTaskToolDef,
100
+ source: "builtin",
101
+ permissionDefault: "allow",
102
+ isReadOnly: false,
103
+ isConcurrencySafe: false,
104
+ },
105
+ execute: controlLongTaskTool,
106
+ exposure: {
107
+ presetTags: ["harness-min", "general"],
108
+ defaultPermissionRules: [{ tool: controlLongTaskToolDef.name, decision: "allow" }],
109
+ availability: controlLongTaskAvailability,
110
+ },
111
+ },
112
+ {
113
+ definition: {
114
+ ...memoryToolDef,
115
+ source: "builtin",
116
+ permissionDefault: "allow",
117
+ isReadOnly: false,
118
+ isConcurrencySafe: false,
119
+ },
120
+ execute: memoryTool,
121
+ exposure: {
122
+ presetTags: ["harness-min", "general"],
123
+ defaultPermissionRules: [{ tool: memoryToolDef.name, decision: "allow" }],
124
+ availability: memoryAvailability,
125
+ },
126
+ },
127
+ {
128
+ definition: {
129
+ ...gatewayToolDef,
130
+ source: "builtin",
131
+ permissionDefault: "allow",
132
+ isReadOnly: true,
133
+ isConcurrencySafe: true,
134
+ },
135
+ execute: gatewayTool,
136
+ exposure: {
137
+ presetTags: ["harness-min", "general"],
138
+ defaultPermissionRules: [{ tool: gatewayToolDef.name, decision: "allow" }],
139
+ availability: gatewayAvailability,
140
+ },
141
+ },
142
+ {
143
+ definition: {
144
+ ...gatewayReplyToolDef,
145
+ source: "builtin",
146
+ permissionDefault: "allow",
147
+ isReadOnly: false,
148
+ isConcurrencySafe: false,
149
+ },
150
+ execute: gatewayReplyTool,
151
+ exposure: {
152
+ presetTags: ["harness-min", "general"],
153
+ defaultPermissionRules: [{ tool: gatewayReplyToolDef.name, decision: "allow" }],
154
+ availability: gatewayReplyAvailability,
155
+ rewriteDefinition: rewriteGatewayReplyDef,
156
+ },
157
+ },
158
+ {
159
+ definition: {
160
+ ...sessionsToolDef,
161
+ source: "builtin",
162
+ permissionDefault: "allow",
163
+ isReadOnly: true,
164
+ isConcurrencySafe: true,
165
+ },
166
+ execute: sessionsTool,
167
+ exposure: {
168
+ presetTags: ["harness-min", "general"],
169
+ defaultPermissionRules: [{ tool: sessionsToolDef.name, decision: "allow" }],
170
+ availability: sessionsAvailability,
171
+ },
172
+ },
173
+ {
174
+ definition: {
175
+ ...followUpsToolDef,
176
+ source: "builtin",
177
+ permissionDefault: "allow",
178
+ isReadOnly: true,
179
+ isConcurrencySafe: true,
180
+ },
181
+ execute: followUpsTool,
182
+ exposure: {
183
+ presetTags: ["harness-min", "general"],
184
+ defaultPermissionRules: [{ tool: followUpsToolDef.name, decision: "allow" }],
185
+ availability: followUpsAvailability,
186
+ },
187
+ },
188
+ {
189
+ definition: {
190
+ ...manageFollowUpToolDef,
191
+ source: "builtin",
192
+ permissionDefault: "allow",
193
+ isReadOnly: false,
194
+ isConcurrencySafe: false,
195
+ },
196
+ execute: manageFollowUpTool,
197
+ exposure: {
198
+ presetTags: ["harness-min", "general"],
199
+ defaultPermissionRules: [{ tool: manageFollowUpToolDef.name, decision: "allow" }],
200
+ availability: manageFollowUpAvailability,
201
+ },
202
+ },
203
+ {
204
+ definition: {
205
+ ...manageSessionsToolDef,
206
+ source: "builtin",
207
+ permissionDefault: "allow",
208
+ isReadOnly: false,
209
+ isConcurrencySafe: false,
210
+ },
211
+ execute: manageSessionsTool,
212
+ exposure: {
213
+ presetTags: ["harness-min", "general"],
214
+ defaultPermissionRules: [{ tool: manageSessionsToolDef.name, decision: "allow" }],
215
+ availability: manageSessionsAvailability,
216
+ },
217
+ },
218
+ {
219
+ definition: {
220
+ ...currentTimeToolDef,
221
+ source: "builtin",
222
+ permissionDefault: "allow",
223
+ isReadOnly: true,
224
+ isConcurrencySafe: true,
225
+ },
226
+ execute: currentTimeTool,
227
+ exposure: {
228
+ presetTags: ["harness-min", "general"],
229
+ defaultPermissionRules: [{ tool: currentTimeToolDef.name, decision: "allow" }],
230
+ availability: currentTimeAvailability,
231
+ },
232
+ },
233
+ {
234
+ definition: {
235
+ ...sendMessageToolDef,
236
+ source: "builtin",
237
+ permissionDefault: "allow",
238
+ isReadOnly: false,
239
+ isConcurrencySafe: false,
240
+ },
241
+ execute: sendMessageTool,
242
+ exposure: {
243
+ presetTags: ["harness-min", "general"],
244
+ defaultPermissionRules: [{ tool: sendMessageToolDef.name, decision: "allow" }],
245
+ availability: sendMessageAvailability,
246
+ rewriteDefinition: rewriteSendMessageDef,
247
+ },
248
+ },
249
+ ],
250
+ };
251
+ }
@@ -0,0 +1,5 @@
1
+ import type { ToolDefinition, ToolVisibilityContext } from "@cjhyy/code-shell-core/extension";
2
+ export declare const CURRENT_TIME_TOOL_NAME = "CurrentTime";
3
+ export declare const currentTimeToolDef: ToolDefinition;
4
+ export declare function currentTimeAvailability(ctx: ToolVisibilityContext): boolean;
5
+ export declare function currentTimeTool(args: Record<string, unknown>): Promise<string>;
@@ -0,0 +1,43 @@
1
+ import { hasOnlyDeclaredToolArguments } from "./tool-arguments.js";
2
+ export const CURRENT_TIME_TOOL_NAME = "CurrentTime";
3
+ export const currentTimeToolDef = {
4
+ name: CURRENT_TIME_TOOL_NAME,
5
+ description: "Return the trusted current local date, time, timezone, UTC offset and epoch. Use this instead " +
6
+ "of guessing from timestamps or trying to run a shell command.",
7
+ inputSchema: {
8
+ type: "object",
9
+ additionalProperties: false,
10
+ properties: {},
11
+ },
12
+ };
13
+ export function currentTimeAvailability(ctx) {
14
+ return ctx.behaviorProfile === "pet";
15
+ }
16
+ export async function currentTimeTool(args) {
17
+ if (!hasOnlyDeclaredToolArguments(args, []))
18
+ return "Error: CurrentTime accepts no arguments.";
19
+ const now = new Date();
20
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
21
+ const parts = new Intl.DateTimeFormat("zh-CN", {
22
+ timeZone,
23
+ year: "numeric",
24
+ month: "2-digit",
25
+ day: "2-digit",
26
+ weekday: "long",
27
+ hour: "2-digit",
28
+ minute: "2-digit",
29
+ second: "2-digit",
30
+ hourCycle: "h23",
31
+ }).format(now);
32
+ const offsetMinutes = -now.getTimezoneOffset();
33
+ const sign = offsetMinutes >= 0 ? "+" : "-";
34
+ const absolute = Math.abs(offsetMinutes);
35
+ const utcOffset = `${sign}${String(Math.floor(absolute / 60)).padStart(2, "0")}:${String(absolute % 60).padStart(2, "0")}`;
36
+ return JSON.stringify({
37
+ epochMs: now.getTime(),
38
+ iso: now.toISOString(),
39
+ local: parts,
40
+ timeZone,
41
+ utcOffset,
42
+ });
43
+ }
@@ -0,0 +1,7 @@
1
+ import type { ToolContext, ToolDefinition, ToolVisibilityContext } from "@cjhyy/code-shell-core/extension";
2
+ import { type PetReusableSessionOption, type PetWorkspaceOption } from "./delegation.js";
3
+ export declare const delegateWorkToolDef: ToolDefinition;
4
+ export declare function delegateWorkAvailability(ctx: ToolVisibilityContext): boolean;
5
+ export declare function rewriteDelegateWorkDef(def: ToolDefinition, ctx: ToolVisibilityContext): ToolDefinition;
6
+ export declare function delegateWorkToolDefFor(workspaces: readonly PetWorkspaceOption[] | undefined, reusableSessions?: readonly PetReusableSessionOption[] | undefined): ToolDefinition;
7
+ export declare function delegateWorkTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
@@ -0,0 +1,142 @@
1
+ import { DELEGATE_WORK_TOOL_NAME, isPetWorkExecutionBackend, } from "./delegation.js";
2
+ // Keep the wire/domain type forward-compatible with persisted "codex"
3
+ // decisions, but advertise only backends the current host actually launches.
4
+ const SUPPORTED_DELEGATE_WORK_BACKENDS = ["codeshell"];
5
+ export const delegateWorkToolDef = {
6
+ name: DELEGATE_WORK_TOOL_NAME,
7
+ description: "Delegate one execution objective to exactly one host-provided Workspace. " +
8
+ "Use only as Mimi after deciding that the user's request requires execution. " +
9
+ "workspace_id must be copied exactly from the available Workspace list.",
10
+ inputSchema: {
11
+ type: "object",
12
+ additionalProperties: false,
13
+ properties: {
14
+ workspace_id: {
15
+ type: "string",
16
+ description: "Exact opaque Workspace id from the host-provided list.",
17
+ },
18
+ objective: {
19
+ type: "string",
20
+ description: "Self-contained objective for the Work Session.",
21
+ },
22
+ executor: {
23
+ type: "string",
24
+ enum: [...SUPPORTED_DELEGATE_WORK_BACKENDS],
25
+ description: "Execution backend. Omit or use 'codeshell' for a normal Work Session. External Codex execution is not supported by this host and must never be substituted silently.",
26
+ },
27
+ session_id: {
28
+ type: "string",
29
+ description: "Optional exact id from the reusable Session list.",
30
+ },
31
+ },
32
+ required: ["workspace_id", "objective"],
33
+ },
34
+ };
35
+ function visibleWorkspaces(ctx) {
36
+ const workspaces = ctx.profileMeta?.petWorkspaces;
37
+ return Array.isArray(workspaces) ? workspaces : [];
38
+ }
39
+ function visibleReusableSessions(ctx) {
40
+ const sessions = ctx.profileMeta?.petReusableSessions;
41
+ return Array.isArray(sessions) ? sessions : [];
42
+ }
43
+ export function delegateWorkAvailability(ctx) {
44
+ return ctx.behaviorProfile === "pet" && visibleWorkspaces(ctx).length > 0;
45
+ }
46
+ function inlineDisplay(value, maximum) {
47
+ return value.replace(/\s+/gu, " ").trim().slice(0, maximum);
48
+ }
49
+ export function rewriteDelegateWorkDef(def, ctx) {
50
+ const dynamic = delegateWorkToolDefFor(visibleWorkspaces(ctx), visibleReusableSessions(ctx));
51
+ return { ...def, description: dynamic.description, inputSchema: dynamic.inputSchema };
52
+ }
53
+ export function delegateWorkToolDefFor(workspaces, reusableSessions = []) {
54
+ const available = workspaces ?? [];
55
+ const sessions = reusableSessions ?? [];
56
+ const workspaceListing = available.length
57
+ ? available
58
+ .map((workspace) => `- ${JSON.stringify(workspace.id)}: ${inlineDisplay(workspace.name, 256)}${workspace.description ? ` — ${inlineDisplay(workspace.description, 4_096)}` : ""}`)
59
+ .join("\n")
60
+ : "- (no Workspace is currently available)";
61
+ const sessionListing = sessions.length
62
+ ? sessions
63
+ .map((session) => `- ${JSON.stringify(session.id)}: ${inlineDisplay(session.name, 256)} (Workspace ${JSON.stringify(session.workspaceId)})${session.description ? ` — ${inlineDisplay(session.description, 4_096)}` : ""}`)
64
+ .join("\n")
65
+ : "- (no existing Session is currently eligible for reuse; omit session_id)";
66
+ const { session_id: _sessionId, ...baseProperties } = delegateWorkToolDef.inputSchema
67
+ .properties;
68
+ return {
69
+ ...delegateWorkToolDef,
70
+ description: `${delegateWorkToolDef.description}\n\nAvailable Workspaces:\n${workspaceListing}\n\nReusable Sessions:\n${sessionListing}`,
71
+ inputSchema: {
72
+ ...delegateWorkToolDef.inputSchema,
73
+ properties: {
74
+ ...baseProperties,
75
+ workspace_id: {
76
+ type: "string",
77
+ enum: available.map((workspace) => workspace.id),
78
+ description: "Exact opaque Workspace id from the available list.",
79
+ },
80
+ ...(sessions.length > 0
81
+ ? {
82
+ session_id: {
83
+ type: "string",
84
+ enum: sessions.map((session) => session.id),
85
+ description: "Optional exact id from the reusable Session list.",
86
+ },
87
+ }
88
+ : {}),
89
+ },
90
+ },
91
+ };
92
+ }
93
+ export async function delegateWorkTool(args, ctx) {
94
+ const services = ctx?.runScopedServices;
95
+ const workspaces = Array.isArray(services?.petWorkspaces) ? services.petWorkspaces : undefined;
96
+ const reusableSessions = Array.isArray(services?.petReusableSessions)
97
+ ? services.petReusableSessions
98
+ : undefined;
99
+ if (!services?.requestPetWorkDelegation || !workspaces || !reusableSessions) {
100
+ return "Error: DelegateWork is available only in a Mimi manager turn.";
101
+ }
102
+ const workspaceId = typeof args.workspace_id === "string" ? args.workspace_id.trim() : "";
103
+ const objective = typeof args.objective === "string" ? args.objective.trim() : "";
104
+ const executor = args.executor === undefined ? "codeshell" : args.executor;
105
+ const reusableSessionId = typeof args.session_id === "string" ? args.session_id.trim() : "";
106
+ if (!workspaceId)
107
+ return "Error: workspace_id is required.";
108
+ if (!objective)
109
+ return "Error: objective is required.";
110
+ if (objective.length > 8_000)
111
+ return "Error: objective is too long (maximum 8000 characters).";
112
+ if (executor === "codex") {
113
+ return "Error: the external Codex execution backend is not supported yet; do not substitute a CodeShell Work Session.";
114
+ }
115
+ if (!isPetWorkExecutionBackend(executor)) {
116
+ return `Error: unknown executor ${JSON.stringify(executor)}. Use ${SUPPORTED_DELEGATE_WORK_BACKENDS.map((backend) => JSON.stringify(backend)).join(" or ")}.`;
117
+ }
118
+ const workspace = workspaces.find((candidate) => candidate?.id === workspaceId);
119
+ if (!workspace) {
120
+ return `Error: unknown workspace_id ${JSON.stringify(workspaceId)}. Copy one exact id from the available Workspace list.`;
121
+ }
122
+ const reusableSession = reusableSessionId
123
+ ? reusableSessions.find((candidate) => candidate?.id === reusableSessionId)
124
+ : undefined;
125
+ if (reusableSessionId && !reusableSession) {
126
+ return `Error: unknown session_id ${JSON.stringify(reusableSessionId)}. Copy one exact id from the reusable Session list or omit session_id.`;
127
+ }
128
+ if (reusableSession && reusableSession.workspaceId !== workspaceId) {
129
+ return "Error: session_id does not belong to workspace_id.";
130
+ }
131
+ const decision = services.requestPetWorkDelegation({
132
+ workspaceId,
133
+ objective,
134
+ ...(executor === "codex" ? { executionBackend: "codex" } : {}),
135
+ ...(reusableSession ? { reusableSessionId: reusableSession.id } : {}),
136
+ });
137
+ if (!decision.ok)
138
+ return `Error: ${decision.error ?? "work delegation was rejected"}`;
139
+ return reusableSession
140
+ ? `Delegation accepted for existing Session ${reusableSession.name} in Workspace ${workspace.name}.`
141
+ : `Delegation accepted for a new Session in Workspace ${workspace.name}.`;
142
+ }
@@ -0,0 +1,33 @@
1
+ export declare const DELEGATE_WORK_TOOL_NAME = "DelegateWork";
2
+ /** Closed set of execution backends; extend here and every schema/validation follows. */
3
+ export declare const PET_WORK_EXECUTION_BACKENDS: readonly ["codeshell", "codex"];
4
+ /** Execution backend selected explicitly by Mimi from the user's request. */
5
+ export type PetWorkExecutionBackend = (typeof PET_WORK_EXECUTION_BACKENDS)[number];
6
+ export declare function isPetWorkExecutionBackend(value: unknown): value is PetWorkExecutionBackend;
7
+ /** Host-provided closed set visible to Mimi for one manager turn. */
8
+ export interface PetWorkspaceOption {
9
+ id: string;
10
+ name: string;
11
+ description?: string;
12
+ }
13
+ /** Host-provided closed set of existing Work Sessions Mimi may continue. */
14
+ export interface PetReusableSessionOption {
15
+ /** Opaque turn-scoped selector; never an unvalidated model-authored Session id. */
16
+ id: string;
17
+ workspaceId: string;
18
+ name: string;
19
+ description?: string;
20
+ }
21
+ /** Structured decision produced only by a successful DelegateWork tool call. */
22
+ export interface PetWorkDelegation {
23
+ workspaceId: string;
24
+ objective: string;
25
+ /** Omitted/CodeShell keeps the normal Work Session executor. */
26
+ executionBackend?: PetWorkExecutionBackend;
27
+ /** Opaque id from the host-provided reusable Session set; absent means create. */
28
+ reusableSessionId?: string;
29
+ }
30
+ export interface PetWorkDelegationDecision {
31
+ ok: boolean;
32
+ error?: string;
33
+ }
@@ -0,0 +1,6 @@
1
+ export const DELEGATE_WORK_TOOL_NAME = "DelegateWork";
2
+ /** Closed set of execution backends; extend here and every schema/validation follows. */
3
+ export const PET_WORK_EXECUTION_BACKENDS = ["codeshell", "codex"];
4
+ export function isPetWorkExecutionBackend(value) {
5
+ return PET_WORK_EXECUTION_BACKENDS.includes(value);
6
+ }
@@ -0,0 +1,22 @@
1
+ export interface DiskWorkSession {
2
+ sessionId: string;
3
+ title: string;
4
+ cwd: string | null;
5
+ status?: string;
6
+ updatedAt: number;
7
+ }
8
+ export declare function listWorkSessionsOnDisk(sessionsRootDir: string, options: {
9
+ limit: number;
10
+ }): Promise<DiskWorkSession[]>;
11
+ /**
12
+ * Resolve one exact ordinary Work Session through the same policy as L1 list.
13
+ * Keeping this check at the read boundary prevents callers that learned an
14
+ * internal id elsewhere from bypassing Pet/sub-agent/child/ephemeral filters.
15
+ */
16
+ export declare function readWorkSessionOnDisk(sessionsRootDir: string, sessionId: string): Promise<DiskWorkSession | null>;
17
+ /**
18
+ * Resolve one opaque host-issued selector without loading and sorting every
19
+ * Session state. Directory names are the only candidates hashed; the ordinary
20
+ * Work Session policy is applied by the exact reader after a match.
21
+ */
22
+ export declare function readWorkSessionBySelectorOnDisk(sessionsRootDir: string, selectorId: string): Promise<DiskWorkSession | null>;