@ouro.bot/cli 0.1.0-alpha.696 → 0.1.0-alpha.698
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 +18 -0
- package/dist/heart/agent-entry.js +32 -2
- 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-entry.js +104 -3
- 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/provider-credentials.js +16 -0
- package/dist/heart/run-ledger.js +247 -0
- package/dist/heart/runtime-credentials.js +12 -3
- package/dist/mind/prompt-budget.js +380 -0
- package/dist/repertoire/tools-base.js +2 -0
- package/dist/repertoire/tools-rsvp.js +139 -0
- package/dist/rsvp/aisleplanner-client.js +354 -0
- package/dist/rsvp/cli.js +704 -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 +161 -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 +91 -1
- 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
|
@@ -0,0 +1,247 @@
|
|
|
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.runLedgerHash = runLedgerHash;
|
|
37
|
+
exports.usageMetadataFromUsageData = usageMetadataFromUsageData;
|
|
38
|
+
exports.deriveRunLedgerIds = deriveRunLedgerIds;
|
|
39
|
+
exports.createRunLedgerRecord = createRunLedgerRecord;
|
|
40
|
+
exports.runLedgerPath = runLedgerPath;
|
|
41
|
+
exports.appendRunLedgerRecord = appendRunLedgerRecord;
|
|
42
|
+
exports.appendRunLedgerRecordNonFatal = appendRunLedgerRecordNonFatal;
|
|
43
|
+
exports.readRunLedger = readRunLedger;
|
|
44
|
+
const node_crypto_1 = require("node:crypto");
|
|
45
|
+
const fs = __importStar(require("node:fs"));
|
|
46
|
+
const path = __importStar(require("node:path"));
|
|
47
|
+
const runtime_1 = require("../nerves/runtime");
|
|
48
|
+
function sha256Hex(input) {
|
|
49
|
+
return (0, node_crypto_1.createHash)("sha256").update(input).digest("hex");
|
|
50
|
+
}
|
|
51
|
+
function canonicalJson(value) {
|
|
52
|
+
if (Array.isArray(value)) {
|
|
53
|
+
return `[${value.map((entry) => canonicalJson(entry)).join(",")}]`;
|
|
54
|
+
}
|
|
55
|
+
if (value && typeof value === "object") {
|
|
56
|
+
const record = value;
|
|
57
|
+
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
58
|
+
}
|
|
59
|
+
return JSON.stringify(value);
|
|
60
|
+
}
|
|
61
|
+
function hashValue(value) {
|
|
62
|
+
return sha256Hex(canonicalJson(value));
|
|
63
|
+
}
|
|
64
|
+
function runLedgerHash(value) {
|
|
65
|
+
return `sha256:${hashValue(value)}`;
|
|
66
|
+
}
|
|
67
|
+
function stableUnique(values) {
|
|
68
|
+
return [...new Set((values ?? []).filter((value) => typeof value === "string" && value.trim().length > 0))];
|
|
69
|
+
}
|
|
70
|
+
function numberOrZero(value) {
|
|
71
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
72
|
+
}
|
|
73
|
+
function usageMetadataFromUsageData(usage, source) {
|
|
74
|
+
return {
|
|
75
|
+
source,
|
|
76
|
+
inputTokens: numberOrZero(usage?.input_tokens),
|
|
77
|
+
outputTokens: numberOrZero(usage?.output_tokens),
|
|
78
|
+
reasoningTokens: numberOrZero(usage?.reasoning_tokens),
|
|
79
|
+
totalTokens: numberOrZero(usage?.total_tokens),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function deriveRunLedgerIds(input) {
|
|
83
|
+
const targetHash = runLedgerHash(input.target);
|
|
84
|
+
const idempotencyKey = `idem_${hashValue({
|
|
85
|
+
agent: input.agent,
|
|
86
|
+
triggerType: input.triggerType,
|
|
87
|
+
sourceKind: input.sourceKind,
|
|
88
|
+
senseOrHabit: input.senseOrHabit,
|
|
89
|
+
scope: input.idempotencyScope ?? input.target,
|
|
90
|
+
}).slice(0, 32)}`;
|
|
91
|
+
const runId = `run_${hashValue({
|
|
92
|
+
agent: input.agent,
|
|
93
|
+
triggerType: input.triggerType,
|
|
94
|
+
sourceKind: input.sourceKind,
|
|
95
|
+
senseOrHabit: input.senseOrHabit,
|
|
96
|
+
targetHash,
|
|
97
|
+
idempotencyKey,
|
|
98
|
+
parentRunId: input.parentRunId ?? null,
|
|
99
|
+
}).slice(0, 24)}`;
|
|
100
|
+
return {
|
|
101
|
+
runId,
|
|
102
|
+
rootRunId: input.rootRunId ?? input.parentRunId ?? runId,
|
|
103
|
+
...(input.parentRunId ? { parentRunId: input.parentRunId } : {}),
|
|
104
|
+
idempotencyKey,
|
|
105
|
+
targetHash,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function createRunLedgerRecord(input) {
|
|
109
|
+
const ids = deriveRunLedgerIds(input);
|
|
110
|
+
return {
|
|
111
|
+
schemaVersion: 1,
|
|
112
|
+
recordedAt: new Date().toISOString(),
|
|
113
|
+
...ids,
|
|
114
|
+
agent: input.agent,
|
|
115
|
+
triggerType: input.triggerType,
|
|
116
|
+
sourceKind: input.sourceKind,
|
|
117
|
+
senseOrHabit: input.senseOrHabit,
|
|
118
|
+
lifecycle: input.lifecycle,
|
|
119
|
+
startedAt: input.startedAt,
|
|
120
|
+
...(input.endedAt ? { endedAt: input.endedAt } : {}),
|
|
121
|
+
...(input.usage ? { usage: input.usage } : {}),
|
|
122
|
+
...(input.provider ? { provider: input.provider } : {}),
|
|
123
|
+
...(input.model ? { model: input.model } : {}),
|
|
124
|
+
...(input.sessionRef ? { sessionRef: input.sessionRef } : {}),
|
|
125
|
+
contextPacketIds: stableUnique(input.contextPacketIds),
|
|
126
|
+
contentStored: false,
|
|
127
|
+
...(input.errorName ? { errorName: input.errorName } : {}),
|
|
128
|
+
...(input.errorCode ? { errorCode: input.errorCode } : {}),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function runLedgerPath(agentRoot) {
|
|
132
|
+
return path.join(agentRoot, "state", "run-ledger", "runs.jsonl");
|
|
133
|
+
}
|
|
134
|
+
function appendRunLedgerRecord(agentRoot, record) {
|
|
135
|
+
const filePath = runLedgerPath(agentRoot);
|
|
136
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
137
|
+
fs.appendFileSync(filePath, `${JSON.stringify(record)}\n`, "utf-8");
|
|
138
|
+
(0, runtime_1.emitNervesEvent)({
|
|
139
|
+
component: "heart",
|
|
140
|
+
event: "heart.run_ledger_recorded",
|
|
141
|
+
message: "recorded causal run ledger row",
|
|
142
|
+
meta: {
|
|
143
|
+
agent: record.agent,
|
|
144
|
+
runId: record.runId,
|
|
145
|
+
triggerType: record.triggerType,
|
|
146
|
+
sourceKind: record.sourceKind,
|
|
147
|
+
lifecycle: record.lifecycle,
|
|
148
|
+
senseOrHabit: record.senseOrHabit,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function appendRunLedgerRecordNonFatal(agentRoot, record) {
|
|
153
|
+
try {
|
|
154
|
+
appendRunLedgerRecord(agentRoot, record);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
(0, runtime_1.emitNervesEvent)({
|
|
159
|
+
level: "error",
|
|
160
|
+
component: "heart",
|
|
161
|
+
event: "heart.run_ledger_record_error",
|
|
162
|
+
message: "failed to record causal run ledger row",
|
|
163
|
+
meta: {
|
|
164
|
+
agent: record.agent,
|
|
165
|
+
runId: record.runId,
|
|
166
|
+
lifecycle: record.lifecycle,
|
|
167
|
+
error: String(error),
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function isStringArray(value) {
|
|
174
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
175
|
+
}
|
|
176
|
+
function isUsageMetadata(value) {
|
|
177
|
+
const usage = value;
|
|
178
|
+
return !!usage
|
|
179
|
+
&& (usage.source === "provider" || usage.source === "none" || usage.source === "reported-unavailable")
|
|
180
|
+
&& typeof usage.inputTokens === "number"
|
|
181
|
+
&& typeof usage.outputTokens === "number"
|
|
182
|
+
&& typeof usage.reasoningTokens === "number"
|
|
183
|
+
&& typeof usage.totalTokens === "number";
|
|
184
|
+
}
|
|
185
|
+
function isSessionRef(value) {
|
|
186
|
+
const ref = value;
|
|
187
|
+
return !!ref
|
|
188
|
+
&& typeof ref.channel === "string"
|
|
189
|
+
&& typeof ref.keyHash === "string";
|
|
190
|
+
}
|
|
191
|
+
function isRunLedgerRecord(value) {
|
|
192
|
+
const record = value;
|
|
193
|
+
return !!record
|
|
194
|
+
&& record.schemaVersion === 1
|
|
195
|
+
&& typeof record.recordedAt === "string"
|
|
196
|
+
&& typeof record.runId === "string"
|
|
197
|
+
&& typeof record.rootRunId === "string"
|
|
198
|
+
&& (record.parentRunId === undefined || typeof record.parentRunId === "string")
|
|
199
|
+
&& typeof record.idempotencyKey === "string"
|
|
200
|
+
&& typeof record.agent === "string"
|
|
201
|
+
&& (record.triggerType === "inbound" || record.triggerType === "habit" || record.triggerType === "reload" || record.triggerType === "recovery" || record.triggerType === "manual")
|
|
202
|
+
&& (record.sourceKind === "sense" || record.sourceKind === "private-runtime" || record.sourceKind === "daemon" || record.sourceKind === "cli")
|
|
203
|
+
&& typeof record.senseOrHabit === "string"
|
|
204
|
+
&& typeof record.targetHash === "string"
|
|
205
|
+
&& (record.lifecycle === "started" || record.lifecycle === "completed" || record.lifecycle === "skipped" || record.lifecycle === "blocked" || record.lifecycle === "error")
|
|
206
|
+
&& typeof record.startedAt === "string"
|
|
207
|
+
&& (record.endedAt === undefined || typeof record.endedAt === "string")
|
|
208
|
+
&& (record.usage === undefined || isUsageMetadata(record.usage))
|
|
209
|
+
&& (record.provider === undefined || typeof record.provider === "string")
|
|
210
|
+
&& (record.model === undefined || typeof record.model === "string")
|
|
211
|
+
&& (record.sessionRef === undefined || isSessionRef(record.sessionRef))
|
|
212
|
+
&& isStringArray(record.contextPacketIds)
|
|
213
|
+
&& record.contentStored === false
|
|
214
|
+
&& (record.errorName === undefined || typeof record.errorName === "string")
|
|
215
|
+
&& (record.errorCode === undefined || typeof record.errorCode === "string");
|
|
216
|
+
}
|
|
217
|
+
function emitMalformedRunLedgerRow(lineNumber, reason) {
|
|
218
|
+
(0, runtime_1.emitNervesEvent)({
|
|
219
|
+
level: "warn",
|
|
220
|
+
component: "heart",
|
|
221
|
+
event: "heart.run_ledger_malformed",
|
|
222
|
+
message: "skipped malformed causal run ledger row",
|
|
223
|
+
meta: { lineNumber, reason },
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
function readRunLedger(agentRoot) {
|
|
227
|
+
const filePath = runLedgerPath(agentRoot);
|
|
228
|
+
if (!fs.existsSync(filePath))
|
|
229
|
+
return [];
|
|
230
|
+
const rows = [];
|
|
231
|
+
const lines = fs.readFileSync(filePath, "utf-8").split("\n").filter((line) => line.trim().length > 0);
|
|
232
|
+
lines.forEach((line, index) => {
|
|
233
|
+
try {
|
|
234
|
+
const parsed = JSON.parse(line);
|
|
235
|
+
if (isRunLedgerRecord(parsed)) {
|
|
236
|
+
rows.push(parsed);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
emitMalformedRunLedgerRow(index + 1, "invalid shape");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
emitMalformedRunLedgerRow(index + 1, String(error));
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
return rows;
|
|
247
|
+
}
|
|
@@ -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();
|
|
@@ -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.
|