@springbrand/agent-runtime 0.1.3-alpha.3 → 0.1.3-alpha.4

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.
@@ -23,11 +23,13 @@ import type {
23
23
  TemporaryAgentRequest,
24
24
  TemporaryAgentRunContext,
25
25
  } from "./layers/orchestration/temporary-agent/core";
26
- import type { AgentConfig } from "./plugins";
26
+ import type { RuntimeAssemblyInput } from "./runtime-assembler";
27
+ import type { RuntimeAssemblyView } from "./kernel/runtime-assembly-view";
28
+ import type { RuntimeConfigUpdateResult } from "./kernel/runtime-config";
27
29
  import { AgentRuntimeKernel } from "./runtime";
28
30
 
29
31
  /**
30
- * 本文件把应用提供的 `createConfig` 接到 Cloudflare Agent 生命周期。
32
+ * 本文件把应用提供的 Config Definition 与 Planner 接到 Cloudflare Agent 生命周期。
31
33
  *
32
34
  * @remarks
33
35
  * 核心术语见包入口 `index.ts`。
@@ -136,24 +138,6 @@ export interface RuntimeAgentConfigContext<
136
138
  readonly name: string;
137
139
  /** 读取从根节点到直接父节点的 Agent 路径。 */
138
140
  readonly parentPath: readonly RuntimeAgentPathStep[];
139
- /**
140
- * 告诉生成类本次配置最终解析到了哪个 Runtime key。
141
- *
142
- * @remarks
143
- * `createConfig` 在完成业务寻址后调用,同一次创建只能报告一个非空值。
144
- *
145
- * 生成类用该 key 合并重复加载;它只是运行时选择,不会写回业务配置。
146
- */
147
- setResolvedRuntimeKey(runtimeKey: string): void;
148
- /**
149
- * 请求重新装配一次 Runtime,让业务侧刚写入的配置立刻生效。
150
- *
151
- * @remarks
152
- * 给「运行中改变自身可用能力」的 Tool 使用:先把变更写进持久层,再调用它,
153
- * 新的 Snapshot 会按同一条解析链重新装配。不要在 Plugin 的 loader 里调用,
154
- * 那会让装配递归。
155
- */
156
- reloadRuntime(): Promise<void>;
157
141
  /**
158
142
  * 通过当前 Agent 调用一个受 SDK 管理的子 Agent Tool。
159
143
  *
@@ -195,16 +179,59 @@ export interface RuntimeAgentConfigContext<
195
179
  ): Promise<string>;
196
180
  }
197
181
 
182
+ export interface RuntimeAgentPlanningContext<
183
+ Env extends Cloudflare.Env = Cloudflare.Env,
184
+ Command = never,
185
+ Change = never,
186
+ > extends RuntimeAgentConfigContext<Env> {
187
+ /** 仅可变 Definition 提供;Host Tool 用它复用生成类的更新与重载编排。 */
188
+ readonly updateConfig?: (
189
+ command: Command,
190
+ ) => Promise<RuntimeConfigUpdateResult<Change>>;
191
+ }
192
+
193
+ /**
194
+ * Config 读取的显式结果;runtimeKey 是生成类唯一的缓存键。
195
+ */
196
+ export interface ResolvedRuntimeConfig<Config> {
197
+ readonly runtimeKey: string;
198
+ readonly config: Config;
199
+ }
200
+
201
+ export interface RuntimeConfigUpdate<Change> {
202
+ readonly changed: boolean;
203
+ readonly change: Change;
204
+ }
205
+
206
+ export interface RuntimeAgentConfigDefinition<
207
+ Env extends Cloudflare.Env,
208
+ Config,
209
+ Command,
210
+ Change,
211
+ > {
212
+ read(
213
+ context: RuntimeAgentConfigContext<Env>,
214
+ requestedRuntimeKey?: string,
215
+ ): Promise<ResolvedRuntimeConfig<Config>>;
216
+ update?(
217
+ context: RuntimeAgentConfigContext<Env>,
218
+ command: Command,
219
+ ): Promise<RuntimeConfigUpdate<Change>>;
220
+ }
221
+
198
222
  /**
199
- * 告诉 Runtime 如何定义 Agent 类并创建配置。
223
+ * 告诉 Runtime 如何读取有效配置,并把它规划为一次扁平装配输入。
200
224
  *
201
225
  * @remarks
202
226
  * Worker 入口把它传给 `defineRuntimeAgent`。
203
227
  *
204
- * `createConfig` 只负责业务寻址和 Plugin 创建,原子装配由 Runtime 完成。
228
+ * Config 只负责业务寻址,Planner 把已读取配置投影为装配输入。
205
229
  */
