@nylorun/runtime 0.1.1-beta → 0.1.2-beta
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/CHANGELOG.md +7 -0
- package/README.md +9 -1
- package/dist/contracts.d.ts +14 -1
- package/dist/model/pi-model.js +64 -4
- package/dist/server/ag-ui.js +13 -4
- package/dist/server/host.js +23 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2-beta
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d27242c: Show stopped guardrail and failed model requests as errors in Studio. Runtime now emits a terminal AG-UI error instead of marking failed requests successful, and Studio displays the reported message. The guardrails example also checks text content parts sent by Studio, including mixed media input, before invoking the model.
|
|
8
|
+
- d27242c: Preserve opaque provider continuation metadata through assistant conversation history. Gemini tool calls now retain thought signatures when sending tool results back to the model, including signed empty text and reasoning blocks. Only the originating provider and model receive their signatures.
|
|
9
|
+
|
|
3
10
|
## 0.1.1-beta
|
|
4
11
|
|
|
5
12
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -35,10 +35,18 @@ The creator runs `nylorun configure` after installation and before development u
|
|
|
35
35
|
|
|
36
36
|
## Portable contracts
|
|
37
37
|
|
|
38
|
-
`RuntimeAgent` supplies `id`, `name`, a matching `manifest`, and `run({ id })`. Its session supplies `input`, `stream`, `observe`, and `stop`. An input handle's `completed` promise settles after that input's execution or pause, with ordered lifecycle events and a status. Runtime uses completion events for terminal output and
|
|
38
|
+
`RuntimeAgent` supplies `id`, `name`, a matching `manifest`, and `run({ id })`. Its session supplies `input`, `stream`, `observe`, and `stop`. An input handle's `completed` promise settles after that input's execution or pause, with ordered lifecycle events and a status. Runtime uses completion events for terminal output, interactions, and failures, and observation events for optional diagnostics. Error or tripwire completions and rejected, cancelled, or stopped statuses produce a terminal AG-UI error; repeated diagnostic and completion reports do not duplicate the failure. Engines need not emit model/tool diagnostics or middleware metadata. Runtime records input before execution and flushes persistence before returning an HTTP reply. `stop()` releases session resources; optional agent `close()` runs after session shutdown. Shutdown is idempotent.
|
|
39
39
|
|
|
40
40
|
`RuntimeModelAdapter` accepts ordered prompt items, tools, model controls, opaque media references, and an abort signal, returning portable text/reasoning/tool-call outputs. It has no engine import. Integration tests in the creator check assignment in both directions with Harness without casts. Runtime's own tests use an independent engine.
|
|
41
41
|
|
|
42
42
|
Discovery and manifest documents use protocol version 2. The agent's manifest is under `manifest`; no engine-branded envelope is required. Existing agent-scoped HTTP endpoint paths remain unchanged. Studio also understands legacy version-1 `harness.manifest` documents.
|
|
43
43
|
|
|
44
44
|
Repository development: [contributing](../CONTRIBUTING.md). Package publication: [releasing](../RELEASING.md).
|
|
45
|
+
|
|
46
|
+
Provider continuation state (for example Gemini thought signatures) travels as
|
|
47
|
+
opaque JSON `providerMetadata` on assistant text, reasoning, and tool-call
|
|
48
|
+
blocks. Engines integrating `piModel()` must preserve it on the corresponding
|
|
49
|
+
assistant prompt parts, including empty signed blocks and their order. Runtime
|
|
50
|
+
only reuses signatures with the originating provider and model. Middleware that
|
|
51
|
+
rewrites a signed block must remove its metadata; signatures describe the
|
|
52
|
+
original provider output. Unsigned reasoning remains diagnostic output.
|
package/dist/contracts.d.ts
CHANGED
|
@@ -40,6 +40,10 @@ export interface RuntimeEvent {
|
|
|
40
40
|
readonly event?: RuntimeInputEvent;
|
|
41
41
|
readonly interaction?: unknown;
|
|
42
42
|
readonly attributes?: unknown;
|
|
43
|
+
readonly tripwire?: {
|
|
44
|
+
readonly code: string;
|
|
45
|
+
readonly message?: string;
|
|
46
|
+
};
|
|
43
47
|
}
|
|
44
48
|
export interface RuntimeCompletion {
|
|
45
49
|
readonly status: "completed" | "waiting" | "rejected" | "cancelled" | "stopped";
|
|
@@ -70,8 +74,15 @@ export interface RuntimeAgent {
|
|
|
70
74
|
}): RuntimeSession;
|
|
71
75
|
close?(): Promise<void>;
|
|
72
76
|
}
|
|
73
|
-
export type PromptContentPart = UserContentPart
|
|
77
|
+
export type PromptContentPart = Exclude<UserContentPart, {
|
|
78
|
+
readonly type: "text";
|
|
79
|
+
}> | {
|
|
80
|
+
readonly type: "text" | "reasoning";
|
|
81
|
+
readonly text: string;
|
|
82
|
+
readonly providerMetadata?: JsonObject;
|
|
83
|
+
} | {
|
|
74
84
|
readonly type: "tool-call";
|
|
85
|
+
readonly providerMetadata?: JsonObject;
|
|
75
86
|
readonly id: string;
|
|
76
87
|
readonly name: string;
|
|
77
88
|
readonly args: JsonObject;
|
|
@@ -117,11 +128,13 @@ export interface RuntimeModelCandidate {
|
|
|
117
128
|
readonly output: readonly ({
|
|
118
129
|
readonly type: "text" | "reasoning";
|
|
119
130
|
readonly text: string;
|
|
131
|
+
readonly providerMetadata?: JsonObject;
|
|
120
132
|
} | {
|
|
121
133
|
readonly type: "json";
|
|
122
134
|
readonly value: JsonValue;
|
|
123
135
|
} | {
|
|
124
136
|
readonly type: "tool-call";
|
|
137
|
+
readonly providerMetadata?: JsonObject;
|
|
125
138
|
readonly id: string;
|
|
126
139
|
readonly name: string;
|
|
127
140
|
readonly args: JsonObject;
|
package/dist/model/pi-model.js
CHANGED
|
@@ -21,6 +21,31 @@ export function piModel(options = {}) {
|
|
|
21
21
|
const selected = registry.getModel(selection.provider, selection.model);
|
|
22
22
|
if (!selected)
|
|
23
23
|
throw new Error("Unknown model. Run nylorun configure.");
|
|
24
|
+
const signatureFor = (part) => {
|
|
25
|
+
const metadata = "providerMetadata" in part ? part.providerMetadata?.pi : undefined;
|
|
26
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata))
|
|
27
|
+
return;
|
|
28
|
+
if (!("provider" in metadata) ||
|
|
29
|
+
metadata.provider !== selected.provider ||
|
|
30
|
+
!("model" in metadata) ||
|
|
31
|
+
metadata.model !== selected.id ||
|
|
32
|
+
!("signature" in metadata) ||
|
|
33
|
+
typeof metadata.signature !== "string")
|
|
34
|
+
return;
|
|
35
|
+
return metadata.signature;
|
|
36
|
+
};
|
|
37
|
+
const metadataFor = (signature, redacted) => signature === undefined
|
|
38
|
+
? {}
|
|
39
|
+
: {
|
|
40
|
+
providerMetadata: {
|
|
41
|
+
pi: {
|
|
42
|
+
provider: selected.provider,
|
|
43
|
+
model: selected.id,
|
|
44
|
+
signature,
|
|
45
|
+
...(redacted ? { redacted: true } : {}),
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
24
49
|
const messages = [];
|
|
25
50
|
const instructions = [];
|
|
26
51
|
const content = async (parts) => {
|
|
@@ -85,11 +110,37 @@ export function piModel(options = {}) {
|
|
|
85
110
|
id: part.id,
|
|
86
111
|
name: part.name,
|
|
87
112
|
arguments: { ...part.args },
|
|
113
|
+
...(signatureFor(part) === undefined
|
|
114
|
+
? {}
|
|
115
|
+
: { thoughtSignature: signatureFor(part) }),
|
|
88
116
|
},
|
|
89
117
|
]
|
|
90
118
|
: part.type === "text"
|
|
91
|
-
? [
|
|
92
|
-
|
|
119
|
+
? [
|
|
120
|
+
{
|
|
121
|
+
type: "text",
|
|
122
|
+
text: part.text,
|
|
123
|
+
...(signatureFor(part) === undefined
|
|
124
|
+
? {}
|
|
125
|
+
: { textSignature: signatureFor(part) }),
|
|
126
|
+
},
|
|
127
|
+
]
|
|
128
|
+
: part.type === "reasoning" &&
|
|
129
|
+
signatureFor(part) !== undefined
|
|
130
|
+
? [
|
|
131
|
+
{
|
|
132
|
+
type: "thinking",
|
|
133
|
+
thinking: part.text,
|
|
134
|
+
thinkingSignature: signatureFor(part),
|
|
135
|
+
...(typeof part.providerMetadata?.pi === "object" &&
|
|
136
|
+
part.providerMetadata.pi !== null &&
|
|
137
|
+
"redacted" in part.providerMetadata.pi &&
|
|
138
|
+
part.providerMetadata.pi.redacted === true
|
|
139
|
+
? { redacted: true }
|
|
140
|
+
: {}),
|
|
141
|
+
},
|
|
142
|
+
]
|
|
143
|
+
: []),
|
|
93
144
|
});
|
|
94
145
|
}
|
|
95
146
|
else
|
|
@@ -132,15 +183,24 @@ export function piModel(options = {}) {
|
|
|
132
183
|
const output = [];
|
|
133
184
|
for (const part of response.content) {
|
|
134
185
|
if (part.type === "text")
|
|
135
|
-
output.push({
|
|
186
|
+
output.push({
|
|
187
|
+
type: "text",
|
|
188
|
+
text: part.text,
|
|
189
|
+
...metadataFor(part.textSignature),
|
|
190
|
+
});
|
|
136
191
|
else if (part.type === "thinking")
|
|
137
|
-
output.push({
|
|
192
|
+
output.push({
|
|
193
|
+
type: "reasoning",
|
|
194
|
+
text: part.thinking,
|
|
195
|
+
...metadataFor(part.thinkingSignature, part.redacted),
|
|
196
|
+
});
|
|
138
197
|
else if (part.type === "toolCall")
|
|
139
198
|
output.push({
|
|
140
199
|
type: "tool-call",
|
|
141
200
|
id: part.id,
|
|
142
201
|
name: part.name,
|
|
143
202
|
args: part.arguments,
|
|
203
|
+
...metadataFor(part.thoughtSignature),
|
|
144
204
|
});
|
|
145
205
|
}
|
|
146
206
|
return {
|
package/dist/server/ag-ui.js
CHANGED
|
@@ -40,13 +40,22 @@ export function agUiEvents(events, threadId, runId) {
|
|
|
40
40
|
: JSON.stringify(event.payload.output);
|
|
41
41
|
output.push({ type: "TEXT_MESSAGE_START", messageId, role: "assistant" }, { type: "TEXT_MESSAGE_CONTENT", messageId, delta: text }, { type: "TEXT_MESSAGE_END", messageId });
|
|
42
42
|
}
|
|
43
|
-
else if (event.type === "error") {
|
|
43
|
+
else if (event.type === "error" || event.type === "tripwire") {
|
|
44
|
+
const tripwire = event.payload.tripwire;
|
|
45
|
+
const payload = tripwire && typeof tripwire === "object"
|
|
46
|
+
? { ...event.payload, ...tripwire }
|
|
47
|
+
: event.payload;
|
|
48
|
+
const attributes = payload.attributes;
|
|
49
|
+
const message = payload.message ??
|
|
50
|
+
(attributes && typeof attributes === "object" && "message" in attributes
|
|
51
|
+
? attributes.message
|
|
52
|
+
: undefined);
|
|
44
53
|
output.push({
|
|
45
54
|
type: "RUN_ERROR",
|
|
46
|
-
message: typeof
|
|
47
|
-
|
|
48
|
-
: "Agent run failed.",
|
|
55
|
+
message: typeof message === "string" ? message : "Agent run failed.",
|
|
56
|
+
...(typeof payload.code === "string" ? { code: payload.code } : {}),
|
|
49
57
|
});
|
|
58
|
+
return Object.freeze(output);
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
61
|
output.push({ type: "RUN_FINISHED", threadId, runId });
|
package/dist/server/host.js
CHANGED
|
@@ -176,7 +176,9 @@ export async function createRuntime(options) {
|
|
|
176
176
|
? payload.threadId
|
|
177
177
|
: randomUUID();
|
|
178
178
|
if (!isSessionId(threadId))
|
|
179
|
-
return context.json({
|
|
179
|
+
return context.json({
|
|
180
|
+
error: "threadId may only contain letters, digits, '.', '_' and '-'",
|
|
181
|
+
}, 400);
|
|
180
182
|
const runId = typeof payload?.runId === "string" && payload.runId
|
|
181
183
|
? payload.runId
|
|
182
184
|
: randomUUID();
|
|
@@ -266,6 +268,7 @@ export async function createRuntime(options) {
|
|
|
266
268
|
...("approved" in inputEvent ? { approved: inputEvent.approved } : {}),
|
|
267
269
|
...("value" in inputEvent ? { value: inputEvent.value } : {}),
|
|
268
270
|
});
|
|
271
|
+
const start = entry.events.length;
|
|
269
272
|
try {
|
|
270
273
|
const result = await entry.session.input(input).completed;
|
|
271
274
|
for (const event of result.events) {
|
|
@@ -281,12 +284,26 @@ export async function createRuntime(options) {
|
|
|
281
284
|
add(entry, agentId, event.type, { interaction: event.interaction });
|
|
282
285
|
}
|
|
283
286
|
}
|
|
284
|
-
entry.
|
|
285
|
-
|
|
287
|
+
const observedFailure = entry.events
|
|
288
|
+
.slice(start)
|
|
289
|
+
.some((event) => event.type === "error" || event.type === "tripwire");
|
|
290
|
+
const completionFailure = result.events.find((event) => event.type === "error" || event.type === "tripwire");
|
|
291
|
+
const failed = observedFailure ||
|
|
292
|
+
completionFailure !== undefined ||
|
|
293
|
+
(result.status !== "completed" && result.status !== "waiting");
|
|
294
|
+
if (!observedFailure && completionFailure) {
|
|
295
|
+
add(entry, agentId, completionFailure.type, observedPayload(completionFailure));
|
|
296
|
+
}
|
|
297
|
+
else if (!observedFailure && failed) {
|
|
298
|
+
add(entry, agentId, "error", {
|
|
299
|
+
message: `Agent run ${result.status}.`,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
entry.status = failed
|
|
303
|
+
? "failed"
|
|
304
|
+
: result.status === "waiting"
|
|
286
305
|
? "waiting"
|
|
287
|
-
:
|
|
288
|
-
? "completed"
|
|
289
|
-
: "failed";
|
|
306
|
+
: "completed";
|
|
290
307
|
await entry.writes;
|
|
291
308
|
}
|
|
292
309
|
catch (error) {
|