dsh-auto-flow 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js ADDED
@@ -0,0 +1,592 @@
1
+ import { Service } from "@deepseek-ai/cordis";
2
+ import { HarnessError } from "@deepseek-ai/dsh-llm";
3
+ import { defineTool } from "@deepseek-ai/dsh-tools";
4
+ import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
5
+ import z from "@deepseek-ai/schemastery";
6
+ import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
7
+ import { z as z$1 } from "zod";
8
+ //#region src/domain.ts
9
+ /**
10
+ * dsh-auto-flow 的持久化领域(dsh-storage domain)声明。
11
+ *
12
+ * 插件的持久化状态一律进 **domain storage**(这里),与 session 日志完全隔离
13
+ * (`dsh-storage-domain` 只存非会话数据、不追加会话事件)。不要把机器执行信息 /
14
+ * 运行态写成会话里的 durable 节点——往会话日志里塞非会话数据,会导致该会话在
15
+ * dsh 重启后无法正常打开。
16
+ *
17
+ * 领域用 `defineDomain` 声明一次(表 schema 用 zod,`z.infer` 派生类型),
18
+ * 由 `ctx.storageDomain.open(spec)` 打开,返回的 `Domain` 提供同步读 + 串行化落盘写。
19
+ *
20
+ * 高级选项(DomainSpec)与适用场景:
21
+ * - layout: 'single'(默认)整单元一文档;'per-record' 按记录分文档,适合大/稀疏/可
22
+ * 独立丢弃的记录(如投影缓存),并逐记录做版本隔离(过期记录文档被丢弃而非迁移)。
23
+ * - compatibleVersions: 列旧版本号,声明当前 record schema 仍能接受旧记录(把旧字段
24
+ * 声明为可选)。升级 version 后写这里。
25
+ * - invalidRecords: 缺省(打开遇坏记录直接拒绝)——权威数据用这个;'backup-and-skip'
26
+ * 把坏记录移到备份、打开继续——可丢弃的派生数据用这个。全局槽始终拒绝。
27
+ * - global: 全局单例槽(schema + 未写前 initial),见下方示例。
28
+ */
29
+ /** 领域名:UNIT_NAME_RE /^[a-z][a-z0-9_]*$/,由脚手架按包名推导(dsh_auto_flow)。 */
30
+ const GreetingDomain = defineDomain({
31
+ name: "dsh_auto_flow",
32
+ version: 1,
33
+ layout: "single",
34
+ global: {
35
+ schema: z$1.object({ lastGreetedAt: z$1.number().nullable() }),
36
+ initial: { lastGreetedAt: null }
37
+ },
38
+ tables: { greetings: domainTable(z$1.object({
39
+ id: z$1.number(),
40
+ text: z$1.string(),
41
+ createdAt: z$1.number()
42
+ })) }
43
+ });
44
+ //#endregion
45
+ //#region src/invariant.ts
46
+ /**
47
+ * 断言助手:失败抛 HarnessError(稳定机器可路由码 INVARIANT)。
48
+ * 官方参考:@deepseek-ai/dsh-goal 的 ./invariant 出口。
49
+ */
50
+ /**
51
+ * 断言 condition 为真,否则抛 INVARIANT 错误(route on code,勿解析 message)。
52
+ * @param condition - 期望为真的条件。
53
+ * @param message - 失败时的人类可读消息。
54
+ */
55
+ function invariant(condition, message) {
56
+ if (!condition) throw new HarnessError(message, "INVARIANT");
57
+ }
58
+ //#endregion
59
+ //#region src/index.ts
60
+ /**
61
+ * dsh-auto-flow — Host 半(dsh 的 Node 进程内运行)。
62
+ *
63
+ * 通信与平台集成(业务开发按此分工):
64
+ * - 重通道:AutoFlowService(Cordis 服务,@Remote 严格模式暴露给浏览器,
65
+ * 契约由 `pnpm typert:gen` 生成——双向 zod 校验 + 完整类型)
66
+ * - 轻执行也走 @Remote:`exec` 在宿主内经 ctx.shell(与 bash 工具同一执行器、不经过
67
+ * 模型)执行;`runInBackground` 走 shell.start 自管后台进程 + `execRead`/`execKill` 消费。
68
+ * - 模型工具:plugin_auto_flow(复用上面的服务;presentCall + presentResult)
69
+ * - 斜杠命令:/auto-flow(ctx.commands 注册,用户键入后直接进 handler,不经过模型)
70
+ * - 持久化:greetings 写入 domain storage(dsh-storage-domain,与 session 日志隔离;
71
+ * 不追加自定义会话事件——harness 持久化读路径会拒绝未知事件类型,见 send 的说明)
72
+
73
+ * - 事件:声明 + 发出 + 监听 `autoFlow/recorded`(跨插件实时通知 seam)
74
+ * - 系统提示词:ctx.systemPrompt.section 贡献一条模型可见指引
75
+ * - 设置:同一份 Config schema 注册为 settings namespace,UI 内可编辑、可 watch
76
+ * - 生命周期:持久层异步打开走 `[Service.init]`(Cordis 类插件 init 钩子)
77
+ */
78
+ var __runInitializers = function(thisArg, initializers, value) {
79
+ var useValue = arguments.length > 2;
80
+ for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
81
+ return useValue ? value : void 0;
82
+ };
83
+ var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
84
+ function accept(f) {
85
+ if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
86
+ return f;
87
+ }
88
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
89
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
90
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
91
+ var _, done = false;
92
+ for (var i = decorators.length - 1; i >= 0; i--) {
93
+ var context = {};
94
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
95
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
96
+ context.addInitializer = function(f) {
97
+ if (done) throw new TypeError("Cannot add initializers after decoration has completed");
98
+ extraInitializers.push(accept(f || null));
99
+ };
100
+ var result = (0, decorators[i])(kind === "accessor" ? {
101
+ get: descriptor.get,
102
+ set: descriptor.set
103
+ } : descriptor[key], context);
104
+ if (kind === "accessor") {
105
+ if (result === void 0) continue;
106
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
107
+ if (_ = accept(result.get)) descriptor.get = _;
108
+ if (_ = accept(result.set)) descriptor.set = _;
109
+ if (_ = accept(result.init)) initializers.unshift(_);
110
+ } else if (_ = accept(result)) {
111
+ if (kind === "field") initializers.unshift(_);
112
+ else descriptor[key] = _;
113
+ }
114
+ }
115
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
116
+ done = true;
117
+ };
118
+ /** 模型可见的工具名。 */
119
+ const TOOL_NAME = "plugin_auto_flow";
120
+ /** 斜杠命令名(小写、不带斜杠前缀)。 */
121
+ const COMMAND_NAME = "auto-flow";
122
+ /** 设置命名空间:与插件短名一致(小写字母开头,字母/数字/连字符)。 */
123
+ const SETTINGS_NS = "auto-flow";
124
+ /** 默认问候前缀。 */
125
+ const DEFAULT_PREFIX = "Hello";
126
+ /** 单条 `exec` 命令允许的最大 UTF-8 字节长度(随部署变化,可经 Config / settings 覆盖)。 */
127
+ const DEFAULT_MAX_COMMAND_BYTES = 1024;
128
+ /** dsh-auto-flow 的领域错误:extends HarnessError 以便工具结果/重放保留失败类别。 */
129
+ var GreetingError = class extends HarnessError {
130
+ constructor(message, code) {
131
+ super(message, code);
132
+ }
133
+ };
134
+ /** 在边界处校验并归一化一份配置。 */
135
+ function resolveConfig(config = {}) {
136
+ const prefix = (config.prefix ?? DEFAULT_PREFIX).trim();
137
+ if (prefix === "") throw new GreetingError("auto-flow: prefix 不能是空字符串", "GREETING_EMPTY_PREFIX");
138
+ const maxCommandBytes = config.maxCommandBytes ?? DEFAULT_MAX_COMMAND_BYTES;
139
+ if (!Number.isSafeInteger(maxCommandBytes) || maxCommandBytes < 1) throw new GreetingError(`auto-flow: maxCommandBytes 必须是正的安全整数,收到 ${String(maxCommandBytes)}`, "GREETING_INVALID_MAX_BYTES");
140
+ return {
141
+ prefix,
142
+ maxCommandBytes
143
+ };
144
+ }
145
+ /** 配置 schema:既是 Loader 校验的类静态 Config,也是 settings namespace 的 schema。 */
146
+ const ConfigSchema = z.object({
147
+ prefix: z.string().default(DEFAULT_PREFIX),
148
+ maxCommandBytes: z.number().step(1).min(1).default(DEFAULT_MAX_COMMAND_BYTES)
149
+ });
150
+ /** 把 ShellRunResult 映射为客户端安全的 ExecForegroundResult(保留截断/沙箱事实)。 */
151
+ function mapRunResult(result) {
152
+ return {
153
+ kind: "foreground",
154
+ exitCode: result.exitCode,
155
+ signal: result.signal,
156
+ timedOut: result.timedOut,
157
+ aborted: result.aborted,
158
+ timeoutMs: result.timeoutMs,
159
+ stdout: {
160
+ text: result.stdout.text,
161
+ truncated: result.stdout.truncated,
162
+ ...result.stdout.spillPath !== void 0 ? { spillPath: result.stdout.spillPath } : {}
163
+ },
164
+ stderr: {
165
+ text: result.stderr.text,
166
+ truncated: result.stderr.truncated,
167
+ ...result.stderr.spillPath !== void 0 ? { spillPath: result.stderr.spillPath } : {}
168
+ },
169
+ ...result.sandbox !== void 0 ? { sandbox: {
170
+ mode: result.sandbox.mode,
171
+ denied: result.sandbox.denied,
172
+ ...result.sandbox.enforcement !== void 0 ? { enforcement: result.sandbox.enforcement } : {},
173
+ ...result.sandbox.runnerFailed !== void 0 ? { runnerFailed: result.sandbox.runnerFailed } : {}
174
+ } } : {}
175
+ };
176
+ }
177
+ /** 后台进程增量读取:附上丢失/溢出提示(与 tool-bash 的 renderProcessRead 同构)。 */
178
+ function renderProcessRead(read) {
179
+ const notices = [];
180
+ if (read.lossy) {
181
+ const paths = [read.stdoutSpillPath, read.stderrSpillPath].filter((path) => path !== void 0);
182
+ notices.push(`[部分输出被丢弃;完整输出:${paths.length > 0 ? paths.join(", ") : "(unavailable)"}]`);
183
+ }
184
+ if (notices.length === 0) return read.delta;
185
+ return `${read.delta}${read.delta.length > 0 && !read.delta.endsWith("\n") ? "\n" : ""}${notices.join("\n")}`;
186
+ }
187
+ /**
188
+ * 示例服务:类本身就是插件——static inject / Config 是 Cordis 类插件的约定,
189
+ * 构造器里的注册(工具/事件/设置/命令)随插件卸载自动回收。
190
+ *
191
+ * 平台服务消费方式(跨插件协作走 Cordis 服务、禁止互相 import):
192
+ * - 必需依赖用 `static inject`(tools);
193
+ * - 可选依赖用 `ctx.inject([...], cb)`(settings / commands / systemPrompt /
194
+ * autoFlowFormatter)——服务缺失时插件照常加载;
195
+ * - shell / sandboxPolicy 是可选服务:用 `ctx.get(...)` 读取,未组合时
196
+ * `exec` 报错、插件其余部分照常工作;
197
+ * - 持久层异步打开走 `[Service.init]`(storageDomain 可选:缺省退化为内存态)。
198
+ */
199
+ let AutoFlowService = (() => {
200
+ let _classSuper = TypertRemoteService;
201
+ let _instanceExtraInitializers = [];
202
+ let _send_decorators;
203
+ let _list_decorators;
204
+ let _last_decorators;
205
+ let _exec_decorators;
206
+ let _execRead_decorators;
207
+ let _execKill_decorators;
208
+ return class AutoFlowService extends _classSuper {
209
+ static {
210
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
211
+ _send_decorators = [Remote("send")];
212
+ _list_decorators = [Remote("list")];
213
+ _last_decorators = [Remote("last")];
214
+ _exec_decorators = [Remote("exec")];
215
+ _execRead_decorators = [Remote("execRead")];
216
+ _execKill_decorators = [Remote("execKill")];
217
+ __esDecorate(this, null, _send_decorators, {
218
+ kind: "method",
219
+ name: "send",
220
+ static: false,
221
+ private: false,
222
+ access: {
223
+ has: (obj) => "send" in obj,
224
+ get: (obj) => obj.send
225
+ },
226
+ metadata: _metadata
227
+ }, null, _instanceExtraInitializers);
228
+ __esDecorate(this, null, _list_decorators, {
229
+ kind: "method",
230
+ name: "list",
231
+ static: false,
232
+ private: false,
233
+ access: {
234
+ has: (obj) => "list" in obj,
235
+ get: (obj) => obj.list
236
+ },
237
+ metadata: _metadata
238
+ }, null, _instanceExtraInitializers);
239
+ __esDecorate(this, null, _last_decorators, {
240
+ kind: "method",
241
+ name: "last",
242
+ static: false,
243
+ private: false,
244
+ access: {
245
+ has: (obj) => "last" in obj,
246
+ get: (obj) => obj.last
247
+ },
248
+ metadata: _metadata
249
+ }, null, _instanceExtraInitializers);
250
+ __esDecorate(this, null, _exec_decorators, {
251
+ kind: "method",
252
+ name: "exec",
253
+ static: false,
254
+ private: false,
255
+ access: {
256
+ has: (obj) => "exec" in obj,
257
+ get: (obj) => obj.exec
258
+ },
259
+ metadata: _metadata
260
+ }, null, _instanceExtraInitializers);
261
+ __esDecorate(this, null, _execRead_decorators, {
262
+ kind: "method",
263
+ name: "execRead",
264
+ static: false,
265
+ private: false,
266
+ access: {
267
+ has: (obj) => "execRead" in obj,
268
+ get: (obj) => obj.execRead
269
+ },
270
+ metadata: _metadata
271
+ }, null, _instanceExtraInitializers);
272
+ __esDecorate(this, null, _execKill_decorators, {
273
+ kind: "method",
274
+ name: "execKill",
275
+ static: false,
276
+ private: false,
277
+ access: {
278
+ has: (obj) => "execKill" in obj,
279
+ get: (obj) => obj.execKill
280
+ },
281
+ metadata: _metadata
282
+ }, null, _instanceExtraInitializers);
283
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, {
284
+ enumerable: true,
285
+ configurable: true,
286
+ writable: true,
287
+ value: _metadata
288
+ });
289
+ }
290
+ /** 依赖的服务;缺失时 Loader 会等待其出现。 */
291
+ static inject = ["tools"];
292
+ /** Config 的 Schema(Cordis 类插件约定:Loader 读类静态 `Config` 校验注入并补齐默认值;
293
+ * 模块级同名导出不会被 Loader 识别——生成插件请把 Schema 放在类上)。 */
294
+ static Config = ConfigSchema;
295
+ /** 校验后的配置(settings scope 可在运行时覆盖,故非 readonly)。 */
296
+ resolved = __runInitializers(this, _instanceExtraInitializers);
297
+ /** 问候的内存视图(domain storage 是持久层)。 */
298
+ greetings = [];
299
+ /** 已打开的持久化领域;无后端时保持 null(内存态兜底)。 */
300
+ storage = null;
301
+ /** 第二包提供的格式化服务;未安装第二包时保持 null(可选依赖)。 */
302
+ formatter = null;
303
+ /** 最近一次提交的问候(由 `autoFlow/recorded` 监听器维护,供 @Remote last)。 */
304
+ lastRecorded = null;
305
+ /** 后台进程句柄(@Remote 轻执行自管;不依赖 ctx.jobs,见 exec 说明)。 */
306
+ background = /* @__PURE__ */ new Map();
307
+ nextBgId = 1;
308
+ nextId = 1;
309
+ constructor(ctx, config = {}) {
310
+ super(ctx, "autoFlow");
311
+ this.resolved = resolveConfig(config);
312
+ this.registerTool(ctx);
313
+ this.registerSettings(ctx, config);
314
+ this.registerCommand(ctx);
315
+ this.registerSystemPrompt(ctx);
316
+ this.registerCore(ctx);
317
+ this.registerEvents(ctx);
318
+ this.registerBackgroundCleanup(ctx);
319
+ }
320
+ /**
321
+ * 异步初始化钩子(Cordis 类插件约定):构造完成后运行。持久层在这里打开——
322
+ * 与 @deepseek-ai/dsh-message-feedback 的 `[Service.init]` 同构;storageDomain
323
+ * 是可选服务,缺失时保持内存态兜底。
324
+ */
325
+ async [Service.init]() {
326
+ const storageDomain = this.ctx.get("storageDomain");
327
+ if (storageDomain === void 0) return;
328
+ try {
329
+ this.storage = await storageDomain.open(GreetingDomain);
330
+ this.hydrateFromStorage();
331
+ } catch {}
332
+ }
333
+ /** 模型工具:复用 @Remote send;presentCall + presentResult 双态展示。 */
334
+ registerTool(ctx) {
335
+ ctx.tools.register(defineTool({
336
+ name: TOOL_NAME,
337
+ description: "追加一条问候记录,返回新记录与当前总数。",
338
+ parameters: { text: {
339
+ type: "string",
340
+ required: true,
341
+ description: "问候内容"
342
+ } },
343
+ output: {
344
+ schema: {
345
+ type: "object",
346
+ additionalProperties: false,
347
+ properties: {
348
+ hello: {
349
+ type: "object",
350
+ required: true,
351
+ additionalProperties: false,
352
+ properties: {
353
+ id: {
354
+ type: "integer",
355
+ required: true
356
+ },
357
+ text: {
358
+ type: "string",
359
+ required: true
360
+ },
361
+ createdAt: {
362
+ type: "integer",
363
+ required: true
364
+ }
365
+ }
366
+ },
367
+ total: {
368
+ type: "integer",
369
+ required: true
370
+ }
371
+ }
372
+ },
373
+ render: (_args, value) => [{
374
+ type: "text",
375
+ text: `已记录:${value.hello.text}(共 ${value.total} 条)`
376
+ }]
377
+ },
378
+ presentCall: (args) => ({
379
+ card: "generic",
380
+ title: "记录问候",
381
+ kind: "other",
382
+ rawInput: args.text
383
+ }),
384
+ presentResult: (_args, result) => {
385
+ const block = result.content[0];
386
+ if (block === void 0 || block.type !== "text") return void 0;
387
+ return {
388
+ card: "generic",
389
+ title: "问候已记录",
390
+ content: [{
391
+ type: "text",
392
+ text: block.text
393
+ }]
394
+ };
395
+ },
396
+ execute: async (args) => {
397
+ return {
398
+ hello: await this.send(args.text),
399
+ total: this.greetings.length
400
+ };
401
+ }
402
+ }));
403
+ }
404
+ /** 设置:同一份 Config schema 注册为可编辑 namespace,入口配置作为 base 层。 */
405
+ registerSettings(ctx, config) {
406
+ ctx.inject(["settings"], (settingsCtx) => {
407
+ let readConfig = () => config;
408
+ settingsCtx.settings.installSection(ctx, SETTINGS_NS, ConfigSchema, config, {
409
+ validate: (value) => {
410
+ resolveConfig(value);
411
+ },
412
+ setSource: (current) => {
413
+ readConfig = current;
414
+ },
415
+ onChange: () => {
416
+ this.resolved = resolveConfig(readConfig());
417
+ }
418
+ });
419
+ });
420
+ }
421
+ /** 从持久化领域恢复内存视图与自增 id。 */
422
+ hydrateFromStorage() {
423
+ if (this.storage === null) return;
424
+ const table = this.storage.table("greetings");
425
+ const restored = [];
426
+ let maxId = 0;
427
+ for (const [, greeting] of table.entries()) {
428
+ restored.push(greeting);
429
+ if (greeting.id > maxId) maxId = greeting.id;
430
+ }
431
+ restored.sort((a, b) => a.id - b.id);
432
+ this.greetings.length = 0;
433
+ this.greetings.push(...restored);
434
+ this.nextId = maxId + 1;
435
+ invariant(this.greetings.length === restored.length, "水合后的内存视图应与恢复记录一致");
436
+ }
437
+ /** 斜杠命令:用户键入 /auto-flow 后直接进 handler(不经过模型)。 */
438
+ registerCommand(ctx) {
439
+ ctx.inject(["commands"], (commandsCtx) => {
440
+ commandsCtx.commands.register({
441
+ name: COMMAND_NAME,
442
+ description: "记录一条问候并返回当前总数",
443
+ input: { hint: "问候内容" },
444
+ handler: async (invocation) => {
445
+ const text = invocation.rawInput.trim();
446
+ if (text === "") return {
447
+ kind: "error",
448
+ text: "问候内容不能为空"
449
+ };
450
+ return {
451
+ kind: "success",
452
+ text: `已记录:${(await this.send(text)).text}(共 ${this.greetings.length} 条)`
453
+ };
454
+ }
455
+ });
456
+ });
457
+ }
458
+ /** 系统提示词:贡献一条模型可见指引(name 唯一、order 升序拼接)。 */
459
+ registerSystemPrompt(ctx) {
460
+ ctx.inject(["systemPrompt"], (promptCtx) => {
461
+ promptCtx.systemPrompt.section({
462
+ name: "auto-flow",
463
+ order: 900,
464
+ text: "auto-flow: 问候通过模型工具 plugin_auto_flow 或斜杠命令 /auto-flow 追加,落插件 domain storage;exec 轻执行输出只回调用方、不进会话日志。"
465
+ });
466
+ });
467
+ }
468
+ /** 事件:监听自身域事件维护 lastRecorded 缓存(演示 ctx.on —— 监听器随 fiber 回收)。
469
+ * 其它插件也可 ctx.on('autoFlow/recorded', ...) 订阅,实现跨插件实时通知。 */
470
+ registerEvents(ctx) {
471
+ ctx.on("autoFlow/recorded", ({ greeting }) => {
472
+ this.lastRecorded = greeting;
473
+ });
474
+ }
475
+ /** 卸载时回收所有未结束的后台进程(@Remote 轻执行自管句柄,必须随 fiber 清理)。 */
476
+ registerBackgroundCleanup(ctx) {
477
+ ctx.effect(() => () => {
478
+ for (const proc of this.background.values()) proc.kill();
479
+ this.background.clear();
480
+ }, "auto-flow: 后台进程清理");
481
+ }
482
+ /** 跨插件服务消费:第二包(dsh-auto-flow-core)可选提供 autoFlowFormatter。
483
+ * 用 ctx.inject 而非 static inject——第二包未安装时插件照常加载(send 走无格式化兜底)。 */
484
+ registerCore(ctx) {
485
+ ctx.inject(["autoFlowFormatter"], (coreCtx) => {
486
+ this.formatter = coreCtx.get("autoFlowFormatter");
487
+ });
488
+ }
489
+ /**
490
+ * 追加一条问候并返回记录(写透 domain storage;若有第二包则先格式化;提交后发域事件)。
491
+ *
492
+ * 不追加自定义会话事件:harness 的 session 持久化读路径会拒绝 `KNOWN_SESSION_EVENT_TYPES`
493
+ * 之外的事件类型(除非信封标记 `ignorable`,而 `Session.append` 无此参数),把插件
494
+ * 自定义事件写进会话日志会导致该会话在重启后无法打开。
495
+ */
496
+ async send(text) {
497
+ const raw = this.resolved.prefix + text;
498
+ const greeting = {
499
+ id: this.nextId,
500
+ text: this.formatter !== null ? this.formatter.format(raw) : raw,
501
+ createdAt: Date.now()
502
+ };
503
+ invariant(Number.isSafeInteger(greeting.id) && greeting.id >= 1, "greeting id 必须是正安全整数");
504
+ this.nextId += 1;
505
+ this.greetings.push(greeting);
506
+ if (this.storage !== null) try {
507
+ await this.storage.table("greetings").put(String(greeting.id), greeting);
508
+ await this.storage.global.set({ lastGreetedAt: greeting.createdAt });
509
+ } catch {}
510
+ this.ctx.emit("autoFlow/recorded", { greeting });
511
+ return greeting;
512
+ }
513
+ /** 返回全部问候记录(内存视图,已由 domain storage 水合)。 */
514
+ list() {
515
+ return Promise.resolve([...this.greetings]);
516
+ }
517
+ /** 返回最近一次提交的问候(由域事件监听器维护),首次提交前为 null。 */
518
+ last() {
519
+ return Promise.resolve(this.lastRecorded);
520
+ }
521
+ /**
522
+ * 执行一条命令行(宿主内经 ctx.shell,与 bash 工具同一执行器,绝不经过模型)。
523
+ * 输出只回调用方、不进会话日志。@Remote 严格契约保证浏览器端完整类型与校验。
524
+ *
525
+ * 相比模型工具(@deepseek-ai/dsh-tool-bash)的 shell 面,@Remote 轻执行只能拿到
526
+ * 能映射过来的子集:timeoutMs / stdoutMaxBytes / sandboxPolicy / 后台任务 / 结果保真。
527
+ * 模型工具专属的 seam(shellEnv.collect 需要 ToolExecution、sandbox 升级需要 approval
528
+ * + callId、abort 需要 ToolExecution.signal)不适用于 @Remote,此处不做。
529
+ *
530
+ * 注意:@Remote 方法不能带 `agent: Agent` 参数——第三方仓库的 typert 生成器无法解析
531
+ * `agent` lookup(其 wire 类型 SessionId 是 branded 类型,官方 monorepo 内才可用)。
532
+ * 因此沙箱策略退化为「部署默认」(`sandboxPolicy.resolve()` 无 session)。
533
+ *
534
+ * 后台执行不走 ctx.jobs(那是给模型工具 job_output/job_kill 用的,需要 agent owner +
535
+ * dsh-tool-jobs 控制器,@Remote 里拿不到):直接用 `shell.start()` 拿 ShellProcess
536
+ * 句柄,自管在 this.background Map 里,execRead/execKill 直接操作句柄;卸载时统一 kill。
537
+ *
538
+ * shell / sandboxPolicy 是可选依赖:未组合时抛对应 RemoteError,插件其余照常。
539
+ */
540
+ async exec(request) {
541
+ const command = request.command.trim();
542
+ if (command === "") throw new RemoteError("gateway/bad-request", "命令不能为空", {});
543
+ const actualBytes = Buffer.byteLength(command, "utf8");
544
+ if (actualBytes > this.resolved.maxCommandBytes) throw new RemoteError("autoFlow/command-too-long", `命令超过 ${this.resolved.maxCommandBytes} 字节`, {
545
+ maxBytes: this.resolved.maxCommandBytes,
546
+ actualBytes
547
+ });
548
+ const shell = this.ctx.get("shell");
549
+ if (shell === void 0) throw new RemoteError("autoFlow/shell-missing", "exec 需要组合 shell 提供方(@deepseek-ai/dsh-shell)", {});
550
+ const sandboxPolicy = this.ctx.get("sandboxPolicy")?.resolve();
551
+ const spec = shell.resolve({
552
+ command,
553
+ ...request.workdir !== void 0 ? { workdir: request.workdir } : {},
554
+ ...request.timeoutMs !== void 0 ? { timeoutMs: request.timeoutMs } : {},
555
+ ...request.stdoutMaxBytes !== void 0 ? { stdoutMaxBytes: request.stdoutMaxBytes } : {},
556
+ ...sandboxPolicy !== void 0 ? { sandboxPolicy } : {}
557
+ });
558
+ if (request.runInBackground === true) {
559
+ const proc = shell.start(spec);
560
+ const jobId = `auto-flow-${this.nextBgId}`;
561
+ this.nextBgId += 1;
562
+ this.background.set(jobId, proc);
563
+ return {
564
+ kind: "background",
565
+ jobId
566
+ };
567
+ }
568
+ return mapRunResult(await shell.run(spec));
569
+ }
570
+ /** 读后台进程的下一次增量/终态输出(自管 ShellProcess 句柄,不依赖 ctx.jobs)。 */
571
+ async execRead(jobId) {
572
+ const proc = this.background.get(jobId);
573
+ if (proc === void 0) throw new RemoteError("autoFlow/job-not-found", "后台任务不存在或已结束", {});
574
+ const settled = proc.status === "completed" || proc.status === "killed";
575
+ return {
576
+ text: renderProcessRead(proc.readOutput()),
577
+ status: proc.status,
578
+ ...settled ? { detail: proc.signal !== null ? `signal: ${proc.signal}` : `exit code: ${proc.exitCode ?? 0}` } : {}
579
+ };
580
+ }
581
+ /** 请求取消后台进程(自管 ShellProcess 句柄,不依赖 ctx.jobs)。 */
582
+ async execKill(jobId) {
583
+ const proc = this.background.get(jobId);
584
+ if (proc === void 0) return { kind: "already-finished" };
585
+ return { kind: proc.kill() ? "requested" : "already-finished" };
586
+ }
587
+ };
588
+ })();
589
+ //#endregion
590
+ export { AutoFlowService, AutoFlowService as default };
591
+
592
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ /* Generated by @deepseek-ai/dsh-typert-generator from FaceModel — do not edit. */
2
+
3
+ export declare const TYPERT: unknown