206
- export interface RuntimeAgentDefinition<
207
- Env extends Cloudflare.Env = Cloudflare.Env,
230
+ interface RuntimeAgentDefinitionBase<
231
+ Env extends Cloudflare.Env,
232
+ Config,
233
+ Command,
234
+ Change,
208
235
  > {
209
236
  /**
210
237
  * 必须等于 Worker 导出名。
@@ -216,20 +243,47 @@ export interface RuntimeAgentDefinition<
216
243
  * Agents SDK 使用构造器名称解析 facet 回调。
217
244
  */
218
245
  className: string;
219
- /**
220
- * 为当前 Agent 实例创建一次完整的 Plugin 配置。
221
- *
222
- * @remarks
223
- * 生成类在首次执行需求和显式切换 Runtime key 时调用。
224
- *
225
- * 返回值尚未生效,只有 Runtime 完成校验和提交后才会替换当前 Snapshot。
226
- */
227
- createConfig(
228
- context: RuntimeAgentConfigContext<Env>,
229
- runtimeKey?: string,
230
- ): Promise<AgentConfig>;
246
+ planRuntime(
247
+ context: RuntimeAgentPlanningContext<Env, Command, Change>,
248
+ config: Config,
249
+ ): Promise<RuntimeAssemblyInput>;
231
250
  }
232
251
 
252
+ export interface ReadonlyRuntimeAgentDefinition<
253
+ Env extends Cloudflare.Env = Cloudflare.Env,
254
+ Config = unknown,
255
+ > extends RuntimeAgentDefinitionBase<Env, Config, never, never> {
256
+ readonly config: Omit<
257
+ RuntimeAgentConfigDefinition<Env, Config, never, never>,
258
+ "update"
259
+ > & { readonly update?: never };
260
+ }
261
+
262
+ export interface MutableRuntimeAgentDefinition<
263
+ Env extends Cloudflare.Env = Cloudflare.Env,
264
+ Config = unknown,
265
+ Command = unknown,
266
+ Change = unknown,
267
+ > extends RuntimeAgentDefinitionBase<Env, Config, Command, Change> {
268
+ readonly config: RuntimeAgentConfigDefinition<
269
+ Env,
270
+ Config,
271
+ Command,
272
+ Change
273
+ > & Required<
274
+ Pick<RuntimeAgentConfigDefinition<Env, Config, Command, Change>, "update">
275
+ >;
276
+ }
277
+
278
+ export type RuntimeAgentDefinition<
279
+ Env extends Cloudflare.Env = Cloudflare.Env,
280
+ Config = unknown,
281
+ Command = never,
282
+ Change = never,
283
+ > = [Command] extends [never]
284
+ ? ReadonlyRuntimeAgentDefinition<Env, Config>
285
+ : MutableRuntimeAgentDefinition<Env, Config, Command, Change>;
286
+
233
287
  /**
234
288
  * 这是生成 Runtime Agent 的稳定公开控制面。
235
289
  *
@@ -257,6 +311,14 @@ export interface RuntimeAgentControls {
257
311
  * 待确认:当前 `refreshContext` 只检查 Snapshot 已安装,没有重新读取 Context 或刷新缓存。
258
312
  */
259
313
  refreshMemoryContext(): Promise<void>;
