@ouro.bot/cli 0.1.0-alpha.729 → 0.1.0-alpha.730
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.json +7 -0
- package/dist/heart/approval-store.js +791 -0
- package/dist/heart/core.js +177 -0
- package/dist/heart/session-events.js +167 -0
- package/dist/heart/tool-approval.js +339 -0
- package/dist/mind/context.js +80 -67
- package/dist/mind/session-transaction.js +379 -0
- package/dist/repertoire/stripe-client.js +2 -2
- package/dist/repertoire/tool-arguments.js +104 -0
- package/dist/repertoire/tools-shell.js +13 -0
- package/dist/repertoire/tools.js +4 -0
- package/dist/senses/bluebubbles/index.js +504 -493
- package/dist/senses/cli.js +234 -214
- package/dist/senses/pipeline.js +23 -0
- package/dist/senses/private-runtime.js +330 -325
- package/dist/senses/shared-turn.js +176 -169
- package/dist/senses/teams.js +175 -163
- package/package.json +2 -1
package/dist/heart/core.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.getProvider = getProvider;
|
|
|
7
7
|
exports.createSummarize = createSummarize;
|
|
8
8
|
exports.getProviderDisplayLabel = getProviderDisplayLabel;
|
|
9
9
|
exports.createHabitCallbackBuffer = createHabitCallbackBuffer;
|
|
10
|
+
exports.resumeApprovalContinuation = resumeApprovalContinuation;
|
|
10
11
|
exports.isChatStyleChannel = isChatStyleChannel;
|
|
11
12
|
exports.buildPonderResult = buildPonderResult;
|
|
12
13
|
exports.isExternalStateQuery = isExternalStateQuery;
|
|
@@ -17,6 +18,8 @@ exports.runAgent = runAgent;
|
|
|
17
18
|
const config_1 = require("./config");
|
|
18
19
|
const identity_1 = require("./identity");
|
|
19
20
|
const tools_1 = require("../repertoire/tools");
|
|
21
|
+
const tool_arguments_1 = require("../repertoire/tool-arguments");
|
|
22
|
+
const session_events_1 = require("./session-events");
|
|
20
23
|
const friends_1 = require("@ouro.bot/friends");
|
|
21
24
|
const tools_2 = require("../repertoire/tools");
|
|
22
25
|
const streaming_1 = require("./streaming");
|
|
@@ -244,6 +247,84 @@ function createHabitCallbackBuffer(callbacks) {
|
|
|
244
247
|
function createFinalOnlyTextBuffer(callbacks) {
|
|
245
248
|
return createCallbackBuffer(callbacks, "text-only");
|
|
246
249
|
}
|
|
250
|
+
function continuationCallbacks(callbacks, text) {
|
|
251
|
+
const noop = () => undefined;
|
|
252
|
+
return {
|
|
253
|
+
onModelStart: callbacks.onModelStart ?? noop,
|
|
254
|
+
onModelStreamStart: callbacks.onModelStreamStart ?? noop,
|
|
255
|
+
onTextChunk: (chunk) => {
|
|
256
|
+
text.value += chunk;
|
|
257
|
+
callbacks.onTextChunk?.(chunk);
|
|
258
|
+
},
|
|
259
|
+
onReasoningChunk: callbacks.onReasoningChunk ?? noop,
|
|
260
|
+
onToolStart: callbacks.onToolStart ?? noop,
|
|
261
|
+
onToolEnd: callbacks.onToolEnd ?? noop,
|
|
262
|
+
onError: callbacks.onError ?? noop,
|
|
263
|
+
...(callbacks.onClearText ? { onClearText: callbacks.onClearText } : {}),
|
|
264
|
+
...(callbacks.flushNow ? { flushNow: callbacks.flushNow } : {}),
|
|
265
|
+
...(callbacks.settleOutputMode ? { settleOutputMode: callbacks.settleOutputMode } : {}),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
async function resumeApprovalContinuation(options) {
|
|
269
|
+
const claim = options.claimContinuation();
|
|
270
|
+
if (!claim.claimed) {
|
|
271
|
+
if (claim.interruptedAfterAttempt) {
|
|
272
|
+
const notice = "the approval continuation was interrupted after provider work began; its outcome is indeterminate and it will not be retried automatically";
|
|
273
|
+
await options.deliver(notice);
|
|
274
|
+
(0, runtime_1.emitNervesEvent)({
|
|
275
|
+
level: "error",
|
|
276
|
+
component: "engine",
|
|
277
|
+
event: "engine.approval_continuation_indeterminate",
|
|
278
|
+
message: "approval continuation interrupted after provider attempt",
|
|
279
|
+
meta: { approvalId: options.record.approvalId, continuationState: claim.record.continuationState },
|
|
280
|
+
});
|
|
281
|
+
return { outcome: "terminal_notice", messages: structuredClone(options.sessionMessages) };
|
|
282
|
+
}
|
|
283
|
+
(0, runtime_1.emitNervesEvent)({
|
|
284
|
+
component: "engine",
|
|
285
|
+
event: "engine.approval_continuation_duplicate",
|
|
286
|
+
message: "approval continuation was already consumed",
|
|
287
|
+
meta: { approvalId: options.record.approvalId },
|
|
288
|
+
});
|
|
289
|
+
return { outcome: "already_continued", messages: structuredClone(options.sessionMessages) };
|
|
290
|
+
}
|
|
291
|
+
const materialized = (0, session_events_1.materializeApprovalTerminal)({
|
|
292
|
+
messages: options.sessionMessages,
|
|
293
|
+
checkpoint: options.checkpoint,
|
|
294
|
+
record: options.record,
|
|
295
|
+
currentSessionRevision: options.currentSessionRevision,
|
|
296
|
+
materializedApprovalIds: options.materializedApprovalIds,
|
|
297
|
+
});
|
|
298
|
+
const recoveredMaterialization = claim.record.continuationState === "materialized";
|
|
299
|
+
if (!recoveredMaterialization) {
|
|
300
|
+
await options.persist(materialized.messages);
|
|
301
|
+
await options.markContinuationMaterialized();
|
|
302
|
+
}
|
|
303
|
+
if (!materialized.resumeProvider) {
|
|
304
|
+
await options.deliver(materialized.directNotice);
|
|
305
|
+
await options.completeContinuation();
|
|
306
|
+
return { outcome: "terminal_notice", messages: materialized.messages };
|
|
307
|
+
}
|
|
308
|
+
const outward = { value: "" };
|
|
309
|
+
const callbacks = continuationCallbacks(options.callbacks, outward);
|
|
310
|
+
await options.markContinuationAttempted();
|
|
311
|
+
const result = await options.runAgent(materialized.messages, callbacks, options.channel, options.signal, options.runAgentOptions);
|
|
312
|
+
if (result.outcome === "suspended") {
|
|
313
|
+
await options.completeContinuation();
|
|
314
|
+
return { outcome: result.outcome, messages: materialized.messages, suspension: result.suspension };
|
|
315
|
+
}
|
|
316
|
+
await options.persist(materialized.messages, result);
|
|
317
|
+
if (outward.value)
|
|
318
|
+
await options.deliver(outward.value);
|
|
319
|
+
await options.completeContinuation();
|
|
320
|
+
(0, runtime_1.emitNervesEvent)({
|
|
321
|
+
component: "engine",
|
|
322
|
+
event: "engine.approval_continuation_completed",
|
|
323
|
+
message: "approval continuation completed through existing provider loop",
|
|
324
|
+
meta: { approvalId: options.record.approvalId, outcome: result.outcome },
|
|
325
|
+
});
|
|
326
|
+
return { outcome: result.outcome, messages: materialized.messages };
|
|
327
|
+
}
|
|
247
328
|
/**
|
|
248
329
|
* Strip <think>...</think> blocks for the violation-detection check at the
|
|
249
330
|
* end of a streaming turn. Used to tell legitimate text-only responses
|
|
@@ -932,6 +1013,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
932
1013
|
let overflowRetried = false;
|
|
933
1014
|
let outcome = "settled";
|
|
934
1015
|
let completion;
|
|
1016
|
+
let suspension;
|
|
935
1017
|
let terminalError;
|
|
936
1018
|
let terminalErrorClassification;
|
|
937
1019
|
let sawSteeringFollowUp = false;
|
|
@@ -1623,6 +1705,100 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1623
1705
|
done = true;
|
|
1624
1706
|
continue;
|
|
1625
1707
|
}
|
|
1708
|
+
const preCallMessages = structuredClone(messages.filter((message) => message.role !== "system"));
|
|
1709
|
+
const validatedCalls = options?.approvalCoordinator
|
|
1710
|
+
? result.toolCalls.map((call) => {
|
|
1711
|
+
const advertised = activeTools.find((tool) => tool.function.name === call.name);
|
|
1712
|
+
if (!advertised || !advertised.function.parameters || typeof advertised.function.parameters !== "object") {
|
|
1713
|
+
return { call, error: "tool was not advertised with a valid argument schema" };
|
|
1714
|
+
}
|
|
1715
|
+
const validated = (0, tool_arguments_1.validateAdvertisedToolArguments)(call.arguments, advertised.function.parameters);
|
|
1716
|
+
return validated.ok
|
|
1717
|
+
? { call, advertised, validated: validated.value }
|
|
1718
|
+
: { call, error: validated.reason };
|
|
1719
|
+
})
|
|
1720
|
+
: null;
|
|
1721
|
+
const invalidApprovalCall = validatedCalls?.find((entry) => "error" in entry);
|
|
1722
|
+
if (invalidApprovalCall) {
|
|
1723
|
+
await streamCallbackBuffer?.flush();
|
|
1724
|
+
pushGenerated(msg);
|
|
1725
|
+
for (const entry of validatedCalls) {
|
|
1726
|
+
const detail = "error" in entry ? entry.error : "another call in this batch had invalid arguments";
|
|
1727
|
+
const rejection = `invalid tool arguments: ${detail}`;
|
|
1728
|
+
pushGenerated({ role: "tool", tool_call_id: entry.call.id, content: rejection });
|
|
1729
|
+
providerRuntime.appendToolOutput(entry.call.id, rejection);
|
|
1730
|
+
}
|
|
1731
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1732
|
+
level: "warn",
|
|
1733
|
+
component: "engine",
|
|
1734
|
+
event: "engine.tool_arguments_rejected",
|
|
1735
|
+
message: "tool batch rejected before execution because arguments were invalid",
|
|
1736
|
+
meta: { toolCallId: invalidApprovalCall.call.id, toolName: invalidApprovalCall.call.name },
|
|
1737
|
+
});
|
|
1738
|
+
continue;
|
|
1739
|
+
}
|
|
1740
|
+
const approvalCalls = validatedCalls?.map((entry) => ({
|
|
1741
|
+
...entry,
|
|
1742
|
+
policy: (0, tools_1.approvalPolicyForToolName)(entry.call.name, entry.validated.arguments),
|
|
1743
|
+
})) ?? [];
|
|
1744
|
+
const protectedCall = approvalCalls.find((entry) => entry.policy.kind === "required");
|
|
1745
|
+
if (protectedCall && result.toolCalls.length !== 1) {
|
|
1746
|
+
streamCallbackBuffer?.discard();
|
|
1747
|
+
pushGenerated(msg);
|
|
1748
|
+
for (const call of result.toolCalls) {
|
|
1749
|
+
const rejection = "rejected: approval-eligible tool must be the sole call; no call in this batch was executed.";
|
|
1750
|
+
pushGenerated({ role: "tool", tool_call_id: call.id, content: rejection });
|
|
1751
|
+
providerRuntime.appendToolOutput(call.id, rejection);
|
|
1752
|
+
}
|
|
1753
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1754
|
+
level: "warn",
|
|
1755
|
+
component: "engine",
|
|
1756
|
+
event: "engine.approval_mixed_batch_rejected",
|
|
1757
|
+
message: "protected tool batch rejected before every handler",
|
|
1758
|
+
meta: { toolCallCount: result.toolCalls.length, protectedToolName: protectedCall.call.name },
|
|
1759
|
+
});
|
|
1760
|
+
continue;
|
|
1761
|
+
}
|
|
1762
|
+
if (protectedCall && protectedCall.policy.kind === "required") {
|
|
1763
|
+
streamCallbackBuffer?.discard();
|
|
1764
|
+
pushGenerated(msg);
|
|
1765
|
+
const toolDigest = (0, tool_arguments_1.digestJson)({
|
|
1766
|
+
name: protectedCall.call.name,
|
|
1767
|
+
schemaDigest: protectedCall.validated.schemaDigest,
|
|
1768
|
+
policyId: protectedCall.policy.policyId,
|
|
1769
|
+
});
|
|
1770
|
+
const policyDigest = (0, tool_arguments_1.digestJson)({
|
|
1771
|
+
policyId: protectedCall.policy.policyId,
|
|
1772
|
+
actionClass: protectedCall.policy.actionClass,
|
|
1773
|
+
classification: "required",
|
|
1774
|
+
});
|
|
1775
|
+
const committed = await options.approvalCoordinator.propose({
|
|
1776
|
+
toolCall: structuredClone(msg.tool_calls[0]),
|
|
1777
|
+
arguments: structuredClone(protectedCall.validated.arguments),
|
|
1778
|
+
preCallMessages,
|
|
1779
|
+
frozenAssistantMessage: structuredClone(msg),
|
|
1780
|
+
schemaDigest: protectedCall.validated.schemaDigest,
|
|
1781
|
+
toolDigest,
|
|
1782
|
+
policyDigest,
|
|
1783
|
+
policyId: protectedCall.policy.policyId,
|
|
1784
|
+
actionClass: protectedCall.policy.actionClass,
|
|
1785
|
+
});
|
|
1786
|
+
suspension = {
|
|
1787
|
+
approvalId: committed.approvalId,
|
|
1788
|
+
toolCallId: protectedCall.call.id,
|
|
1789
|
+
checkpointDigest: committed.checkpointDigest,
|
|
1790
|
+
suspendedSessionRevision: committed.suspendedSessionRevision,
|
|
1791
|
+
};
|
|
1792
|
+
outcome = "suspended";
|
|
1793
|
+
done = true;
|
|
1794
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1795
|
+
component: "engine",
|
|
1796
|
+
event: "engine.approval_turn_suspended",
|
|
1797
|
+
message: "agent turn suspended before protected tool execution",
|
|
1798
|
+
meta: { approvalId: committed.approvalId, toolCallId: protectedCall.call.id },
|
|
1799
|
+
});
|
|
1800
|
+
continue;
|
|
1801
|
+
}
|
|
1626
1802
|
const containsSoleCallOnlyViolation = result.toolCalls.length > 1
|
|
1627
1803
|
&& result.toolCalls.some((call) => {
|
|
1628
1804
|
const terminalProjection = (0, tools_1.resolveToolDefinition)(call.name)?.terminalProjection;
|
|
@@ -1975,6 +2151,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1975
2151
|
usage: lastUsage,
|
|
1976
2152
|
outcome,
|
|
1977
2153
|
completion,
|
|
2154
|
+
...(suspension ? { suspension } : {}),
|
|
1978
2155
|
...(terminalError ? { error: terminalError, errorClassification: terminalErrorClassification } : {}),
|
|
1979
2156
|
};
|
|
1980
2157
|
}
|
|
@@ -34,6 +34,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.EVENT_CONTENT_MAX_CHARS = void 0;
|
|
37
|
+
exports.materializeApprovalTerminal = materializeApprovalTerminal;
|
|
38
|
+
exports.attachApprovalSuspensionCheckpoint = attachApprovalSuspensionCheckpoint;
|
|
39
|
+
exports.listApprovalSuspensionCheckpoints = listApprovalSuspensionCheckpoints;
|
|
40
|
+
exports.removeApprovalSuspensionCheckpoint = removeApprovalSuspensionCheckpoint;
|
|
37
41
|
exports.truncateLargeEventContent = truncateLargeEventContent;
|
|
38
42
|
exports.capStructuredRecordString = capStructuredRecordString;
|
|
39
43
|
exports.capStructuredRecordStringArray = capStructuredRecordStringArray;
|
|
@@ -59,8 +63,169 @@ exports.loadSessionEnvelopeFile = loadSessionEnvelopeFile;
|
|
|
59
63
|
exports.buildCanonicalSessionEnvelope = buildCanonicalSessionEnvelope;
|
|
60
64
|
exports.appendSyntheticAssistantEvent = appendSyntheticAssistantEvent;
|
|
61
65
|
const fs = __importStar(require("fs"));
|
|
66
|
+
const node_crypto_1 = require("node:crypto");
|
|
62
67
|
const runtime_1 = require("../nerves/runtime");
|
|
63
68
|
const structured_output_1 = require("./structured-output");
|
|
69
|
+
const APPROVAL_TERMINAL_TEXT = {
|
|
70
|
+
denied: "approval denied by requester; the protected action was not executed",
|
|
71
|
+
expired: "approval expired before execution; the protected action was not executed",
|
|
72
|
+
drifted: "approval became invalid before execution; the protected action was not executed",
|
|
73
|
+
abandoned_before_attempt: "approval was abandoned before execution; the protected action was not executed; request a fresh approval",
|
|
74
|
+
attempted_indeterminate: "execution may have occurred; do not retry automatically",
|
|
75
|
+
};
|
|
76
|
+
function messagesRevision(messages) {
|
|
77
|
+
return (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(messages), "utf8").digest("hex");
|
|
78
|
+
}
|
|
79
|
+
function hasExactMaterializedApprovalPair(messages, checkpoint, toolCallId, content) {
|
|
80
|
+
if (messages.length < 2)
|
|
81
|
+
return false;
|
|
82
|
+
const assistant = messages.at(-2);
|
|
83
|
+
const tool = messages.at(-1);
|
|
84
|
+
const expectedTool = {
|
|
85
|
+
role: "tool",
|
|
86
|
+
tool_call_id: toolCallId,
|
|
87
|
+
content,
|
|
88
|
+
};
|
|
89
|
+
return assistant.role === "assistant"
|
|
90
|
+
&& tool.role === "tool"
|
|
91
|
+
&& messageFingerprint(assistant) === messageFingerprint(checkpoint.frozenAssistantMessage)
|
|
92
|
+
&& messageFingerprint(tool) === messageFingerprint(expectedTool);
|
|
93
|
+
}
|
|
94
|
+
function materializeApprovalTerminal(input) {
|
|
95
|
+
const messages = structuredClone(input.messages);
|
|
96
|
+
if (input.record.state === "session_head_changed") {
|
|
97
|
+
return {
|
|
98
|
+
messages,
|
|
99
|
+
materialized: false,
|
|
100
|
+
resumeProvider: false,
|
|
101
|
+
directNotice: "the session changed before this approval could be applied; the protected action was not executed",
|
|
102
|
+
revision: messagesRevision(messages),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const content = input.record.state === "succeeded" || input.record.state === "failed"
|
|
106
|
+
? input.record.result
|
|
107
|
+
: APPROVAL_TERMINAL_TEXT[input.record.state];
|
|
108
|
+
if (!content)
|
|
109
|
+
throw new Error(`approval state ${input.record.state} cannot be materialized`);
|
|
110
|
+
const resumeProvider = input.record.state === "succeeded" || input.record.state === "failed" || input.record.state === "denied";
|
|
111
|
+
const exactPair = hasExactMaterializedApprovalPair(messages, input.checkpoint, input.record.toolCallId, content);
|
|
112
|
+
if (exactPair) {
|
|
113
|
+
return {
|
|
114
|
+
messages,
|
|
115
|
+
materialized: false,
|
|
116
|
+
resumeProvider,
|
|
117
|
+
...(resumeProvider ? {} : { directNotice: content }),
|
|
118
|
+
...(input.record.state === "attempted_indeterminate" ? { severity: "high" } : {}),
|
|
119
|
+
revision: messagesRevision(messages),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const advanced = input.currentSessionRevision !== input.checkpoint.suspendedSessionRevision;
|
|
123
|
+
if (advanced) {
|
|
124
|
+
return {
|
|
125
|
+
messages,
|
|
126
|
+
materialized: false,
|
|
127
|
+
resumeProvider: false,
|
|
128
|
+
directNotice: "the session changed before this approval could be applied; the protected action was not executed",
|
|
129
|
+
revision: messagesRevision(messages),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (input.materializedApprovalIds?.includes(input.record.approvalId)) {
|
|
133
|
+
return {
|
|
134
|
+
messages,
|
|
135
|
+
materialized: false,
|
|
136
|
+
resumeProvider,
|
|
137
|
+
...(resumeProvider ? {} : { directNotice: content }),
|
|
138
|
+
...(input.record.state === "attempted_indeterminate" ? { severity: "high" } : {}),
|
|
139
|
+
revision: messagesRevision(messages),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
const frozen = structuredClone(input.checkpoint.frozenAssistantMessage);
|
|
143
|
+
messages.push(frozen, { role: "tool", tool_call_id: input.record.toolCallId, content });
|
|
144
|
+
const directNotice = resumeProvider ? undefined : content;
|
|
145
|
+
(0, runtime_1.emitNervesEvent)({
|
|
146
|
+
component: "mind",
|
|
147
|
+
event: "mind.approval_terminal_materialized",
|
|
148
|
+
message: "materialized approval terminal transcript pair",
|
|
149
|
+
meta: { approvalId: input.record.approvalId, state: input.record.state, resumeProvider },
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
messages,
|
|
153
|
+
materialized: true,
|
|
154
|
+
resumeProvider,
|
|
155
|
+
...(directNotice ? { directNotice } : {}),
|
|
156
|
+
...(input.record.state === "attempted_indeterminate" ? { severity: "high" } : {}),
|
|
157
|
+
revision: messagesRevision(messages),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const APPROVAL_HASH = /^[a-f0-9]{64}$/;
|
|
161
|
+
function normalizeApprovalSuspensions(value) {
|
|
162
|
+
if (!Array.isArray(value))
|
|
163
|
+
return [];
|
|
164
|
+
return value.flatMap((item) => {
|
|
165
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
166
|
+
return [];
|
|
167
|
+
const record = item;
|
|
168
|
+
if (typeof record.approvalId !== "string"
|
|
169
|
+
|| typeof record.checkpointDigest !== "string"
|
|
170
|
+
|| !APPROVAL_HASH.test(record.checkpointDigest)
|
|
171
|
+
|| typeof record.baseSessionRevision !== "string"
|
|
172
|
+
|| !APPROVAL_HASH.test(record.baseSessionRevision)
|
|
173
|
+
|| typeof record.suspendedSessionRevision !== "string"
|
|
174
|
+
|| !APPROVAL_HASH.test(record.suspendedSessionRevision)
|
|
175
|
+
|| typeof record.argumentDigest !== "string"
|
|
176
|
+
|| !APPROVAL_HASH.test(record.argumentDigest)
|
|
177
|
+
|| typeof record.schemaDigest !== "string"
|
|
178
|
+
|| !APPROVAL_HASH.test(record.schemaDigest)
|
|
179
|
+
|| typeof record.toolDigest !== "string"
|
|
180
|
+
|| !APPROVAL_HASH.test(record.toolDigest)
|
|
181
|
+
|| typeof record.policyDigest !== "string"
|
|
182
|
+
|| !APPROVAL_HASH.test(record.policyDigest)
|
|
183
|
+
|| typeof record.preCallDigest !== "string"
|
|
184
|
+
|| !APPROVAL_HASH.test(record.preCallDigest)
|
|
185
|
+
|| !Array.isArray(record.preCallMessages)
|
|
186
|
+
|| !record.frozenAssistantMessage
|
|
187
|
+
|| typeof record.frozenAssistantMessage !== "object"
|
|
188
|
+
|| Array.isArray(record.frozenAssistantMessage))
|
|
189
|
+
return [];
|
|
190
|
+
return [{
|
|
191
|
+
approvalId: record.approvalId,
|
|
192
|
+
checkpointDigest: record.checkpointDigest,
|
|
193
|
+
baseSessionRevision: record.baseSessionRevision,
|
|
194
|
+
suspendedSessionRevision: record.suspendedSessionRevision,
|
|
195
|
+
argumentDigest: record.argumentDigest,
|
|
196
|
+
schemaDigest: record.schemaDigest,
|
|
197
|
+
toolDigest: record.toolDigest,
|
|
198
|
+
policyDigest: record.policyDigest,
|
|
199
|
+
preCallDigest: record.preCallDigest,
|
|
200
|
+
preCallMessages: structuredClone(record.preCallMessages),
|
|
201
|
+
frozenAssistantMessage: structuredClone(record.frozenAssistantMessage),
|
|
202
|
+
}];
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
function attachApprovalSuspensionCheckpoint(envelope, checkpoint) {
|
|
206
|
+
const existing = envelope.approvalSuspensions ?? [];
|
|
207
|
+
const retained = existing.filter((item) => item.approvalId !== checkpoint.approvalId);
|
|
208
|
+
(0, runtime_1.emitNervesEvent)({
|
|
209
|
+
component: "engine",
|
|
210
|
+
event: "engine.approval_checkpoint_attached",
|
|
211
|
+
message: "approval suspension checkpoint attached outside provider projection",
|
|
212
|
+
meta: { approvalId: checkpoint.approvalId },
|
|
213
|
+
});
|
|
214
|
+
return { ...envelope, approvalSuspensions: [...retained, structuredClone(checkpoint)] };
|
|
215
|
+
}
|
|
216
|
+
function listApprovalSuspensionCheckpoints(envelope) {
|
|
217
|
+
return structuredClone(envelope.approvalSuspensions ?? []);
|
|
218
|
+
}
|
|
219
|
+
function removeApprovalSuspensionCheckpoint(envelope, approvalId) {
|
|
220
|
+
const approvalSuspensions = (envelope.approvalSuspensions ?? []).filter((item) => item.approvalId !== approvalId);
|
|
221
|
+
(0, runtime_1.emitNervesEvent)({
|
|
222
|
+
component: "engine",
|
|
223
|
+
event: "engine.approval_checkpoint_removed",
|
|
224
|
+
message: "approval suspension checkpoint removed",
|
|
225
|
+
meta: { approvalId },
|
|
226
|
+
});
|
|
227
|
+
return { ...envelope, approvalSuspensions };
|
|
228
|
+
}
|
|
64
229
|
exports.EVENT_CONTENT_MAX_CHARS = 256 * 1024;
|
|
65
230
|
function truncateLargeEventContent(content, maxChars) {
|
|
66
231
|
if (typeof content !== "string") {
|
|
@@ -972,6 +1137,7 @@ function parseSessionEnvelope(raw, options = {}) {
|
|
|
972
1137
|
: (0, structured_output_1.normalizeStructuredOutputs)(record.structuredOutputs),
|
|
973
1138
|
lastUsage: normalizeUsage(record.lastUsage),
|
|
974
1139
|
state: normalizeContinuityState(record.state),
|
|
1140
|
+
approvalSuspensions: normalizeApprovalSuspensions(record.approvalSuspensions),
|
|
975
1141
|
};
|
|
976
1142
|
}
|
|
977
1143
|
function loadSessionEnvelopeFile(filePath) {
|
|
@@ -1109,6 +1275,7 @@ function buildCanonicalSessionEnvelope(options) {
|
|
|
1109
1275
|
structuredOutputs: (0, structured_output_1.extractStructuredOutputsFromEvents)(prunedEvents),
|
|
1110
1276
|
lastUsage: normalizeUsage(options.lastUsage),
|
|
1111
1277
|
state: normalizeContinuityState(options.state),
|
|
1278
|
+
approvalSuspensions: structuredClone(options.existing?.approvalSuspensions ?? []),
|
|
1112
1279
|
},
|
|
1113
1280
|
evictedEvents,
|
|
1114
1281
|
};
|