@springbrand/agent-runtime 0.3.0-alpha.21 → 0.3.0-alpha.23
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/dist/canonical-context.d.ts +661 -0
- package/dist/canonical-context.js +122 -0
- package/package.json +16 -8
- package/scripts/build-canonical-context.mjs +38 -0
- package/src/adapter/cloudflare/agent.ts +2 -2
- package/src/adapter/cloudflare/define-agent.ts +4 -6
- package/src/config/public-contracts.ts +1 -0
- package/src/conversation/canonical-context.ts +144 -0
- package/src/conversation/history.ts +41 -45
- package/src/conversation/message/contract.ts +2 -11
- package/src/conversation/message/conversion.ts +3 -5
- package/src/conversation/message/user-sidecar.ts +7 -0
- package/src/conversation/native-extensions.ts +13 -0
- package/src/index.ts +1 -1
- package/src/models/openrouter/protocol-response.ts +1 -11
- package/src/submission/lifecycle.ts +10 -1
- package/src/submission/steer-delivery.ts +4 -2
- package/src/telemetry/coordinator.ts +13 -3
package/src/index.ts
CHANGED
|
@@ -51,6 +51,7 @@ export type { RuntimeAssemblyView } from "./config/assembly-view";
|
|
|
51
51
|
export * from "./adapter/cloudflare/define-agent";
|
|
52
52
|
export type { ToolInteractionResponsePreparation } from "./adapter/cloudflare/agent";
|
|
53
53
|
export * from "./conversation/state";
|
|
54
|
+
export type { ModelProtocolResponse } from "./conversation/canonical-context";
|
|
54
55
|
export * from "./tools/workspace/versioning";
|
|
55
56
|
export * from "./lib/execution-level";
|
|
56
57
|
export type {
|
|
@@ -150,6 +151,5 @@ export {
|
|
|
150
151
|
} from "./model-protocol/openrouter";
|
|
151
152
|
export type { OpenRouterRequestOptions } from "./model-protocol/openrouter";
|
|
152
153
|
export type {
|
|
153
|
-
ModelProtocolResponse,
|
|
154
154
|
OpenRouterProtocol,
|
|
155
155
|
} from "./models/openrouter/protocol-response";
|
|
@@ -9,20 +9,10 @@ import {
|
|
|
9
9
|
} from "@earendil-works/pi-ai";
|
|
10
10
|
import type { RuntimeModelProtocol } from "../../config/ports";
|
|
11
11
|
import { overlayProviderStreams } from "../provider-streams";
|
|
12
|
+
import type { ModelProtocolResponse } from "../../conversation/canonical-context";
|
|
12
13
|
|
|
13
14
|
export type OpenRouterProtocol = Extract<RuntimeModelProtocol, `openrouter-${string}`>;
|
|
14
15
|
|
|
15
|
-
/** Native output required by OpenRouter's stateless continuation. */
|
|
16
|
-
export interface ModelProtocolResponse {
|
|
17
|
-
readonly output: readonly Readonly<Record<string, unknown>>[];
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
declare module "@earendil-works/pi-ai" {
|
|
21
|
-
interface AssistantMessage {
|
|
22
|
-
protocolResponse?: ModelProtocolResponse;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
16
|
function record(value: unknown): Record<string, unknown> | undefined {
|
|
27
17
|
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
28
18
|
? value as Record<string, unknown>
|
|
@@ -597,7 +597,16 @@ export class SubmissionLifecycle<
|
|
|
597
597
|
activate(submission: TSubmission, active: TActive): () => void {
|
|
598
598
|
this.activeBySubmission.set(submission.submissionId, active);
|
|
599
599
|
const latest = this.options.store.find(submission.submissionId);
|
|
600
|
-
if (latest?.abortReason)
|
|
600
|
+
if (latest?.abortReason) {
|
|
601
|
+
// The cancel is already committed; this abort only stops the executor and may lose to it settling.
|
|
602
|
+
void (async () => this.options.abortActive(active))().catch((error: unknown) => {
|
|
603
|
+
console.warn(
|
|
604
|
+
"[runtime-early-abort:failed]",
|
|
605
|
+
submission.submissionId,
|
|
606
|
+
error instanceof Error ? error.message : String(error),
|
|
607
|
+
);
|
|
608
|
+
});
|
|
609
|
+
}
|
|
601
610
|
return () => {
|
|
602
611
|
if (this.activeBySubmission.get(submission.submissionId) === active) {
|
|
603
612
|
this.activeBySubmission.delete(submission.submissionId);
|
|
@@ -6,6 +6,7 @@ import type { SubmitMessageOptions } from "./admission";
|
|
|
6
6
|
import type { SubmissionHandle } from "./lifecycle";
|
|
7
7
|
import { submissionAbortReason, type StoredSubmission, type SubmissionRepository } from "./repository";
|
|
8
8
|
import type { PiCanonicalUserInput } from "../conversation/message/contract";
|
|
9
|
+
import { captureUIUserSidecar } from "../conversation/message/conversion";
|
|
9
10
|
import type { MessageUiRepository } from "../conversation/repository";
|
|
10
11
|
import type { PendingSteer, SteerRepository } from "./steer-repository";
|
|
11
12
|
|
|
@@ -70,6 +71,7 @@ export async function finishPendingSteers(options: {
|
|
|
70
71
|
const userMessage = steer.uiMessageJson
|
|
71
72
|
? JSON.parse(steer.uiMessageJson) as UIMessage & { role: "user" }
|
|
72
73
|
: undefined;
|
|
74
|
+
const userSidecar = userMessage ? { id: userMessage.id, ...captureUIUserSidecar(userMessage) } : undefined;
|
|
73
75
|
const requestId = `${options.submissionId}:steer:${steer.steerId}`;
|
|
74
76
|
if (options.aborted) {
|
|
75
77
|
const owner = options.transaction(() => {
|
|
@@ -81,7 +83,7 @@ export async function finishPendingSteers(options: {
|
|
|
81
83
|
}
|
|
82
84
|
return handoff?.status === "aborted" || handoff?.status === "skipped" ? handoff.submissionId : options.submissionId;
|
|
83
85
|
});
|
|
84
|
-
await options.conversation.append(steer.messageId, message, { submissionId: owner, ...(
|
|
86
|
+
await options.conversation.append(steer.messageId, message, { submissionId: owner, ...(userSidecar ? { userSidecar } : {}) });
|
|
85
87
|
options.transaction(() => {
|
|
86
88
|
if (steer.harnessEntryId) options.messageUi.delete(steer.harnessEntryId);
|
|
87
89
|
options.steers.deleteByMessageId(steer.messageId);
|
|
@@ -117,7 +119,7 @@ export async function finishPendingSteers(options: {
|
|
|
117
119
|
}
|
|
118
120
|
return current;
|
|
119
121
|
});
|
|
120
|
-
if (handoff.status === "skipped" || handoff.status === "aborted") await options.conversation.append(steer.messageId, message, { submissionId: handoff.submissionId, ...(
|
|
122
|
+
if (handoff.status === "skipped" || handoff.status === "aborted") await options.conversation.append(steer.messageId, message, { submissionId: handoff.submissionId, ...(userSidecar ? { userSidecar } : {}) });
|
|
121
123
|
options.transaction(() => options.steers.deleteByMessageId(steer.messageId));
|
|
122
124
|
}
|
|
123
125
|
}
|
|
@@ -53,6 +53,7 @@ export class RuntimeTelemetryCoordinator {
|
|
|
53
53
|
private delivery?: TelemetryDelivery;
|
|
54
54
|
private configured = false;
|
|
55
55
|
private draining?: Promise<void>;
|
|
56
|
+
private drainRequested = false;
|
|
56
57
|
private readonly coldBuffer: BufferedTelemetryFact[] = [];
|
|
57
58
|
|
|
58
59
|
constructor(
|
|
@@ -182,11 +183,19 @@ export class RuntimeTelemetryCoordinator {
|
|
|
182
183
|
}
|
|
183
184
|
|
|
184
185
|
private queueDrain(): void {
|
|
185
|
-
if (!this.delivery
|
|
186
|
+
if (!this.delivery) return;
|
|
187
|
+
// A drain reads its batch before publishing; facts written after that wait for the next one.
|
|
188
|
+
if (this.draining) {
|
|
189
|
+
this.drainRequested = true;
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
this.drainRequested = false;
|
|
186
193
|
const pending = this.drain();
|
|
187
194
|
let tracked: Promise<void>;
|
|
188
195
|
tracked = pending.finally(() => {
|
|
189
|
-
if (this.draining
|
|
196
|
+
if (this.draining !== tracked) return;
|
|
197
|
+
this.draining = undefined;
|
|
198
|
+
if (this.drainRequested) this.queueDrain();
|
|
190
199
|
});
|
|
191
200
|
this.draining = tracked;
|
|
192
201
|
this.options.waitUntil(tracked);
|
|
@@ -196,7 +205,8 @@ export class RuntimeTelemetryCoordinator {
|
|
|
196
205
|
if (!this.delivery) return;
|
|
197
206
|
try {
|
|
198
207
|
const result = await this.delivery.drain();
|
|
199
|
-
if
|
|
208
|
+
// A queued follow-up drain schedules its own retry if it still leaves work.
|
|
209
|
+
if (!result.hasPending || this.drainRequested) return;
|
|
200
210
|
await this.options.scheduleRetry(
|
|
201
211
|
TELEMETRY_RETRY_DELAY_MS / 1_000,
|
|
202
212
|
idempotentRetry,
|