314
+ /**
315
+ * Read the Runtime Snapshot currently installed on this Session facet.
316
+ *
317
+ * @remarks
318
+ * Debug panels and Host tooling call this to inspect skills, subagents,
319
+ * extensions, and degradations that were actually assembled.
320
+ */
321
+ getRuntimeAssembly(): Promise<RuntimeAssemblyView>;
260
322
  /**
261
323
  * 读取 canonical transcript 的浏览器投影。
262
324
  *
@@ -286,6 +348,16 @@ export interface RuntimeAgentControls {
286
348
  executionId: string,
287
349
  decision: ApprovalDecision,
288
350
  ): Promise<{ ok: boolean }>;
351
+ /**
352
+ * 把客户端投递的响应作为某次 Tool 调用的结果并续跑原 Turn。
353
+ *
354
+ * @remarks
355
+ * 前端经 WS RPC 调用,只带 `toolCallId`。查无、已结算或响应体不过校验都返回 `{ ok: false }`。
356
+ */
357
+ respondToolInteraction(
358
+ toolCallId: string,
359
+ response: unknown,
360
+ ): Promise<{ ok: boolean }>;
289
361
  /**
290
362
  * 登记一条受管临时 Agent 发起的审批请求。
291
363
  *
@@ -375,6 +447,15 @@ export interface RuntimeAgentControls {
375
447
  ): Promise<{ ok: boolean }>;
376
448
  }
377
449
 
450
+ export type RuntimeAgentConfigControls<Command, Change> =
451
+ [Command] extends [never]
452
+ ? Record<never, never>
453
+ : {
454
+ updateConfig(
455
+ command: Command,
456
+ ): Promise<RuntimeConfigUpdateResult<Change>>;
457
+ };
458
+
378
459
  /**
379
460
  * 表示 `defineRuntimeAgent` 生成类的实例。
380
461
  *
@@ -383,7 +464,10 @@ export interface RuntimeAgentControls {
383
464
  */
384
465
  export type RuntimeAgentInstance<
385
466
  Env extends Cloudflare.Env = Cloudflare.Env,
386
- > = Agent<Env, RuntimeState> & RuntimeAgentControls;
467
+ Command = never,
468
+ Change = never,
469
+ > = Agent<Env, RuntimeState> & RuntimeAgentControls &
470
+ RuntimeAgentConfigControls<Command, Change>;
387
471
 
388
472
  /**
389
473
  * 表示 `defineRuntimeAgent` 返回的 Durable Object 类构造器。
@@ -393,8 +477,13 @@ export type RuntimeAgentInstance<
393
477
  */
394
478
  export interface RuntimeAgentClass<
395
479
  Env extends Cloudflare.Env = Cloudflare.Env,
480
+ Command = never,
481
+ Change = never,
396
482
  > {
397
- new (ctx: DurableObjectState, env: Env): RuntimeAgentInstance<Env>;
483
+ new (
484
+ ctx: DurableObjectState,
485
+ env: Env,
486
+ ): RuntimeAgentInstance<Env, Command, Change>;
398
487
  }
399
488
 
400
489
  // #endregion
