@springbrand/agent-runtime 0.3.0-alpha.5 → 0.3.0-alpha.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@springbrand/agent-runtime",
3
- "version": "0.3.0-alpha.5",
3
+ "version": "0.3.0-alpha.6",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src",
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  createModels,
3
+ lazyStream,
3
4
  type MutableModels,
4
5
  } from "@earendil-works/pi-ai";
5
6
  import {
@@ -23,6 +24,7 @@ import {
23
24
  type PreparePiRuntimeOptions,
24
25
  type PreparedPiRuntime,
25
26
  } from "./pinning";
27
+ import { refreshModelGuidance } from "./model-guidance";
26
28
 
27
29
  /**
28
30
  * Runtime 通过这个配置入口准备、固定并读取 Harness 的模型、提示和工具输入。
@@ -109,11 +111,17 @@ export class HarnessRuntimeConfig {
109
111
  : target.getModel(provider, modelId);
110
112
  }
111
113
  if (property === "streamSimple") {
112
- return withProviderBoundary(
114
+ const stream = withProviderBoundary(
113
115
  target.streamSimple.bind(target),
114
116
  request.modelSessionId,
115
117
  request.onGeneration,
116
118
  );
119
+ return ((model, context, options) => lazyStream(model, async () => {
120
+ options?.signal?.throwIfAborted();
121
+ const refreshed = await refreshModelGuidance(state.candidates, context);
122
+ options?.signal?.throwIfAborted();
123
+ return stream(model, refreshed, options);
124
+ })) satisfies typeof stream;
117
125
  }
118
126
  if (property === "completeSimple") {
119
127
  return target.completeSimple.bind(models);
@@ -0,0 +1,21 @@
1
+ import type { Context } from "@earendil-works/pi-ai";
2
+ import type { PiToolCandidate } from "../tools/compiler";
3
+
4
+ /** Only descriptions and guidance may vary inside a pinned Submission. */
5
+ export async function refreshModelGuidance(
6
+ candidates: readonly PiToolCandidate[],
7
+ context: Context,
8
+ ): Promise<Context> {
9
+ const guidance = new Map(await Promise.all(candidates
10
+ .filter(candidate => candidate.modelGuidance && context.tools?.some(tool => tool.name === candidate.tool.name))
11
+ .map(async candidate => [candidate.tool.name, await candidate.modelGuidance!()] as const)));
12
+ if (!guidance.size) return context;
13
+ return {
14
+ ...context,
15
+ systemPrompt: [context.systemPrompt, ...new Set([...guidance.values()].map(item => item.systemPrompt).filter(Boolean))].filter(Boolean).join("\n\n"),
16
+ tools: context.tools?.map(tool => {
17
+ const prompt = guidance.get(tool.name);
18
+ return prompt ? { ...tool, description: prompt.description } : tool;
19
+ }),
20
+ };
21
+ }
@@ -175,12 +175,13 @@ export class SessionHarness<TContext extends object | undefined> {
175
175
  }
176
176
 
177
177
  private async operationEntries(context: Context): Promise<Entry[]> {
178
+ // Stop boundaries follow traversal order; walk back from the current tip.
178
179
  const entries = await this.lane.findEntries({
179
180
  ...(this.operationStartTip === null ? {} : { stopAtId: this.operationStartTip }),
180
181
  type: "message",
181
- order: "oldestFirst",
182
+ order: "newestFirst",
182
183
  }, context);
183
- return entries.filter(({ id }) => id !== this.operationStartTip);
184
+ return entries.filter(({ id }) => id !== this.operationStartTip).reverse();
184
185
  }
185
186
 
186
187
  private async restoreToolGovernance(context: Context): Promise<void> {
@@ -62,6 +62,8 @@ export interface PiToolConfirmationSpec {
62
62
  }
63
63
 
64
64
  export interface PiToolCandidate {
65
+ /** Presentation only, resolved at the model boundary; never changes approved execution inputs. */
66
+ readonly modelGuidance?: () => Promise<{ description: string; systemPrompt?: string }>;
65
67
  readonly confirmation?: PiToolConfirmationSpec;
66
68
  readonly owner: string;
67
69
  readonly tool: AgentTool<any, any>;