@ouro.bot/cli 0.1.0-alpha.695 → 0.1.0-alpha.697
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 +15 -0
- package/dist/arc/flight-recorder.js +139 -21
- package/dist/arc/obligations.js +28 -6
- package/dist/arc/packets.js +48 -0
- package/dist/heart/autonomy-budget.js +434 -0
- package/dist/heart/core.js +11 -0
- package/dist/heart/daemon/cadence.js +110 -0
- package/dist/heart/daemon/cli-exec.js +20 -0
- package/dist/heart/daemon/cli-help.js +74 -0
- package/dist/heart/daemon/cli-parse.js +466 -0
- package/dist/heart/daemon/daemon.js +24 -3
- package/dist/heart/daemon/doctor.js +151 -0
- package/dist/heart/daemon/stale-bundle-prune.js +23 -6
- package/dist/heart/habits/habit-parser.js +4 -1
- package/dist/heart/habits/habit-scheduler.js +46 -38
- package/dist/heart/habits/habit-session.js +2 -3
- package/dist/heart/run-ledger.js +247 -0
- package/dist/heart/runtime-credentials.js +12 -3
- package/dist/heart/work-card.js +1 -11
- package/dist/mind/prompt-budget.js +380 -0
- package/dist/repertoire/tools-base.js +2 -0
- package/dist/repertoire/tools-obligations.js +39 -0
- package/dist/repertoire/tools-rsvp.js +139 -0
- package/dist/rsvp/aisleplanner-client.js +354 -0
- package/dist/rsvp/cli.js +679 -0
- package/dist/rsvp/config.js +327 -0
- package/dist/rsvp/cutover.js +431 -0
- package/dist/rsvp/diagnostics.js +222 -0
- package/dist/rsvp/diff-renderer.js +159 -0
- package/dist/rsvp/habit-policy.js +85 -0
- package/dist/rsvp/habit-stage.js +93 -0
- package/dist/rsvp/incident-bundle.js +97 -0
- package/dist/rsvp/migration.js +204 -0
- package/dist/rsvp/outbound-state.js +254 -0
- package/dist/rsvp/query.js +118 -0
- package/dist/rsvp/replay.js +233 -0
- package/dist/rsvp/snapshot.js +232 -0
- package/dist/senses/bluebubbles/client.js +13 -7
- package/dist/senses/bluebubbles/index.js +227 -22
- package/dist/senses/bluebubbles/outbound-state.js +304 -0
- package/dist/senses/bluebubbles/replay.js +147 -0
- package/dist/senses/context-packet-ledger.js +245 -0
- package/dist/senses/context-packets.js +154 -0
- package/dist/senses/pipeline.js +92 -12
- package/dist/senses/private-runtime-worker.js +170 -31
- package/dist/senses/private-runtime.js +13 -1
- package/dist/senses/voice/twilio-phone.js +9 -5
- package/package.json +1 -1
|
@@ -43,6 +43,7 @@ exports.applyRuntimeCredentialBootstrapMessage = applyRuntimeCredentialBootstrap
|
|
|
43
43
|
exports.waitForRuntimeCredentialBootstrap = waitForRuntimeCredentialBootstrap;
|
|
44
44
|
exports.refreshRuntimeCredentialConfig = refreshRuntimeCredentialConfig;
|
|
45
45
|
exports.refreshMachineRuntimeCredentialConfig = refreshMachineRuntimeCredentialConfig;
|
|
46
|
+
exports.mergeRuntimeCredentialConfig = mergeRuntimeCredentialConfig;
|
|
46
47
|
exports.upsertRuntimeCredentialConfig = upsertRuntimeCredentialConfig;
|
|
47
48
|
exports.upsertMachineRuntimeCredentialConfig = upsertMachineRuntimeCredentialConfig;
|
|
48
49
|
exports.mergeMachineRuntimeCredentialConfig = mergeMachineRuntimeCredentialConfig;
|
|
@@ -315,17 +316,25 @@ async function refreshRuntimeCredentialConfig(agentName, options = {}) {
|
|
|
315
316
|
async function refreshMachineRuntimeCredentialConfig(agentName, machineId, options = {}) {
|
|
316
317
|
return refreshRuntimeCredentialConfigItem(agentName, machineRuntimeConfigItemName(machineId), cacheMachineResult, options);
|
|
317
318
|
}
|
|
318
|
-
function
|
|
319
|
+
function mergeRuntimeCredentialConfigObjects(base, patch) {
|
|
319
320
|
const next = { ...base };
|
|
320
321
|
for (const key of Object.keys(patch)) {
|
|
321
322
|
const baseValue = next[key];
|
|
322
323
|
const patchValue = patch[key];
|
|
323
324
|
next[key] = isRecord(baseValue) && isRecord(patchValue)
|
|
324
|
-
?
|
|
325
|
+
? mergeRuntimeCredentialConfigObjects(baseValue, patchValue)
|
|
325
326
|
: patchValue;
|
|
326
327
|
}
|
|
327
328
|
return next;
|
|
328
329
|
}
|
|
330
|
+
async function mergeRuntimeCredentialConfig(agentName, patch, now = new Date()) {
|
|
331
|
+
const current = await refreshRuntimeCredentialConfig(agentName);
|
|
332
|
+
if (!current.ok && current.reason !== "missing") {
|
|
333
|
+
throw new Error(`cannot merge runtime config at ${current.itemPath}: ${current.error}`);
|
|
334
|
+
}
|
|
335
|
+
const base = current.ok ? current.config : {};
|
|
336
|
+
return upsertRuntimeCredentialConfig(agentName, mergeRuntimeCredentialConfigObjects(base, patch), now);
|
|
337
|
+
}
|
|
329
338
|
async function upsertRuntimeCredentialConfig(agentName, config, now = new Date()) {
|
|
330
339
|
const payload = {
|
|
331
340
|
schemaVersion: 1,
|
|
@@ -381,7 +390,7 @@ async function mergeMachineRuntimeCredentialConfig(agentName, machineId, patch,
|
|
|
381
390
|
throw new Error(`cannot merge machine runtime config at ${current.itemPath}: ${current.error}`);
|
|
382
391
|
}
|
|
383
392
|
const base = current.ok ? current.config : {};
|
|
384
|
-
return upsertMachineRuntimeCredentialConfig(agentName, machineId,
|
|
393
|
+
return upsertMachineRuntimeCredentialConfig(agentName, machineId, mergeRuntimeCredentialConfigObjects(base, patch), now);
|
|
385
394
|
}
|
|
386
395
|
function resetRuntimeCredentialConfigCache() {
|
|
387
396
|
cachedRuntimeConfigs = new Map();
|
package/dist/heart/work-card.js
CHANGED
|
@@ -44,14 +44,6 @@ const evolution_1 = require("../arc/evolution");
|
|
|
44
44
|
const obligations_1 = require("../arc/obligations");
|
|
45
45
|
const packets_1 = require("../arc/packets");
|
|
46
46
|
const flight_recorder_1 = require("../arc/flight-recorder");
|
|
47
|
-
const ACTIVE_PACKET_STATUSES = new Set([
|
|
48
|
-
"drafting",
|
|
49
|
-
"processing",
|
|
50
|
-
"validating",
|
|
51
|
-
"collaborating",
|
|
52
|
-
"paused",
|
|
53
|
-
"blocked",
|
|
54
|
-
]);
|
|
55
47
|
function isoFromMs(ms) {
|
|
56
48
|
return new Date(ms).toISOString();
|
|
57
49
|
}
|
|
@@ -244,9 +236,7 @@ function buildWorkCard(agentName, agentRoot, options = {}) {
|
|
|
244
236
|
const sourceIssues = scanArcSourceIssues(agentRoot);
|
|
245
237
|
const owed = (0, obligations_1.readPendingObligations)(agentRoot).map(obligationItem);
|
|
246
238
|
const returnObligations = (0, obligations_1.listActiveReturnObligationsForRoot)(agentRoot, { now: options.nowMs }).map(returnObligationItem);
|
|
247
|
-
const activePackets = (0, packets_1.
|
|
248
|
-
.filter((packet) => ACTIVE_PACKET_STATUSES.has(packet.status))
|
|
249
|
-
.map(packetItem);
|
|
239
|
+
const activePackets = (0, packets_1.listActivePonderPackets)(agentRoot, { now: options.nowMs }).map(packetItem);
|
|
250
240
|
const evolutionCases = (0, evolution_1.listOpenEvolutionCases)(agentRoot).map(evolutionItem);
|
|
251
241
|
const activeWork = [...activePackets, ...evolutionCases];
|
|
252
242
|
const waiting = waitingOnHuman([...owed, ...returnObligations, ...activeWork]);
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROMPT_BUDGET_FALLBACK_CONTEXT_WINDOW_TOKENS = exports.PROMPT_BUDGET_POLICY_VERSION = void 0;
|
|
4
|
+
exports.estimatePromptBudgetTokensForMessage = estimatePromptBudgetTokensForMessage;
|
|
5
|
+
exports.estimatePromptBudgetTokens = estimatePromptBudgetTokens;
|
|
6
|
+
exports.applyPromptBudget = applyPromptBudget;
|
|
7
|
+
const runtime_1 = require("../nerves/runtime");
|
|
8
|
+
exports.PROMPT_BUDGET_POLICY_VERSION = "prompt-budget/v1";
|
|
9
|
+
exports.PROMPT_BUDGET_FALLBACK_CONTEXT_WINDOW_TOKENS = 24_000;
|
|
10
|
+
function contentCharacters(content) {
|
|
11
|
+
if (typeof content === "string")
|
|
12
|
+
return content.length;
|
|
13
|
+
if (!content)
|
|
14
|
+
return 0;
|
|
15
|
+
if (Array.isArray(content)) {
|
|
16
|
+
return content.reduce((total, part) => {
|
|
17
|
+
if (typeof part === "string")
|
|
18
|
+
return total + part.length;
|
|
19
|
+
if (!part || typeof part !== "object")
|
|
20
|
+
return total;
|
|
21
|
+
const record = part;
|
|
22
|
+
if (typeof record.text === "string")
|
|
23
|
+
return total + record.text.length;
|
|
24
|
+
if (typeof record.content === "string")
|
|
25
|
+
return total + record.content.length;
|
|
26
|
+
try {
|
|
27
|
+
return total + JSON.stringify(record).length;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return total;
|
|
31
|
+
}
|
|
32
|
+
}, 0);
|
|
33
|
+
}
|
|
34
|
+
if (typeof content === "object") {
|
|
35
|
+
const record = content;
|
|
36
|
+
if (typeof record.text === "string")
|
|
37
|
+
return record.text.length;
|
|
38
|
+
if (typeof record.content === "string")
|
|
39
|
+
return record.content.length;
|
|
40
|
+
try {
|
|
41
|
+
return JSON.stringify(record).length;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
function toolCallCharacters(toolCalls) {
|
|
50
|
+
if (!Array.isArray(toolCalls))
|
|
51
|
+
return 0;
|
|
52
|
+
let total = 0;
|
|
53
|
+
for (const call of toolCalls) {
|
|
54
|
+
if (!call || typeof call !== "object")
|
|
55
|
+
continue;
|
|
56
|
+
const record = call;
|
|
57
|
+
if (typeof record.id === "string")
|
|
58
|
+
total += record.id.length;
|
|
59
|
+
const fn = record.function;
|
|
60
|
+
if (fn && typeof fn === "object") {
|
|
61
|
+
const fnRecord = fn;
|
|
62
|
+
if (typeof fnRecord.name === "string")
|
|
63
|
+
total += fnRecord.name.length;
|
|
64
|
+
if (typeof fnRecord.arguments === "string")
|
|
65
|
+
total += fnRecord.arguments.length;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return total;
|
|
69
|
+
}
|
|
70
|
+
function estimatePromptBudgetTokensForMessage(message) {
|
|
71
|
+
const record = message;
|
|
72
|
+
let characters = 0;
|
|
73
|
+
if (typeof record.role === "string")
|
|
74
|
+
characters += record.role.length;
|
|
75
|
+
if (typeof record.name === "string")
|
|
76
|
+
characters += record.name.length;
|
|
77
|
+
if (typeof record.tool_call_id === "string")
|
|
78
|
+
characters += record.tool_call_id.length;
|
|
79
|
+
characters += contentCharacters(record.content);
|
|
80
|
+
characters += toolCallCharacters(record.tool_calls);
|
|
81
|
+
return Math.ceil(characters / 4);
|
|
82
|
+
}
|
|
83
|
+
function estimatePromptBudgetTokens(messages) {
|
|
84
|
+
return messages.reduce((total, message) => total + estimatePromptBudgetTokensForMessage(message), 0);
|
|
85
|
+
}
|
|
86
|
+
function itemTokens(item) {
|
|
87
|
+
return estimatePromptBudgetTokens(item.messages);
|
|
88
|
+
}
|
|
89
|
+
function cloneMessage(message) {
|
|
90
|
+
return { ...message };
|
|
91
|
+
}
|
|
92
|
+
function messageText(message) {
|
|
93
|
+
return typeof message.content === "string" ? message.content : "";
|
|
94
|
+
}
|
|
95
|
+
function isSenseContextPacketText(text) {
|
|
96
|
+
return /^Untrusted\s+\S+\s+context for this same thread\./.test(text);
|
|
97
|
+
}
|
|
98
|
+
function sourceRefsFrom(text) {
|
|
99
|
+
return [...new Set(text.match(/\bbbmsg:[^\]\s,]+/g) ?? [])];
|
|
100
|
+
}
|
|
101
|
+
function compactSenseContextItem(item) {
|
|
102
|
+
const message = item.messages[0];
|
|
103
|
+
const text = messageText(message);
|
|
104
|
+
const firstLine = text.split("\n").find((line) => line.trim().length > 0);
|
|
105
|
+
const refs = sourceRefsFrom(text);
|
|
106
|
+
const content = [
|
|
107
|
+
firstLine,
|
|
108
|
+
`[prompt budget compacted message excerpts; source refs retained: ${refs.length > 0 ? refs.join(", ") : "none"}]`,
|
|
109
|
+
].join("\n");
|
|
110
|
+
return {
|
|
111
|
+
...item,
|
|
112
|
+
compacted: true,
|
|
113
|
+
messages: [{ ...cloneMessage(message), content }],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const SYSTEM_MARKERS = [
|
|
117
|
+
{ marker: "## from my kept record", source: "kept-memory", priority: 5 },
|
|
118
|
+
{ marker: "## retrieved from my Desk record diary", source: "kept-memory", priority: 5 },
|
|
119
|
+
{ marker: "## habit receipt", source: "habit-receipt", priority: 6 },
|
|
120
|
+
{ marker: "## run ledger", source: "habit-receipt", priority: 6 },
|
|
121
|
+
{ marker: "## diagnostics", source: "diagnostics", priority: 7 },
|
|
122
|
+
{ marker: "## doctor", source: "diagnostics", priority: 7 },
|
|
123
|
+
{ marker: "## incident bundle", source: "diagnostics", priority: 7 },
|
|
124
|
+
{ marker: "## replay", source: "diagnostics", priority: 7 },
|
|
125
|
+
];
|
|
126
|
+
function classifySystemSegment(text) {
|
|
127
|
+
const lower = text.toLowerCase();
|
|
128
|
+
if (lower.includes("habit receipt") || lower.includes("run ledger"))
|
|
129
|
+
return { source: "habit-receipt", priority: 6 };
|
|
130
|
+
if (lower.includes("diagnostics") || lower.includes("doctor") || lower.includes("incident bundle") || lower.includes("replay")) {
|
|
131
|
+
return { source: "diagnostics", priority: 7 };
|
|
132
|
+
}
|
|
133
|
+
return { source: "system-prompt", priority: 1 };
|
|
134
|
+
}
|
|
135
|
+
function systemItems(message, index) {
|
|
136
|
+
const text = messageText(message);
|
|
137
|
+
if (isSenseContextPacketText(text)) {
|
|
138
|
+
return [{
|
|
139
|
+
id: `system:${index}:context`,
|
|
140
|
+
priority: 4,
|
|
141
|
+
source: "sense-context-packet",
|
|
142
|
+
messages: [cloneMessage(message)],
|
|
143
|
+
originalMessageCount: 1,
|
|
144
|
+
}];
|
|
145
|
+
}
|
|
146
|
+
const markerPositions = SYSTEM_MARKERS
|
|
147
|
+
.map((marker) => ({ ...marker, start: text.toLowerCase().indexOf(marker.marker.toLowerCase()) }))
|
|
148
|
+
.filter((entry) => entry.start >= 0)
|
|
149
|
+
.sort((left, right) => left.start - right.start);
|
|
150
|
+
if (markerPositions.length === 0) {
|
|
151
|
+
const classified = classifySystemSegment(text);
|
|
152
|
+
return [{
|
|
153
|
+
id: `system:${index}:0`,
|
|
154
|
+
priority: classified.priority,
|
|
155
|
+
source: classified.source,
|
|
156
|
+
messages: [cloneMessage(message)],
|
|
157
|
+
originalMessageCount: 1,
|
|
158
|
+
}];
|
|
159
|
+
}
|
|
160
|
+
const segments = [];
|
|
161
|
+
if (markerPositions[0].start > 0) {
|
|
162
|
+
segments.push({ start: 0, end: markerPositions[0].start, source: "system-prompt", priority: 1 });
|
|
163
|
+
}
|
|
164
|
+
markerPositions.forEach((position, markerIndex) => {
|
|
165
|
+
segments.push({
|
|
166
|
+
start: position.start,
|
|
167
|
+
end: markerPositions[markerIndex + 1]?.start ?? text.length,
|
|
168
|
+
source: position.source,
|
|
169
|
+
priority: position.priority,
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
return segments
|
|
173
|
+
.map((segment, segmentIndex) => {
|
|
174
|
+
const content = text.slice(segment.start, segment.end).trim();
|
|
175
|
+
if (!content)
|
|
176
|
+
return null;
|
|
177
|
+
return {
|
|
178
|
+
id: `system:${index}:${segmentIndex}`,
|
|
179
|
+
priority: segment.priority,
|
|
180
|
+
source: segment.source,
|
|
181
|
+
messages: [{ ...cloneMessage(message), content }],
|
|
182
|
+
originalMessageCount: segmentIndex === 0 ? 1 : 0,
|
|
183
|
+
};
|
|
184
|
+
})
|
|
185
|
+
.filter((item) => item !== null);
|
|
186
|
+
}
|
|
187
|
+
function assistantHasToolCalls(message) {
|
|
188
|
+
return message.role === "assistant"
|
|
189
|
+
&& Array.isArray(message.tool_calls)
|
|
190
|
+
&& message.tool_calls.length > 0;
|
|
191
|
+
}
|
|
192
|
+
function nonSystemSourceFor(messages) {
|
|
193
|
+
const text = messages.map(messageText).join("\n").toLowerCase();
|
|
194
|
+
if (text.includes("habit receipt") || text.includes("run ledger"))
|
|
195
|
+
return "habit-receipt";
|
|
196
|
+
if (text.includes("diagnostics") || text.includes("doctor") || text.includes("incident bundle") || text.includes("replay"))
|
|
197
|
+
return "diagnostics";
|
|
198
|
+
return "recent-session-tail";
|
|
199
|
+
}
|
|
200
|
+
function buildPromptBudgetItems(messages) {
|
|
201
|
+
const rawItems = [];
|
|
202
|
+
let index = 0;
|
|
203
|
+
while (index < messages.length) {
|
|
204
|
+
const message = messages[index];
|
|
205
|
+
if (message.role === "system") {
|
|
206
|
+
rawItems.push(...systemItems(message, index));
|
|
207
|
+
index += 1;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (assistantHasToolCalls(message)) {
|
|
211
|
+
const block = [cloneMessage(message)];
|
|
212
|
+
let next = index + 1;
|
|
213
|
+
while (messages[next]?.role === "tool") {
|
|
214
|
+
block.push(cloneMessage(messages[next]));
|
|
215
|
+
next += 1;
|
|
216
|
+
}
|
|
217
|
+
rawItems.push({
|
|
218
|
+
id: `conversation:${index}`,
|
|
219
|
+
priority: 3,
|
|
220
|
+
source: nonSystemSourceFor(block),
|
|
221
|
+
messages: block,
|
|
222
|
+
originalMessageCount: block.length,
|
|
223
|
+
});
|
|
224
|
+
index = next;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
rawItems.push({
|
|
228
|
+
id: `conversation:${index}`,
|
|
229
|
+
priority: 3,
|
|
230
|
+
source: nonSystemSourceFor([message]),
|
|
231
|
+
messages: [cloneMessage(message)],
|
|
232
|
+
originalMessageCount: 1,
|
|
233
|
+
});
|
|
234
|
+
index += 1;
|
|
235
|
+
}
|
|
236
|
+
const lastUserItemIndex = rawItems.findLastIndex((item) => item.messages.some((message) => message.role === "user"));
|
|
237
|
+
const latestToolItemIndex = rawItems.findLastIndex((item) => item.messages.some((message) => message.role === "tool" || assistantHasToolCalls(message)));
|
|
238
|
+
return rawItems.map((item, itemIndex) => {
|
|
239
|
+
if (itemIndex === lastUserItemIndex) {
|
|
240
|
+
return { ...item, priority: 1, source: "current-user-message" };
|
|
241
|
+
}
|
|
242
|
+
if (itemIndex === latestToolItemIndex) {
|
|
243
|
+
return { ...item, priority: 1, source: "active-tool-result-context" };
|
|
244
|
+
}
|
|
245
|
+
if (item.messages.every((message) => message.role === "system"))
|
|
246
|
+
return item;
|
|
247
|
+
if (item.source === "habit-receipt")
|
|
248
|
+
return { ...item, priority: 6 };
|
|
249
|
+
if (item.source === "diagnostics")
|
|
250
|
+
return { ...item, priority: 7 };
|
|
251
|
+
if (lastUserItemIndex >= 0 && itemIndex < lastUserItemIndex)
|
|
252
|
+
return { ...item, priority: 8, source: "older-session-history" };
|
|
253
|
+
return { ...item, priority: 3, source: "recent-session-tail" };
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
function flattenItems(items) {
|
|
257
|
+
return items.flatMap((item) => item.messages.map(cloneMessage));
|
|
258
|
+
}
|
|
259
|
+
function budgetFor(input) {
|
|
260
|
+
const contextWindowTokens = typeof input.contextWindowTokens === "number" && Number.isFinite(input.contextWindowTokens) && input.contextWindowTokens > 0
|
|
261
|
+
? Math.floor(input.contextWindowTokens)
|
|
262
|
+
: exports.PROMPT_BUDGET_FALLBACK_CONTEXT_WINDOW_TOKENS;
|
|
263
|
+
const outputReserveRatio = input.outputReserveRatio ?? 0.2;
|
|
264
|
+
const protocolReserveRatio = input.protocolReserveRatio ?? 0.1;
|
|
265
|
+
const outputReserveTokens = Math.floor(contextWindowTokens * outputReserveRatio);
|
|
266
|
+
const protocolReserveTokens = Math.floor(contextWindowTokens * protocolReserveRatio);
|
|
267
|
+
return {
|
|
268
|
+
provider: input.provider,
|
|
269
|
+
model: input.model,
|
|
270
|
+
contextWindowTokens,
|
|
271
|
+
outputReserveTokens,
|
|
272
|
+
protocolReserveTokens,
|
|
273
|
+
inputTokenLimit: Math.max(0, contextWindowTokens - outputReserveTokens - protocolReserveTokens),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function totalItemTokens(items) {
|
|
277
|
+
return estimatePromptBudgetTokens(flattenItems(items));
|
|
278
|
+
}
|
|
279
|
+
function truncationFor(item, reason, beforeTokens, afterTokens) {
|
|
280
|
+
return {
|
|
281
|
+
source: item.source,
|
|
282
|
+
reason,
|
|
283
|
+
beforeTokens,
|
|
284
|
+
afterTokens,
|
|
285
|
+
messageCount: item.originalMessageCount,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function emitPromptBudgetEvent(result) {
|
|
289
|
+
(0, runtime_1.emitNervesEvent)({
|
|
290
|
+
component: "mind",
|
|
291
|
+
event: "mind.prompt_budget_applied",
|
|
292
|
+
message: "applied final prompt budget before provider turn",
|
|
293
|
+
meta: {
|
|
294
|
+
status: result.status,
|
|
295
|
+
provider: result.budget.provider,
|
|
296
|
+
model: result.budget.model,
|
|
297
|
+
contextWindowTokens: result.budget.contextWindowTokens,
|
|
298
|
+
inputTokenLimit: result.budget.inputTokenLimit,
|
|
299
|
+
estimatedBeforeTokens: result.stats.estimatedBeforeTokens,
|
|
300
|
+
estimatedAfterTokens: result.stats.estimatedAfterTokens,
|
|
301
|
+
droppedMessages: result.stats.droppedMessages,
|
|
302
|
+
truncationCount: result.stats.truncations.length,
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
function applyPromptBudget(input) {
|
|
307
|
+
const budget = budgetFor(input);
|
|
308
|
+
const estimatedBeforeTokens = estimatePromptBudgetTokens(input.messages);
|
|
309
|
+
const truncations = [];
|
|
310
|
+
let droppedMessages = 0;
|
|
311
|
+
if (estimatedBeforeTokens <= budget.inputTokenLimit) {
|
|
312
|
+
const result = {
|
|
313
|
+
policyVersion: exports.PROMPT_BUDGET_POLICY_VERSION,
|
|
314
|
+
status: "within_budget",
|
|
315
|
+
messages: input.messages.map(cloneMessage),
|
|
316
|
+
budget,
|
|
317
|
+
stats: {
|
|
318
|
+
estimatedBeforeTokens,
|
|
319
|
+
estimatedAfterTokens: estimatedBeforeTokens,
|
|
320
|
+
originalMessages: input.messages.length,
|
|
321
|
+
finalMessages: input.messages.length,
|
|
322
|
+
droppedMessages,
|
|
323
|
+
truncations,
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
emitPromptBudgetEvent(result);
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
let items = buildPromptBudgetItems(input.messages);
|
|
330
|
+
for (const priority of [8, 7, 6, 5, 4, 3]) {
|
|
331
|
+
if (totalItemTokens(items) <= budget.inputTokenLimit)
|
|
332
|
+
break;
|
|
333
|
+
if (priority === 4) {
|
|
334
|
+
items = items.map((item) => {
|
|
335
|
+
if (item.priority !== 4 || item.compacted)
|
|
336
|
+
return item;
|
|
337
|
+
const beforeTokens = itemTokens(item);
|
|
338
|
+
const compacted = compactSenseContextItem(item);
|
|
339
|
+
const afterTokens = itemTokens(compacted);
|
|
340
|
+
if (afterTokens < beforeTokens) {
|
|
341
|
+
truncations.push(truncationFor(item, "compacted message excerpts to source refs", beforeTokens, afterTokens));
|
|
342
|
+
}
|
|
343
|
+
return compacted;
|
|
344
|
+
});
|
|
345
|
+
if (totalItemTokens(items) <= budget.inputTokenLimit)
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
for (let itemIndex = 0; itemIndex < items.length && totalItemTokens(items) > budget.inputTokenLimit; itemIndex += 1) {
|
|
349
|
+
const item = items[itemIndex];
|
|
350
|
+
if (item.priority !== priority)
|
|
351
|
+
continue;
|
|
352
|
+
const beforeTokens = itemTokens(item);
|
|
353
|
+
droppedMessages += item.originalMessageCount;
|
|
354
|
+
truncations.push(truncationFor(item, "dropped to fit prompt budget", beforeTokens, 0));
|
|
355
|
+
items = items.filter((_, index) => index !== itemIndex);
|
|
356
|
+
itemIndex -= 1;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const messages = flattenItems(items);
|
|
360
|
+
const estimatedAfterTokens = estimatePromptBudgetTokens(messages);
|
|
361
|
+
const status = estimatedAfterTokens <= budget.inputTokenLimit
|
|
362
|
+
? "trimmed"
|
|
363
|
+
: "over_budget";
|
|
364
|
+
const result = {
|
|
365
|
+
policyVersion: exports.PROMPT_BUDGET_POLICY_VERSION,
|
|
366
|
+
status,
|
|
367
|
+
messages,
|
|
368
|
+
budget,
|
|
369
|
+
stats: {
|
|
370
|
+
estimatedBeforeTokens,
|
|
371
|
+
estimatedAfterTokens,
|
|
372
|
+
originalMessages: input.messages.length,
|
|
373
|
+
finalMessages: messages.length,
|
|
374
|
+
droppedMessages,
|
|
375
|
+
truncations,
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
emitPromptBudgetEvent(result);
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
@@ -26,6 +26,7 @@ const tools_obligations_1 = require("./tools-obligations");
|
|
|
26
26
|
const tools_evolution_1 = require("./tools-evolution");
|
|
27
27
|
const tools_runtime_1 = require("./tools-runtime");
|
|
28
28
|
const tools_orientation_1 = require("./tools-orientation");
|
|
29
|
+
const tools_rsvp_1 = require("./tools-rsvp");
|
|
29
30
|
// Re-export flow tools for consumers that import them from tools-base
|
|
30
31
|
var tools_flow_1 = require("./tools-flow");
|
|
31
32
|
Object.defineProperty(exports, "ponderTool", { enumerable: true, get: function () { return tools_flow_1.ponderTool; } });
|
|
@@ -67,6 +68,7 @@ exports.baseToolDefinitions = [
|
|
|
67
68
|
...tools_evolution_1.evolutionToolDefinitions,
|
|
68
69
|
...tools_orientation_1.orientationToolDefinitions,
|
|
69
70
|
...tools_runtime_1.runtimeToolDefinitions,
|
|
71
|
+
...tools_rsvp_1.rsvpToolDefinitions,
|
|
70
72
|
];
|
|
71
73
|
// Convenience array of just the tool schemas (no handler/integration metadata).
|
|
72
74
|
// Used by consumers that need the OpenAI function-tool format.
|
|
@@ -39,6 +39,7 @@ const identity_1 = require("../heart/identity");
|
|
|
39
39
|
const runtime_1 = require("../nerves/runtime");
|
|
40
40
|
const json_store_1 = require("../arc/json-store");
|
|
41
41
|
const obligations_1 = require("../arc/obligations");
|
|
42
|
+
const packets_1 = require("../arc/packets");
|
|
42
43
|
/**
|
|
43
44
|
* `let_go` lets the agent release a held work item from its attention loop.
|
|
44
45
|
*
|
|
@@ -61,6 +62,40 @@ function outerObligationsDir(agentRoot) {
|
|
|
61
62
|
function readOuterObligation(agentRoot, id) {
|
|
62
63
|
return (0, json_store_1.readJsonFile)(outerObligationsDir(agentRoot), id) ?? null;
|
|
63
64
|
}
|
|
65
|
+
function completeRelatedPacket(agentRoot, packet, relation) {
|
|
66
|
+
try {
|
|
67
|
+
(0, packets_1.completePonderPacket)(agentRoot, packet.id);
|
|
68
|
+
(0, runtime_1.emitNervesEvent)({
|
|
69
|
+
component: "repertoire",
|
|
70
|
+
event: "repertoire.packet_completed_for_released_obligation",
|
|
71
|
+
message: "linked packet completed for released return obligation",
|
|
72
|
+
meta: { ...relation, packetId: packet.id },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
(0, runtime_1.emitNervesEvent)({
|
|
77
|
+
level: "warn",
|
|
78
|
+
component: "repertoire",
|
|
79
|
+
event: "repertoire.packet_completion_for_released_obligation_failed",
|
|
80
|
+
message: "failed to complete linked packet for released return obligation",
|
|
81
|
+
meta: {
|
|
82
|
+
...relation,
|
|
83
|
+
packetId: packet.id,
|
|
84
|
+
error: error instanceof Error ? error.message : String(error),
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function completePacketsForReturnObligation(agentRoot, returnObligationId) {
|
|
90
|
+
for (const packet of (0, packets_1.listPonderPacketsByRelatedReturnObligationId)(agentRoot, returnObligationId)) {
|
|
91
|
+
completeRelatedPacket(agentRoot, packet, { returnObligationId });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function completePacketsForObligation(agentRoot, obligationId) {
|
|
95
|
+
for (const packet of (0, packets_1.listPonderPacketsByRelatedObligationId)(agentRoot, obligationId)) {
|
|
96
|
+
completeRelatedPacket(agentRoot, packet, { obligationId });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
64
99
|
function letGo(args, agentRoot, agentName) {
|
|
65
100
|
if (typeof args.id !== "string" || args.id.trim().length === 0) {
|
|
66
101
|
return JSON.stringify({ error: "id is required" });
|
|
@@ -73,6 +108,7 @@ function letGo(args, agentRoot, agentName) {
|
|
|
73
108
|
const ret = (0, obligations_1.readReturnObligation)(agentName, id);
|
|
74
109
|
if (ret) {
|
|
75
110
|
if (ret.status === "returned" || ret.status === "deferred") {
|
|
111
|
+
completePacketsForReturnObligation(agentRoot, id);
|
|
76
112
|
return JSON.stringify({ kind: "return_obligation", id, already: ret.status });
|
|
77
113
|
}
|
|
78
114
|
(0, obligations_1.advanceReturnObligation)(agentName, id, {
|
|
@@ -80,6 +116,7 @@ function letGo(args, agentRoot, agentName) {
|
|
|
80
116
|
returnedAt: Date.now(),
|
|
81
117
|
returnTarget: "surface",
|
|
82
118
|
});
|
|
119
|
+
completePacketsForReturnObligation(agentRoot, id);
|
|
83
120
|
(0, runtime_1.emitNervesEvent)({
|
|
84
121
|
component: "repertoire",
|
|
85
122
|
event: "repertoire.obligation_let_go",
|
|
@@ -92,9 +129,11 @@ function letGo(args, agentRoot, agentName) {
|
|
|
92
129
|
const outer = readOuterObligation(agentRoot, id);
|
|
93
130
|
if (outer) {
|
|
94
131
|
if (outer.status === "fulfilled") {
|
|
132
|
+
completePacketsForObligation(agentRoot, id);
|
|
95
133
|
return JSON.stringify({ kind: "obligation", id, already: "fulfilled" });
|
|
96
134
|
}
|
|
97
135
|
(0, obligations_1.fulfillObligation)(agentRoot, id);
|
|
136
|
+
completePacketsForObligation(agentRoot, id);
|
|
98
137
|
if (reason !== null) {
|
|
99
138
|
// Persist the dismissal reason as the obligation's latestNote so future-me
|
|
100
139
|
// can read why this was released (the nerves event also captures it).
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.rsvpToolDefinitions = void 0;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const runtime_1 = require("../nerves/runtime");
|
|
39
|
+
const query_1 = require("../rsvp/query");
|
|
40
|
+
const snapshot_1 = require("../rsvp/snapshot");
|
|
41
|
+
function readSnapshot(snapshotPath) {
|
|
42
|
+
if (!snapshotPath || snapshotPath.trim().length === 0) {
|
|
43
|
+
return { ok: false, reason: "missing", message: "RSVP snapshot not found." };
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const raw = fs.readFileSync(snapshotPath, "utf-8");
|
|
47
|
+
const parsed = (0, snapshot_1.parseRsvpSnapshot)(JSON.parse(raw));
|
|
48
|
+
if (!parsed.ok) {
|
|
49
|
+
return { ok: false, reason: "invalid", message: "RSVP snapshot could not be read or failed integrity validation." };
|
|
50
|
+
}
|
|
51
|
+
return { ok: true, snapshot: parsed.snapshot };
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
55
|
+
return { ok: false, reason: "missing", message: "RSVP snapshot not found." };
|
|
56
|
+
}
|
|
57
|
+
return { ok: false, reason: "invalid", message: "RSVP snapshot could not be read or failed integrity validation." };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function emitToolRead(toolName, result) {
|
|
61
|
+
(0, runtime_1.emitNervesEvent)({
|
|
62
|
+
component: "repertoire",
|
|
63
|
+
event: "repertoire.rsvp_tool_read",
|
|
64
|
+
message: "RSVP tool read snapshot",
|
|
65
|
+
meta: result.ok
|
|
66
|
+
? { toolName, snapshotId: result.snapshot.snapshotId, total: result.snapshot.summary.total }
|
|
67
|
+
: { toolName, reason: result.reason },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
exports.rsvpToolDefinitions = [
|
|
71
|
+
{
|
|
72
|
+
tool: {
|
|
73
|
+
type: "function",
|
|
74
|
+
function: {
|
|
75
|
+
name: "rsvp_query",
|
|
76
|
+
description: "answer deterministic questions about a native RSVP snapshot file, such as who is pending, declined, attending, unknown, or matching a guest name.",
|
|
77
|
+
parameters: {
|
|
78
|
+
type: "object",
|
|
79
|
+
properties: {
|
|
80
|
+
snapshot_path: {
|
|
81
|
+
type: "string",
|
|
82
|
+
description: "absolute path to a native RSVP snapshot JSON file.",
|
|
83
|
+
},
|
|
84
|
+
query: {
|
|
85
|
+
type: "string",
|
|
86
|
+
description: "natural-language query or guest-name filter.",
|
|
87
|
+
},
|
|
88
|
+
status: {
|
|
89
|
+
type: "string",
|
|
90
|
+
enum: ["attending", "declined", "pending", "unknown", "all"],
|
|
91
|
+
description: "optional explicit RSVP status filter.",
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
required: ["snapshot_path"],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
handler: async (args) => {
|
|
99
|
+
const result = readSnapshot(args.snapshot_path);
|
|
100
|
+
emitToolRead("rsvp_query", result);
|
|
101
|
+
if (!result.ok)
|
|
102
|
+
return result.message;
|
|
103
|
+
return (0, query_1.queryRsvpSnapshot)(result.snapshot, {
|
|
104
|
+
query: args.query,
|
|
105
|
+
status: args.status,
|
|
106
|
+
}).text;
|
|
107
|
+
},
|
|
108
|
+
summaryKeys: ["snapshot_path", "query", "status"],
|
|
109
|
+
riskProfile: { mutates: "none", risk: "low", reason: "reads a local native RSVP snapshot" },
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
tool: {
|
|
113
|
+
type: "function",
|
|
114
|
+
function: {
|
|
115
|
+
name: "rsvp_summary",
|
|
116
|
+
description: "summarize counts and provenance for a native RSVP snapshot file.",
|
|
117
|
+
parameters: {
|
|
118
|
+
type: "object",
|
|
119
|
+
properties: {
|
|
120
|
+
snapshot_path: {
|
|
121
|
+
type: "string",
|
|
122
|
+
description: "absolute path to a native RSVP snapshot JSON file.",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
required: ["snapshot_path"],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
handler: async (args) => {
|
|
130
|
+
const result = readSnapshot(args.snapshot_path);
|
|
131
|
+
emitToolRead("rsvp_summary", result);
|
|
132
|
+
if (!result.ok)
|
|
133
|
+
return result.message;
|
|
134
|
+
return (0, query_1.summarizeRsvpSnapshot)(result.snapshot);
|
|
135
|
+
},
|
|
136
|
+
summaryKeys: ["snapshot_path"],
|
|
137
|
+
riskProfile: { mutates: "none", risk: "low", reason: "reads a local native RSVP snapshot" },
|
|
138
|
+
},
|
|
139
|
+
];
|