@@ -423,12 +512,35 @@ function assertClassName(className: string): void {
423
512
  *
424
513
  * 生成类把首次加载、并发去重、按 key 重载和失败重试收口到一个入口。
425
514
  */
515
+ export function defineRuntimeAgent<
516
+ Env extends Cloudflare.Env,
517
+ Config,
518
+ Command,
519
+ Change,
520
+ >(
521
+ definition: MutableRuntimeAgentDefinition<Env, Config, Command, Change>,
522
+ ): RuntimeAgentClass<Env, Command, Change>;
523
+ export function defineRuntimeAgent<
524
+ Env extends Cloudflare.Env = Cloudflare.Env,
525
+ Config = unknown,
526
+ >(
527
+ definition: ReadonlyRuntimeAgentDefinition<Env, Config>,
528
+ ): RuntimeAgentClass<Env>;
426
529
  export function defineRuntimeAgent<
427
530
  Env extends Cloudflare.Env = Cloudflare.Env,
531
+ Config = unknown,
532
+ Command = never,
533
+ Change = never,
428
534
  >(
429
- definition: RuntimeAgentDefinition<Env>,
430
- ): RuntimeAgentClass<Env> {
535
+ definition:
536
+ | MutableRuntimeAgentDefinition<Env, Config, Command, Change>
537
+ | ReadonlyRuntimeAgentDefinition<Env, Config>,
538
+ ): RuntimeAgentClass<Env, Command, Change> {
431
539
  assertClassName(definition.className);
540
+ const planRuntime = definition.planRuntime as (
541
+ context: RuntimeAgentPlanningContext<Env, Command, Change>,
542
+ config: Config,
543
+ ) => Promise<RuntimeAssemblyInput>;
432
544
 
433
545
  const GeneratedRuntimeAgent = {
434
546
  [definition.className]: class extends AgentRuntimeKernel<Env> {
@@ -438,7 +550,7 @@ export function defineRuntimeAgent<
438
550
 
439
551
  // 作用:在 Kernel 真正消费 RuntimeSnapshot 前确保配置已完整提交。
440
552
  // 调用:Submission 准入、恢复、审批续跑与 Runtime Workspace 路径调用。
441
- // 原因:Session 本地读取不应支付 Config、Plugin、MCP 和 Pi 装配成本。
553
+ // 原因:Session 本地读取不应支付 Config、Assembly、MCP 和 Pi 装配成本。
442
554
  protected ensureRuntimeReady(): Promise<void> {
443
555
  return this.ensureConfig();
444
556
  }
@@ -453,6 +565,23 @@ export function defineRuntimeAgent<
453
565
  await this.ensureConfig(runtimeKey, options?.force === true);
454
566
  }
455
567
 
568
+ async updateConfig(
569
+ command: Command,
570
+ ): Promise<RuntimeConfigUpdateResult<Change>> {
571
+ const update = definition.config.update;
572
+ if (!update) throw new Error("Runtime Agent Config is read-only");
573
+ const result = await update(this.createDefinitionContext(), command);
574
+ if (!result.changed) {
575
+ return { change: result.change, runtime: "unchanged" };
576
+ }
577
+ try {
578
+ await this.ensureConfig(undefined, true);
579
+ return { change: result.change, runtime: "reloaded" };
580
+ } catch {
581
+ return { change: result.change, runtime: "reload-failed" };
582
+ }
583
+ }
584
+
456
585
  // 作用:确保 Runtime 可用后请求 Kernel 刷新 Session Context。
457
586
  // 调用:Inbox 等 RPC 调用方在外部 Context 变更后调用。
458
587
  // 待确认:当前 Kernel 的 `refreshContext` 只检查 Snapshot,并未执行实际刷新。
@@ -461,6 +590,11 @@ export function defineRuntimeAgent<
461
590
  await this.refreshContext();
462
591
  }
463
592
 
593
+ async getRuntimeAssembly(): Promise<RuntimeAssemblyView> {
594
+ await this.ensureRuntimeReady();
595
+ return this.readRuntimeAssembly();
596
+ }
597
+
464
598
  dispatchMessage(
465
599
  message: UIMessage,
466
600
  delivery: MessageDelivery,
@@ -481,6 +615,13 @@ export function defineRuntimeAgent<
481
615
  return super.cancelSubmissionById(submissionId, reason);
482
616
  }
483
617
 
618
+ respondToolInteraction(
619
+ toolCallId: string,
620
+ response: unknown,
621
+ ): Promise<{ ok: boolean }> {
622
+ return super.respondToolInteraction(toolCallId, response);
623
+ }
624
+
484
625
  stopTurn(
485
626
  requestId?: string,
486
627
  reason?: string,
@@ -535,83 +676,28 @@ export function defineRuntimeAgent<
535
676
 
536
677
  // 先创建局部 Promise,再赋给 `loading`,失败时也能在 finally 中放开重试。
537
678
  const pending = (async () => {
538
- this.beginRuntimeLoad();
539
- let resolvedRuntimeKey: string | undefined;
540
- const agent = this;
541
- const config = await definition.createConfig(
542
- {
543
- ctx: this.ctx,
544
- env: this.env,
545
- // 作用:在创建配置时读取当前 Agent 实例名。
546
- // 调用:应用的 `createConfig` 在解析 Session 身份时读取。
547
- // 原因:使用实时 getter,不在上下文创建时复制 SDK 身份值。
548
- get name() {
549
- return agent.name;
550
- },
551
- // 作用:在创建配置时读取根到直接父节点的路径。
552
- // 调用:应用的 `createConfig` 在解析 facet 归属时读取。
553
- // 原因:路径由 Agents SDK 维护,Runtime 只转发而不另存副本。
554
- get parentPath() {
555
- return agent.parentPath;
556
- },
557
- // 作用:记住本次配置解析出的 Runtime key。
558
- // 调用:应用的 `createConfig` 在完成业务寻址后调用。
559
- // 原因:只接受一个非空结果,防止同一次装配在两个业务身份之间摇摆。
560
- setResolvedRuntimeKey(runtimeKey) {
561
- const normalized = runtimeKey.trim();
562
- if (!normalized) {
563
- throw new Error(
564
- "Resolved Runtime key must not be empty",
565
- );
566
- }
567
- if (
568
- resolvedRuntimeKey &&
569
- resolvedRuntimeKey !== normalized
570
- ) {
571
- throw new Error(
572
- "Resolved Runtime key cannot change during Config creation",
573
- );
574
- }
575
- resolvedRuntimeKey = normalized;
576
- },
577
- // 作用:强制按当前业务数据重新装配 Runtime。
578
- // 调用:会修改自身配置的 Tool 在持久化成功后调用。
579
- // 原因:配置内容可能已变而 key 不变,因此不能复用已加载标记。
580
- reloadRuntime: () => agent.ensureConfig(undefined, true),
581
- // 作用:通过当前 Agent 启动一个受 SDK 管理的子运行。
582
- // 调用:subagent 端口在委派 Agent Tool 时调用。
583
- // 原因:保留 Agents SDK 的运行登记、事件、取消和清理边界。
584
- runAgentTool: async (agentClass, options) =>
585
- agent.runAgentTool(
586
- agentClass,
587
- options as never,
588
- ) as Promise<RuntimeAgentToolResult>,
589
- // 作用:清理符合明确策略的已保留子运行。
590
- // 调用:subagent 端口在执行运行保留期回收时调用。
591
- // 原因:清理仍由 Agents SDK 执行,Runtime 不越过 SDK 直接修改子运行存储。
592
- clearAgentToolRuns: (options) =>
593
- agent.clearAgentToolRuns(options),
594
- // 作用:返回当前已装配 User Agent 的执行档位。
595
- // 调用:Plugin 在装配临时 Agent 审批桥时调用。
596
- // 原因:只读 Profile,不再维护 Session 覆盖状态。
597
- executionLevel: () => agent.executionLevel(),
598
- // 作用:在当前 Session 中执行一次性临时 Agent。
599
- // 调用:temporary-agent 端口在 Tool 需要调用内委派时调用。
600
- // 原因:交给 Kernel 统一管理重名、取消和审批,不创建持久 facet。
601
- runTemporaryAgent: (request, runContext, execute) =>
602
- agent.runTemporaryAgent(request, runContext, execute),
603
- },
679
+ this.runtimeLoad.begin();
680
+ const context = this.createDefinitionContext();
681
+ const resolved = await definition.config.read(
682
+ context,
604
683
  requestedRuntimeKey,
605
684
  );
685
+ const resolvedRuntimeKey = resolved.runtimeKey.trim();
686
+ if (!resolvedRuntimeKey) {
687
+ throw new Error("Resolved Runtime key must not be empty");
688
+ }
689
+ const input = await planRuntime(context, resolved.config);
606
690
 
607
691
  // 只有原子提交成功后才更新已加载标记和 key。
608
- await this.initConfig(config);
692
+ await this.initConfig(input);
609
693
  this.hasLoadedRuntime = true;
610
- this.loadedRuntimeKey =
611
- resolvedRuntimeKey ?? requestedRuntimeKey;
612
- this.completeRuntimeLoad();
694
+ this.loadedRuntimeKey = resolvedRuntimeKey;
695
+ this.runtimeLoad.complete();
696
+ // 提交之前 turnEvents 绑定还不存在,此前每一次广播都到不了 Host。
697
+ // Host 的会话列表把装配后的第一次投影当作「facet 已就绪」的信号。
698
+ await this.broadcastApprovals();
613
699
  })().catch((error) => {
614
- this.failRuntimeLoad();
700
+ this.runtimeLoad.fail();
615
701
  throw error;
616
702
  });
617
703
 
@@ -625,6 +711,40 @@ export function defineRuntimeAgent<
625
711
  this.loading = tracked;
626
712
  return tracked;
627
713
  }
714
+
715
+ private createDefinitionContext(): RuntimeAgentPlanningContext<
716
+ Env,
717
+ Command,
718
+ Change
719
+ > {
720
+ const agent = this;
721
+ return {
722
+ ctx: this.ctx,
723
+ env: this.env,
724
+ get name() {
725
+ return agent.name;
726
+ },
727
+ get parentPath() {
728
+ return agent.parentPath;
729
+ },
730
+ ...(definition.config.update
731
+ ? {
732
+ updateConfig: (command: Command) =>
733
+ agent.updateConfig(command),
734
+ }
735
+ : {}),
736
+ runAgentTool: async (agentClass, options) =>
737
+ agent.runAgentTool(
738
+ agentClass,
739
+ options as never,
740
+ ) as Promise<RuntimeAgentToolResult>,
741
+ clearAgentToolRuns: (options) =>
742
+ agent.clearAgentToolRuns(options),
743
+ executionLevel: () => agent.executionLevel(),
744
+ runTemporaryAgent: (request, runContext, execute) =>
745
+ agent.runTemporaryAgent(request, runContext, execute),
746
+ };
747
+ }
628
748
  },
629
749
  }[definition.className];
630
750
 
@@ -637,6 +757,7 @@ export function defineRuntimeAgent<
637
757
  // Decorator syntax renames this computed class during Worker bundling.
638
758
  // Apply the same decorator function after class-name inference instead.
639
759
  const callableContext = {} as ClassMethodDecoratorContext;
760
+ callable()(GeneratedRuntimeAgent.prototype.reloadRuntime, callableContext);
640
761
  callable()(GeneratedRuntimeAgent.prototype.dispatchMessage, callableContext);
641
762
  callable()(
642
763
  GeneratedRuntimeAgent.prototype.steerQueuedSubmission,
@@ -647,8 +768,20 @@ export function defineRuntimeAgent<
647
768
  callableContext,
648
769
  );
649
770
  callable()(GeneratedRuntimeAgent.prototype.stopTurn, callableContext);
771
+ callable()(
772
+ GeneratedRuntimeAgent.prototype.respondToolInteraction,
773
+ callableContext,
774
+ );
775
+ callable()(GeneratedRuntimeAgent.prototype.getRuntimeAssembly, callableContext);
776
+ if (definition.config.update) {
777
+ callable()(GeneratedRuntimeAgent.prototype.updateConfig, callableContext);
778
+ } else {
779
+ delete (GeneratedRuntimeAgent.prototype as {
780
+ updateConfig?: unknown;
781
+ }).updateConfig;
782
+ }
650
783
 
651
- return GeneratedRuntimeAgent as RuntimeAgentClass<Env>;
784
+ return GeneratedRuntimeAgent as RuntimeAgentClass<Env, Command, Change>;
652
785
  }
653
786
 
654
787
  // #endregion