@peer-agent/runtime-sdk 0.1.0-rc.10 → 0.1.0-rc.12

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.
@@ -13,6 +13,8 @@ export interface RuntimePipelineRunInput<TInput = unknown> {
13
13
  /** Scheduling allowance; unlike maxToolCalls this can end at a batch boundary. */
14
14
  readonly sliceToolCalls?: number;
15
15
  readonly yieldAtTurnLimit?: boolean;
16
+ /** Host scheduling request, checked at durable batch boundaries. */
17
+ readonly shouldYield?: () => boolean;
16
18
  readonly maxToolBatchCalls?: number;
17
19
  }
18
20
  export interface RuntimePipelineToolCall<TPayload = unknown> {
@@ -54,6 +56,8 @@ export interface RuntimePipelineModelAdapter<TInput = unknown, TState = unknown,
54
56
  initialize(input: RuntimePipelineRunInput<TInput>, context: RuntimePipelineTurnContext<TInput>): TState | Promise<TState>;
55
57
  runTurn(state: TState, context: RuntimePipelineTurnContext<TInput>): RuntimePipelineTurnResult<TState, TCall, TOutput> | Promise<RuntimePipelineTurnResult<TState, TCall, TOutput>>;
56
58
  applyToolResults(state: TState, executions: readonly RuntimePipelineToolExecution<TCall, TToolResult>[], context: RuntimePipelineTurnContext<TInput>): TState | Promise<TState>;
59
+ /** Critical durable commit after paired tool results; failure ends execution. */
60
+ checkpoint?(state: TState, executions: readonly RuntimePipelineToolExecution<TCall, TToolResult>[], context: RuntimePipelineTurnContext<TInput>): void | Promise<void>;
57
61
  onCompleted?(state: TState, output: TOutput | undefined, context: RuntimePipelineTurnContext<TInput>): void | Promise<void>;
58
62
  onStopped?(state: TState, executions: readonly RuntimePipelineToolExecution<TCall, TToolResult>[], context: RuntimePipelineTurnContext<TInput>): void | Promise<void>;
59
63
  onCancelled?(state: TState | undefined, context: RuntimePipelineTurnContext<TInput>): void | Promise<void>;
package/dist/pipeline.js CHANGED
@@ -81,12 +81,17 @@ export function createRuntimePipeline(options) {
81
81
  state = await options.model.initialize(input, turnContext(0));
82
82
  for (let turn = 0; turn < maxTurns; turn += 1) {
83
83
  throwIfAborted(signal);
84
+ if (input.shouldYield?.() === true)
85
+ return await yieldRun();
84
86
  turns = turn + 1;
85
87
  const currentContext = turnContext(turn);
86
88
  const outcome = await options.model.runTurn(state, currentContext);
87
89
  state = outcome.state;
88
- if (outcome.kind === 'continue')
90
+ if (outcome.kind === 'continue') {
91
+ if (input.shouldYield?.() === true)
92
+ return await yieldRun();
89
93
  continue;
94
+ }
90
95
  if (outcome.kind === 'completed') {
91
96
  await options.model.onCompleted?.(state, outcome.output, currentContext);
92
97
  if (input.streamId) {
@@ -114,6 +119,7 @@ export function createRuntimePipeline(options) {
114
119
  if (options.tools.notExecuted) {
115
120
  const denied = await Promise.all(outcome.calls.map(call => options.tools.notExecuted(call, 'max_tool_calls_exceeded')));
116
121
  state = await options.model.applyToolResults(state, denied, currentContext);
122
+ await options.model.checkpoint?.(state, denied, currentContext);
117
123
  }
118
124
  return await exhaust('max_tool_calls_exceeded');
119
125
  }
@@ -162,6 +168,7 @@ export function createRuntimePipeline(options) {
162
168
  }
163
169
  state = await options.model.applyToolResults(state, executions, currentContext);
164
170
  throwIfAborted(signal);
171
+ await options.model.checkpoint?.(state, executions, currentContext);
165
172
  try {
166
173
  await options.lifecycle?.toolResultsApplied?.(state, executions, currentContext);
167
174
  }
@@ -188,7 +195,7 @@ export function createRuntimePipeline(options) {
188
195
  reason: terminalExecution.terminalReason || 'tool_requested_stop',
189
196
  };
190
197
  }
191
- if (toolCalls >= sliceToolCalls)
198
+ if (toolCalls >= sliceToolCalls || input.shouldYield?.() === true)
192
199
  return await yieldRun();
193
200
  }
194
201
  return input.yieldAtTurnLimit ? await yieldRun() : await exhaust('max_turns_exceeded');
package/dist/runtime.js CHANGED
@@ -130,9 +130,15 @@ export function createRuntimeSdk(options) {
130
130
  decision: mostRestrictiveHookDecision(postRecords),
131
131
  records: postRecords,
132
132
  });
133
- const result = options.host.appendHookEvidence
133
+ let result = options.host.appendHookEvidence
134
134
  ? options.host.appendHookEvidence(execution.result, [...preRecords, ...postRecords], preDecision)
135
135
  : execution.result;
136
+ // Host-only sideband must survive a Hook Evidence clone without becoming
137
+ // enumerable event/storage data. Model-supplied fields cannot create it.
138
+ const sideband = Object.getOwnPropertyDescriptor(execution.result, 'modelContext');
139
+ if (result !== execution.result && sideband && sideband.enumerable === false) {
140
+ result = Object.defineProperty({ ...result }, 'modelContext', sideband);
141
+ }
136
142
  const resolvedExecution = { ...execution, result };
137
143
  emit({ type: 'tool.completed', ...eventBase, decision: preDecision, result });
138
144
  return resolvedExecution;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peer-agent/runtime-sdk",
3
- "version": "0.1.0-rc.10",
3
+ "version": "0.1.0-rc.12",
4
4
  "description": "Public host-neutral orchestration SDK for Peer Agent runtimes.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,8 +34,8 @@
34
34
  "LICENSE"
35
35
  ],
36
36
  "dependencies": {
37
- "@peer-agent/protocol": "^0.1.0-rc.10",
38
- "@peer-agent/runtime-core": "^0.1.0-rc.10"
37
+ "@peer-agent/protocol": "^0.1.0-rc.12",
38
+ "@peer-agent/runtime-core": "^0.1.0-rc.12"
39
39
  },
40
40
  "devDependencies": {
41
41
  "typescript": "^5.9.3"