@ouro.bot/cli 0.1.0-alpha.732 → 0.1.0-alpha.734
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/README.md +1 -1
- package/changelog.json +34 -0
- package/deploy/unraid/Dockerfile +27 -0
- package/deploy/unraid/README.txt +1368 -0
- package/deploy/unraid/audit-container-spec.sh +4 -0
- package/deploy/unraid/container-runtime.json +1 -0
- package/deploy/unraid/sanctuary-acceptance-adapter.sh +37 -0
- package/deploy/unraid/sanctuary-acceptance-contract.json +200 -0
- package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
- package/deploy/unraid/sanctuary-deployment-target.mjs +705 -0
- package/deploy/unraid/sanctuary-safe-rename.py +24 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +1777 -0
- package/deploy/unraid/sanctuary-unit16-run.sh +502 -0
- package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
- package/deploy/unraid/sanctuary.ouro/agent.json +24 -0
- package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
- package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
- package/deploy/unraid/sanctuary.ouro/provider-readiness.json +18 -0
- package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +5 -0
- package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +7 -0
- package/deploy/unraid/sanctuary.ouro/tool-profiles.json +21 -0
- package/deploy/unraid/sanctuary.xml +25 -0
- package/dist/heart/approval-files.js +113 -0
- package/dist/heart/approval-store.js +168 -9
- package/dist/heart/auth/auth-flow.js +17 -1
- package/dist/heart/autonomy-budget.js +44 -1
- package/dist/heart/awaiting/await-scheduler.js +6 -2
- package/dist/heart/config.js +8 -0
- package/dist/heart/core.js +117 -132
- package/dist/heart/daemon/cli-exec.js +74 -2
- package/dist/heart/daemon/cli-help.js +6 -1
- package/dist/heart/daemon/cli-parse.js +6 -4
- package/dist/heart/daemon/container-credential-bootstrap.js +185 -0
- package/dist/heart/daemon/container-healthcheck.js +94 -0
- package/dist/heart/daemon/container-runtime.js +84 -0
- package/dist/heart/daemon/container-spec-auditor-main.js +156 -0
- package/dist/heart/daemon/container-spec-auditor.js +156 -0
- package/dist/heart/daemon/daemon-bootstrap-startup.js +41 -0
- package/dist/heart/daemon/daemon-entry.js +116 -28
- package/dist/heart/daemon/daemon-health.js +10 -0
- package/dist/heart/daemon/daemon.js +92 -5
- package/dist/heart/daemon/doctor.js +56 -7
- package/dist/heart/daemon/runtime-logging.js +6 -0
- package/dist/heart/daemon/runtime-mode.js +13 -2
- package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2365 -0
- package/dist/heart/daemon/sanctuary-acceptance-harness.js +1624 -0
- package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
- package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +985 -0
- package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
- package/dist/heart/daemon/sanctuary-scheduler-origin.js +126 -0
- package/dist/heart/daemon/sense-manager.js +25 -6
- package/dist/heart/daemon/supercronic-supervisor.js +267 -0
- package/dist/heart/habits/habit-scheduler.js +7 -3
- package/dist/heart/identity.js +13 -3
- package/dist/heart/provider-credentials.js +9 -1
- package/dist/heart/provider-models.js +8 -0
- package/dist/heart/provider-ping.js +5 -2
- package/dist/heart/providers/openai-compatible.js +177 -0
- package/dist/heart/runtime-credentials.js +217 -8
- package/dist/heart/sense-truth.js +2 -0
- package/dist/heart/tool-approval.js +1 -0
- package/dist/heart/turn-context.js +8 -0
- package/dist/mailroom/blob-store.js +97 -19
- package/dist/mailroom/cache-sync-cli.js +5 -1
- package/dist/mailroom/core.js +23 -8
- package/dist/mailroom/hosted-cache-sync.js +179 -44
- package/dist/mailroom/search-cache.js +109 -0
- package/dist/mind/prompt.js +9 -0
- package/dist/nerves/coverage/source-scanner.js +4 -4
- package/dist/nerves/index.js +75 -9
- package/dist/nerves/runtime.js +12 -0
- package/dist/repertoire/tool-arguments.js +39 -0
- package/dist/repertoire/tools-unraid.js +353 -0
- package/dist/repertoire/tools.js +19 -1
- package/dist/repertoire/unraid-client.js +155 -0
- package/dist/repertoire/unraid-restart.js +130 -0
- package/dist/senses/private-runtime-worker.js +3 -2
- package/dist/senses/sanctuary-grounding.js +118 -0
- package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
- package/dist/senses/sanctuary-health-acceptance-probe.js +772 -0
- package/dist/senses/sanctuary-health-runner.js +146 -0
- package/dist/senses/sanctuary-health.js +447 -0
- package/dist/senses/sanctuary-interactive-control.js +306 -0
- package/dist/senses/sanctuary-runtime.js +206 -0
- package/dist/senses/shared-turn.js +11 -3
- package/dist/senses/telegram-approval-runtime.js +526 -0
- package/dist/senses/telegram-audit-ledger.js +220 -0
- package/dist/senses/telegram-client.js +1471 -0
- package/dist/senses/telegram-entry.js +80 -0
- package/dist/senses/telegram.js +1232 -0
- package/npm-shrinkwrap.json +6558 -0
- package/package.json +4 -2
package/dist/heart/core.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_PROVIDER_ITERATIONS = void 0;
|
|
4
|
+
exports.validateToolCallBatchAtProductionBoundary = validateToolCallBatchAtProductionBoundary;
|
|
3
5
|
exports.createProviderRegistry = createProviderRegistry;
|
|
4
6
|
exports.resetProviderRuntime = resetProviderRuntime;
|
|
5
7
|
exports.getModel = getModel;
|
|
@@ -24,6 +26,20 @@ const friends_1 = require("@ouro.bot/friends");
|
|
|
24
26
|
const tools_2 = require("../repertoire/tools");
|
|
25
27
|
const streaming_1 = require("./streaming");
|
|
26
28
|
const runtime_1 = require("../nerves/runtime");
|
|
29
|
+
function validateToolCallBatchAtProductionBoundary(calls, activeTools) {
|
|
30
|
+
const callIdCounts = new Map();
|
|
31
|
+
for (const call of calls)
|
|
32
|
+
callIdCounts.set(call.id, (callIdCounts.get(call.id) ?? 0) + 1);
|
|
33
|
+
return calls.map((call) => {
|
|
34
|
+
if (callIdCounts.get(call.id) !== 1)
|
|
35
|
+
return { call, error: "duplicate tool call id" };
|
|
36
|
+
const advertised = activeTools.find((tool) => tool.function.name === call.name);
|
|
37
|
+
if (!advertised || !advertised.function.parameters || typeof advertised.function.parameters !== "object")
|
|
38
|
+
return { call, error: "tool was not advertised with a valid argument schema" };
|
|
39
|
+
const validated = (0, tool_arguments_1.validateAdvertisedToolArguments)(call.arguments, advertised.function.parameters);
|
|
40
|
+
return validated.ok ? { call, advertised, validated: validated.value } : { call, error: validated.reason };
|
|
41
|
+
});
|
|
42
|
+
}
|
|
27
43
|
const context_1 = require("../mind/context");
|
|
28
44
|
const prompt_budget_1 = require("../mind/prompt-budget");
|
|
29
45
|
const prompt_1 = require("../mind/prompt");
|
|
@@ -34,6 +50,7 @@ const azure_1 = require("./providers/azure");
|
|
|
34
50
|
const minimax_1 = require("./providers/minimax");
|
|
35
51
|
const openai_codex_1 = require("./providers/openai-codex");
|
|
36
52
|
const github_copilot_1 = require("./providers/github-copilot");
|
|
53
|
+
const openai_compatible_1 = require("./providers/openai-compatible");
|
|
37
54
|
const orientation_frame_1 = require("./orientation-frame");
|
|
38
55
|
const identity_2 = require("./identity");
|
|
39
56
|
const socket_client_1 = require("./daemon/socket-client");
|
|
@@ -109,6 +126,9 @@ function createProviderRegistry() {
|
|
|
109
126
|
return (0, openai_codex_1.createOpenAICodexProviderRuntime)(model, providerConfig);
|
|
110
127
|
case "github-copilot":
|
|
111
128
|
return (0, github_copilot_1.createGithubCopilotProviderRuntime)(model, providerConfig);
|
|
129
|
+
case "openai-compatible":
|
|
130
|
+
case "openai-compatible-gemini":
|
|
131
|
+
return (0, openai_compatible_1.createOpenAICompatibleProviderRuntime)(provider, model, providerConfig);
|
|
112
132
|
}
|
|
113
133
|
},
|
|
114
134
|
};
|
|
@@ -198,6 +218,8 @@ function getProviderDisplayLabel(facing = "human") {
|
|
|
198
218
|
"openai-codex": () => `openai codex (${model})`,
|
|
199
219
|
/* v8 ignore next -- branch: tested via display label unit test @preserve */
|
|
200
220
|
"github-copilot": () => `github copilot (${model})`,
|
|
221
|
+
"openai-compatible": () => `z.ai openai-compatible (${model})`,
|
|
222
|
+
"openai-compatible-gemini": () => `gemini openai-compatible (${model})`,
|
|
201
223
|
};
|
|
202
224
|
return providerLabelBuilders[provider]();
|
|
203
225
|
}
|
|
@@ -247,6 +269,7 @@ function createHabitCallbackBuffer(callbacks) {
|
|
|
247
269
|
function createFinalOnlyTextBuffer(callbacks) {
|
|
248
270
|
return createCallbackBuffer(callbacks, "text-only");
|
|
249
271
|
}
|
|
272
|
+
exports.MAX_PROVIDER_ITERATIONS = 8;
|
|
250
273
|
function continuationCallbacks(callbacks, text) {
|
|
251
274
|
const noop = () => undefined;
|
|
252
275
|
return {
|
|
@@ -356,18 +379,6 @@ function hasFreshPendingWork(options) {
|
|
|
356
379
|
&& message.content.trim().length > 0);
|
|
357
380
|
}
|
|
358
381
|
const HABIT_CONTROL_TOOLS = new Set(["rest", "ponder", "observe"]);
|
|
359
|
-
function habitToolArgs(call) {
|
|
360
|
-
try {
|
|
361
|
-
const parsed = JSON.parse(call.arguments);
|
|
362
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
363
|
-
return { ok: false, reason: `habit tool '${call.name}' arguments must be a JSON object` };
|
|
364
|
-
}
|
|
365
|
-
return { ok: true, args: parsed };
|
|
366
|
-
}
|
|
367
|
-
catch {
|
|
368
|
-
return { ok: false, reason: `habit tool '${call.name}' has malformed JSON arguments` };
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
382
|
function highRiskExternalMutation(profile) {
|
|
372
383
|
if (profile.risk !== "high")
|
|
373
384
|
return null;
|
|
@@ -383,8 +394,8 @@ function recordBlockedHabitSurfaceAttempts(habitSession, toolCalls, reason) {
|
|
|
383
394
|
for (const call of toolCalls) {
|
|
384
395
|
if (call.name !== "send_message" && call.name !== "surface")
|
|
385
396
|
continue;
|
|
386
|
-
|
|
387
|
-
const args =
|
|
397
|
+
// The canonical pre-batch schema gate guarantees object arguments here.
|
|
398
|
+
const args = JSON.parse(call.arguments);
|
|
388
399
|
habitSession.recordSurfaceAttempt({
|
|
389
400
|
recipient: String(args.friendId ?? args.delegationId ?? "unknown"),
|
|
390
401
|
channel: String(args.channel ?? call.name),
|
|
@@ -395,10 +406,7 @@ function recordBlockedHabitSurfaceAttempts(habitSession, toolCalls, reason) {
|
|
|
395
406
|
});
|
|
396
407
|
}
|
|
397
408
|
}
|
|
398
|
-
async function habitToolBatchBlockReason(habitSession, toolCalls, delegatedOrigins
|
|
399
|
-
if (noSend && toolCalls.some((call) => call.name === "ponder")) {
|
|
400
|
-
return "habit tool 'ponder' cannot create continuations under immutable no-send authority";
|
|
401
|
-
}
|
|
409
|
+
async function habitToolBatchBlockReason(habitSession, toolCalls, delegatedOrigins) {
|
|
402
410
|
if (!habitSession)
|
|
403
411
|
return null;
|
|
404
412
|
const granted = new Set(habitSession.toolPolicy.grantedTools);
|
|
@@ -406,16 +414,13 @@ async function habitToolBatchBlockReason(habitSession, toolCalls, delegatedOrigi
|
|
|
406
414
|
for (const call of toolCalls) {
|
|
407
415
|
if (denied.has(call.name))
|
|
408
416
|
return `habit tool '${call.name}' is denied by this habit session`;
|
|
409
|
-
if (!activeToolNames.has(call.name))
|
|
410
|
-
return `habit tool '${call.name}' was not advertised to this model turn`;
|
|
411
417
|
if (HABIT_CONTROL_TOOLS.has(call.name))
|
|
412
418
|
continue;
|
|
413
419
|
if (!granted.has(call.name))
|
|
414
420
|
return `habit tool '${call.name}' was not granted to this habit session`;
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const riskProfile = (0, tools_1.riskProfileForToolName)(call.name, parsed.args);
|
|
421
|
+
// The canonical pre-batch schema gate guarantees object arguments here.
|
|
422
|
+
const args = JSON.parse(call.arguments);
|
|
423
|
+
const riskProfile = (0, tools_1.riskProfileForToolName)(call.name, args);
|
|
419
424
|
if (!riskProfile)
|
|
420
425
|
return `habit tool '${call.name}' does not have a known executable risk profile`;
|
|
421
426
|
const externalMutation = highRiskExternalMutation(riskProfile);
|
|
@@ -427,7 +432,7 @@ async function habitToolBatchBlockReason(habitSession, toolCalls, delegatedOrigi
|
|
|
427
432
|
agentRoot: (0, identity_2.getAgentRoot)(),
|
|
428
433
|
envelope: habitSession.permissionEnvelope,
|
|
429
434
|
toolName: call.name,
|
|
430
|
-
args
|
|
435
|
+
args,
|
|
431
436
|
friendStore: habitSession.friendStore,
|
|
432
437
|
delegatedOrigins,
|
|
433
438
|
});
|
|
@@ -441,7 +446,7 @@ async function habitToolBatchBlockReason(habitSession, toolCalls, delegatedOrigi
|
|
|
441
446
|
* BlueBubbles is final-only: its adapter owns one accepted visibility boundary.
|
|
442
447
|
* The private runtime has `ponder`; MCP returns synchronously; mail is batch. */
|
|
443
448
|
function isChatStyleChannel(channel) {
|
|
444
|
-
return
|
|
449
|
+
return (0, friends_1.getChannelCapabilities)(channel).chatStyle;
|
|
445
450
|
}
|
|
446
451
|
function bindCurrentIngressEvidenceLocator(toolList, evidence) {
|
|
447
452
|
if (!evidence
|
|
@@ -493,13 +498,8 @@ function parseSettlePayload(argumentsText) {
|
|
|
493
498
|
return { answer, intent };
|
|
494
499
|
}
|
|
495
500
|
function parsePonderPayload(argumentsText) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
return parsed && typeof parsed === "object" ? parsed : {};
|
|
499
|
-
}
|
|
500
|
-
catch {
|
|
501
|
-
return {};
|
|
502
|
-
}
|
|
501
|
+
// The canonical pre-batch schema gate guarantees a valid object payload.
|
|
502
|
+
return JSON.parse(argumentsText);
|
|
503
503
|
}
|
|
504
504
|
function parseSuccessCriteria(raw) {
|
|
505
505
|
if (typeof raw !== "string")
|
|
@@ -885,7 +885,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
885
885
|
generatedMessages.push(...structuredClone(next));
|
|
886
886
|
};
|
|
887
887
|
const facing = (0, friends_1.channelToFacing)(channel);
|
|
888
|
-
let providerRuntime = await getProviderRuntime(facing);
|
|
888
|
+
let providerRuntime = options?.providerRuntimeOverride ?? await getProviderRuntime(facing);
|
|
889
889
|
const provider = providerRuntime.id;
|
|
890
890
|
const toolChoiceRequired = options?.toolChoiceRequired ?? true;
|
|
891
891
|
const traceId = options?.traceId;
|
|
@@ -1039,6 +1039,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1039
1039
|
// a ponder packet created the return obligation in this turn.
|
|
1040
1040
|
let noToolCallRetries = 0;
|
|
1041
1041
|
const NO_TOOL_CALL_MAX_RETRIES = 2;
|
|
1042
|
+
let providerIterations = 0;
|
|
1042
1043
|
const toolLoopState = (0, tool_loop_1.createToolLoopState)();
|
|
1043
1044
|
const toolFrictionLedger = (0, tool_friction_1.createToolFrictionLedger)();
|
|
1044
1045
|
const finishTerminalProviderError = (error, classification) => {
|
|
@@ -1126,7 +1127,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1126
1127
|
const filteredBaseTools = isPrivateRuntimeChannel
|
|
1127
1128
|
? baseTools.filter((t) => privateRuntimeHabitCanSendMessage || t.function.name !== "send_message")
|
|
1128
1129
|
: baseTools;
|
|
1129
|
-
const
|
|
1130
|
+
const ordinaryActiveTools = [
|
|
1130
1131
|
...filteredBaseTools,
|
|
1131
1132
|
...(augmentedToolContext?.noSend === true ? [] : [tools_1.ponderTool]),
|
|
1132
1133
|
...(isPrivateRuntimeChannel && privateRuntimeHabitCanSurface ? [tools_2.surfaceToolDef] : []),
|
|
@@ -1135,6 +1136,15 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1135
1136
|
...(!isPrivateRuntimeChannel ? [tools_1.settleTool] : []),
|
|
1136
1137
|
...(isChatStyleChannel(channel ?? "") ? [tools_1.speakTool] : []),
|
|
1137
1138
|
];
|
|
1139
|
+
const activeTools = options?.toolProfile === "sanctuary-health-private"
|
|
1140
|
+
? (() => {
|
|
1141
|
+
const sendTools = baseTools.filter((tool) => tool.function.name === "send_message");
|
|
1142
|
+
if (channel !== "inner" || sendTools.length !== 1 || baseTools.length !== 1) {
|
|
1143
|
+
throw new Error("sanctuary-health-private requires inner channel with exactly one canonical send_message definition");
|
|
1144
|
+
}
|
|
1145
|
+
return [sendTools[0], tools_1.restTool];
|
|
1146
|
+
})()
|
|
1147
|
+
: ordinaryActiveTools;
|
|
1138
1148
|
const activeToolNames = new Set(activeTools.map((tool) => tool.function.name));
|
|
1139
1149
|
const steeringFollowUps = options?.drainSteeringFollowUps?.() ?? [];
|
|
1140
1150
|
if (steeringFollowUps.length > 0) {
|
|
@@ -1278,6 +1288,10 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1278
1288
|
continue;
|
|
1279
1289
|
}
|
|
1280
1290
|
const result = attempt.value;
|
|
1291
|
+
providerIterations += 1;
|
|
1292
|
+
if (providerIterations === exports.MAX_PROVIDER_ITERATIONS && result.toolCalls.length > 0) {
|
|
1293
|
+
throw new Error(`provider iteration limit exhausted at response ${exports.MAX_PROVIDER_ITERATIONS} before tool execution`);
|
|
1294
|
+
}
|
|
1281
1295
|
const streamCallbackBuffer = turnCallbackBufferRef.current;
|
|
1282
1296
|
turnCallbackBufferRef.current = null;
|
|
1283
1297
|
if (result.settleFinalization && !result.settleFinalization.ok) {
|
|
@@ -1454,7 +1468,54 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1454
1468
|
else {
|
|
1455
1469
|
// Reset the retry counter on any successful tool call.
|
|
1456
1470
|
noToolCallRetries = 0;
|
|
1457
|
-
const
|
|
1471
|
+
const preCallMessages = structuredClone(messages.filter((message) => message.role !== "system"));
|
|
1472
|
+
const validatedCalls = validateToolCallBatchAtProductionBoundary(result.toolCalls, activeTools);
|
|
1473
|
+
const invalidCall = validatedCalls.find((entry) => "error" in entry);
|
|
1474
|
+
if (invalidCall) {
|
|
1475
|
+
await streamCallbackBuffer?.flush();
|
|
1476
|
+
pushGenerated(msg);
|
|
1477
|
+
const unadvertisedCall = validatedCalls.find((entry) => !activeToolNames.has(entry.call.name));
|
|
1478
|
+
for (const entry of validatedCalls) {
|
|
1479
|
+
const detail = "error" in entry ? entry.error : "another call in this batch had invalid arguments";
|
|
1480
|
+
const rejection = unadvertisedCall
|
|
1481
|
+
? `rejected: ${entry.call.name} was not advertised for this channel; no handler was executed.`
|
|
1482
|
+
: `invalid tool arguments: ${detail}`;
|
|
1483
|
+
pushGenerated({ role: "tool", tool_call_id: entry.call.id, content: rejection });
|
|
1484
|
+
providerRuntime.appendToolOutput(entry.call.id, rejection);
|
|
1485
|
+
options?.toolBoundaryObserver?.({
|
|
1486
|
+
name: entry.call.name,
|
|
1487
|
+
reason: activeToolNames.has(entry.call.name) ? "invalid_arguments" : "profile_excluded",
|
|
1488
|
+
globallyResolvable: typeof (0, tools_1.resolveToolDefinition)(entry.call.name)?.handler === "function",
|
|
1489
|
+
invoked: false,
|
|
1490
|
+
sideEffect: false,
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
if (unadvertisedCall) {
|
|
1494
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1495
|
+
level: "warn",
|
|
1496
|
+
component: "engine",
|
|
1497
|
+
event: "engine.unadvertised_tool_blocked",
|
|
1498
|
+
message: "blocked an unadvertised tool call before batch execution",
|
|
1499
|
+
meta: { channel: String(channel), toolName: unadvertisedCall.call.name },
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
else {
|
|
1503
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1504
|
+
level: "warn",
|
|
1505
|
+
component: "engine",
|
|
1506
|
+
event: "engine.tool_arguments_rejected",
|
|
1507
|
+
message: "tool batch rejected before execution because arguments were invalid",
|
|
1508
|
+
meta: { toolCallId: invalidCall.call.id, toolName: invalidCall.call.name },
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
continue;
|
|
1512
|
+
}
|
|
1513
|
+
const validCalls = validatedCalls;
|
|
1514
|
+
const validatedCallArguments = new Map(validCalls.map((entry) => [
|
|
1515
|
+
entry.call,
|
|
1516
|
+
entry.validated.arguments,
|
|
1517
|
+
]));
|
|
1518
|
+
const habitBlockReason = await habitToolBatchBlockReason(habitSession, result.toolCalls, augmentedToolContext?.delegatedOrigins);
|
|
1458
1519
|
if (habitBlockReason) {
|
|
1459
1520
|
streamCallbackBuffer?.discard();
|
|
1460
1521
|
recordBlockedHabitSurfaceAttempts(habitSession, result.toolCalls, habitBlockReason);
|
|
@@ -1480,16 +1541,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1480
1541
|
? (0, tools_1.resolveToolDefinition)(soleTerminalCall.name)?.terminalProjection
|
|
1481
1542
|
: undefined;
|
|
1482
1543
|
if (soleTerminalCall && soleTerminalProjection?.mode === "verbatim") {
|
|
1483
|
-
|
|
1484
|
-
try {
|
|
1485
|
-
const parsed = JSON.parse(soleTerminalCall.arguments);
|
|
1486
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1487
|
-
terminalArgs = parsed;
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
catch {
|
|
1491
|
-
// The registered handler owns exact argument validation.
|
|
1492
|
-
}
|
|
1544
|
+
const terminalArgs = validatedCallArguments.get(soleTerminalCall);
|
|
1493
1545
|
if (soleTerminalProjection.clearBufferedText
|
|
1494
1546
|
|| callbacks.settleOutputMode === "final_only") {
|
|
1495
1547
|
streamCallbackBuffer?.discard();
|
|
@@ -1529,13 +1581,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1529
1581
|
}
|
|
1530
1582
|
// Check for settle sole call: intercept before tool execution
|
|
1531
1583
|
if (isSoleSettle) {
|
|
1532
|
-
|
|
1533
|
-
const settleArgs = (() => { try {
|
|
1534
|
-
return JSON.parse(result.toolCalls[0].arguments);
|
|
1535
|
-
}
|
|
1536
|
-
catch {
|
|
1537
|
-
return {};
|
|
1538
|
-
} })();
|
|
1584
|
+
const settleArgs = validatedCallArguments.get(result.toolCalls[0]);
|
|
1539
1585
|
callbacks.onToolStart("settle", settleArgs);
|
|
1540
1586
|
// Private-runtime attention queue gate: reject settle if items remain
|
|
1541
1587
|
const attentionQueue = augmentedToolContext?.delegatedOrigins;
|
|
@@ -1627,13 +1673,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1627
1673
|
const isSoleObserve = result.toolCalls.length === 1 && result.toolCalls[0].name === "observe";
|
|
1628
1674
|
if (isSoleObserve) {
|
|
1629
1675
|
streamCallbackBuffer?.discard();
|
|
1630
|
-
|
|
1631
|
-
const observeArgs = (() => { try {
|
|
1632
|
-
return JSON.parse(result.toolCalls[0].arguments);
|
|
1633
|
-
}
|
|
1634
|
-
catch {
|
|
1635
|
-
return {};
|
|
1636
|
-
} })();
|
|
1676
|
+
const observeArgs = validatedCallArguments.get(result.toolCalls[0]);
|
|
1637
1677
|
let reason;
|
|
1638
1678
|
if (typeof observeArgs?.reason === "string")
|
|
1639
1679
|
reason = observeArgs.reason;
|
|
@@ -1657,12 +1697,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1657
1697
|
const isSoleRest = result.toolCalls.length === 1 && result.toolCalls[0].name === "rest";
|
|
1658
1698
|
if (isSoleRest) {
|
|
1659
1699
|
streamCallbackBuffer?.discard();
|
|
1660
|
-
const restArgs = (
|
|
1661
|
-
return JSON.parse(result.toolCalls[0].arguments);
|
|
1662
|
-
}
|
|
1663
|
-
catch {
|
|
1664
|
-
return {};
|
|
1665
|
-
} })();
|
|
1700
|
+
const restArgs = validatedCallArguments.get(result.toolCalls[0]);
|
|
1666
1701
|
callbacks.onToolStart("rest", restArgs);
|
|
1667
1702
|
// Attention queue gate: reject rest if items remain
|
|
1668
1703
|
const attentionQueue = augmentedToolContext?.delegatedOrigins;
|
|
@@ -1705,42 +1740,10 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1705
1740
|
done = true;
|
|
1706
1741
|
continue;
|
|
1707
1742
|
}
|
|
1708
|
-
const
|
|
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) => ({
|
|
1743
|
+
const approvalCalls = options?.approvalCoordinator ? validCalls.map((entry) => ({
|
|
1741
1744
|
...entry,
|
|
1742
1745
|
policy: (0, tools_1.approvalPolicyForToolName)(entry.call.name, entry.validated.arguments),
|
|
1743
|
-
}))
|
|
1746
|
+
})) : [];
|
|
1744
1747
|
const protectedCall = approvalCalls.find((entry) => entry.policy.kind === "required");
|
|
1745
1748
|
if (protectedCall && result.toolCalls.length !== 1) {
|
|
1746
1749
|
streamCallbackBuffer?.discard();
|
|
@@ -1816,21 +1819,6 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1816
1819
|
for (const tc of result.toolCalls) {
|
|
1817
1820
|
if (signal?.aborted)
|
|
1818
1821
|
break;
|
|
1819
|
-
if (tc.name === "speak" && !activeToolNames.has("speak")) {
|
|
1820
|
-
const rejection = "rejected: speak was not advertised for this channel; no outward message was sent.";
|
|
1821
|
-
callbacks.onToolStart(tc.name, {});
|
|
1822
|
-
callbacks.onToolEnd(tc.name, "", false);
|
|
1823
|
-
pushGenerated({ role: "tool", tool_call_id: tc.id, content: rejection });
|
|
1824
|
-
providerRuntime.appendToolOutput(tc.id, rejection);
|
|
1825
|
-
(0, runtime_1.emitNervesEvent)({
|
|
1826
|
-
level: "warn",
|
|
1827
|
-
component: "engine",
|
|
1828
|
-
event: "engine.unadvertised_speak_blocked",
|
|
1829
|
-
message: "blocked an unadvertised speak tool call",
|
|
1830
|
-
meta: { channel: String(channel) },
|
|
1831
|
-
});
|
|
1832
|
-
continue;
|
|
1833
|
-
}
|
|
1834
1822
|
// Reject sole-call tools when mixed with other tool calls
|
|
1835
1823
|
const terminalProjection = (0, tools_1.resolveToolDefinition)(tc.name)?.terminalProjection;
|
|
1836
1824
|
const soleCallRejection = SOLE_CALL_REJECTION[tc.name]
|
|
@@ -1842,13 +1830,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1842
1830
|
providerRuntime.appendToolOutput(tc.id, soleCallRejection);
|
|
1843
1831
|
continue;
|
|
1844
1832
|
}
|
|
1845
|
-
|
|
1846
|
-
try {
|
|
1847
|
-
args = JSON.parse(tc.arguments);
|
|
1848
|
-
}
|
|
1849
|
-
catch {
|
|
1850
|
-
/* ignore */
|
|
1851
|
-
}
|
|
1833
|
+
const args = validatedCallArguments.get(tc);
|
|
1852
1834
|
if (tc.name === "send_message" && args.friendId === "self") {
|
|
1853
1835
|
const latestUserText = latestUserMessageText(messages);
|
|
1854
1836
|
if (!isPrivateRuntimeChannel && looksLikePrivateReturnRequest(latestUserText)) {
|
|
@@ -1863,12 +1845,9 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
1863
1845
|
sawSendMessageSelf = true;
|
|
1864
1846
|
}
|
|
1865
1847
|
if (tc.name === "speak") {
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
}
|
|
1870
|
-
catch { /* malformed */ }
|
|
1871
|
-
const speakMessage = typeof speakArgs.message === "string" ? speakArgs.message : "";
|
|
1848
|
+
// The canonical pre-batch schema gate guarantees a required string.
|
|
1849
|
+
const speakArgs = JSON.parse(tc.arguments);
|
|
1850
|
+
const speakMessage = speakArgs.message;
|
|
1872
1851
|
const argSummary = (0, tools_1.summarizeArgs)("speak", { message: speakMessage });
|
|
1873
1852
|
callbacks.onToolStart("speak", { message: speakMessage });
|
|
1874
1853
|
if (speakMessage.trim().length === 0) {
|
|
@@ -2024,7 +2003,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
2024
2003
|
}
|
|
2025
2004
|
}
|
|
2026
2005
|
}
|
|
2027
|
-
else
|
|
2006
|
+
else {
|
|
2028
2007
|
const packetId = typeof parsedArgs.packet_id === "string" ? parsedArgs.packet_id.trim() : "";
|
|
2029
2008
|
const kind = parsedArgs.kind;
|
|
2030
2009
|
const objective = typeof parsedArgs.objective === "string" ? parsedArgs.objective.trim() : "";
|
|
@@ -2045,9 +2024,6 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
2045
2024
|
: null;
|
|
2046
2025
|
resultAction = "revised";
|
|
2047
2026
|
}
|
|
2048
|
-
else {
|
|
2049
|
-
throw new Error("ponder requires action=create or revise.");
|
|
2050
|
-
}
|
|
2051
2027
|
if (returnObligationId) {
|
|
2052
2028
|
for (const token of extractPrivateReturnHeldTokens(privateReturnSourceRequest)) {
|
|
2053
2029
|
privateReturnHeldTokens.add(token);
|
|
@@ -2110,6 +2086,15 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
2110
2086
|
success = false;
|
|
2111
2087
|
augmentedToolContext?.habitSession?.recordError?.(toolResult);
|
|
2112
2088
|
}
|
|
2089
|
+
const resolvedRiskProfile = (0, tools_1.resolveToolDefinition)(tc.name)?.riskProfile;
|
|
2090
|
+
const toolRiskProfile = typeof resolvedRiskProfile === "function" ? resolvedRiskProfile(args) : resolvedRiskProfile;
|
|
2091
|
+
options?.toolBoundaryObserver?.({
|
|
2092
|
+
name: tc.name,
|
|
2093
|
+
reason: "dispatched",
|
|
2094
|
+
globallyResolvable: typeof (0, tools_1.resolveToolDefinition)(tc.name)?.handler === "function",
|
|
2095
|
+
invoked: true,
|
|
2096
|
+
sideEffect: success && toolRiskProfile?.mutates !== "none",
|
|
2097
|
+
});
|
|
2113
2098
|
toolResult = (0, tool_friction_1.rewriteToolResultForModel)(tc.name, toolResult, toolFrictionLedger);
|
|
2114
2099
|
(0, tool_loop_1.recordToolOutcome)(toolLoopState, tc.name, args, toolResult, success);
|
|
2115
2100
|
callbacks.onToolEnd(tc.name, (0, tools_1.buildToolResultSummary)(tc.name, args, toolResult, success), success);
|
|
@@ -93,6 +93,7 @@ const background_operations_1 = require("../background-operations");
|
|
|
93
93
|
const mail_import_discovery_1 = require("../mail-import-discovery");
|
|
94
94
|
const session_activity_1 = require("../session-activity");
|
|
95
95
|
const target_resolution_1 = require("../target-resolution");
|
|
96
|
+
const sanctuary_scheduler_origin_1 = require("./sanctuary-scheduler-origin");
|
|
96
97
|
const cli_parse_1 = require("./cli-parse");
|
|
97
98
|
const cli_parse_2 = require("./cli-parse");
|
|
98
99
|
const cli_help_1 = require("./cli-help");
|
|
@@ -1488,6 +1489,8 @@ function toDaemonCommand(command) {
|
|
|
1488
1489
|
noSend: true,
|
|
1489
1490
|
};
|
|
1490
1491
|
}
|
|
1492
|
+
if (command.kind === "habit.poke")
|
|
1493
|
+
return (0, sanctuary_scheduler_origin_1.createSanctuarySchedulerFireCommand)(command) ?? command;
|
|
1491
1494
|
return command;
|
|
1492
1495
|
}
|
|
1493
1496
|
function habitProbeJsonResponse(response) {
|
|
@@ -2217,7 +2220,7 @@ const DEFAULT_RUNTIME_APPLY_TIMEOUT_MS = 15_000;
|
|
|
2217
2220
|
const DEFAULT_RUNTIME_APPLY_POLL_INTERVAL_MS = 500;
|
|
2218
2221
|
const DEFAULT_DAEMON_STATUS_TIMEOUT_MS = 4_000;
|
|
2219
2222
|
const DEFAULT_AGENT_RESTART_TIMEOUT_MS = 8_000;
|
|
2220
|
-
const CONNECT_PROVIDER_CHOICES = ["openai-codex", "anthropic", "minimax", "azure", "github-copilot"];
|
|
2223
|
+
const CONNECT_PROVIDER_CHOICES = ["openai-compatible", "openai-compatible-gemini", "openai-codex", "anthropic", "minimax", "azure", "github-copilot"];
|
|
2221
2224
|
function hasRuntimeConfigValue(config, key) {
|
|
2222
2225
|
return readRuntimeConfigString(config, key) !== null;
|
|
2223
2226
|
}
|
|
@@ -2813,6 +2816,7 @@ function enableAgentSense(agent, sense, deps) {
|
|
|
2813
2816
|
mail: senses.mail ?? { enabled: false },
|
|
2814
2817
|
voice: senses.voice ?? { enabled: false },
|
|
2815
2818
|
a2a: senses.a2a ?? { enabled: false },
|
|
2819
|
+
telegram: senses.telegram ?? { enabled: false },
|
|
2816
2820
|
[sense]: { ...existing, enabled: true },
|
|
2817
2821
|
};
|
|
2818
2822
|
fs.writeFileSync(configPath, `${JSON.stringify(raw, null, 2)}\n`, "utf-8");
|
|
@@ -4997,6 +5001,71 @@ async function executeConnectA2A(agent, deps) {
|
|
|
4997
5001
|
deps.writeStdout(message);
|
|
4998
5002
|
return message;
|
|
4999
5003
|
}
|
|
5004
|
+
function parseCanonicalTelegramId(value, label) {
|
|
5005
|
+
const trimmed = value.trim();
|
|
5006
|
+
if (!/^[1-9][0-9]*$/u.test(trimmed)) {
|
|
5007
|
+
throw new Error(`${label} must be a canonical positive decimal string`);
|
|
5008
|
+
}
|
|
5009
|
+
return trimmed;
|
|
5010
|
+
}
|
|
5011
|
+
async function executeConnectTelegram(agent, deps) {
|
|
5012
|
+
if (agent === "SerpentGuide") {
|
|
5013
|
+
throw new Error("SerpentGuide has no persistent runtime credentials. Connect Telegram on the hatchling agent instead.");
|
|
5014
|
+
}
|
|
5015
|
+
const promptInput = requirePromptInput(deps, "Telegram setup");
|
|
5016
|
+
const promptSecret = requirePromptSecret(deps, "Telegram bot token entry");
|
|
5017
|
+
const botToken = (await promptSecret("Telegram bot token: ")).trim();
|
|
5018
|
+
if (!/^[0-9]+:[A-Za-z0-9_-]+$/u.test(botToken))
|
|
5019
|
+
throw new Error("Telegram bot token has an invalid format");
|
|
5020
|
+
const authorizedUserId = parseCanonicalTelegramId(await promptInput("Authorized Telegram user ID: "), "Telegram authorized user ID");
|
|
5021
|
+
const authorizedChatId = parseCanonicalTelegramId(await promptInput("Authorized private Telegram chat ID: "), "Telegram authorized chat ID");
|
|
5022
|
+
const progress = createHumanCommandProgress(deps, "connect telegram");
|
|
5023
|
+
let stored;
|
|
5024
|
+
try {
|
|
5025
|
+
stored = await runCommandProgressPhase(progress, "saving Telegram setup", async () => {
|
|
5026
|
+
progress.updateDetail("checking existing runtime config");
|
|
5027
|
+
const current = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(agent, { preserveCachedOnFailure: true });
|
|
5028
|
+
if (!current.ok && current.reason !== "missing") {
|
|
5029
|
+
throw new Error(`cannot read existing runtime credentials from ${current.itemPath}: ${current.error}`);
|
|
5030
|
+
}
|
|
5031
|
+
progress.updateDetail("storing private Telegram coordinates in the agent vault");
|
|
5032
|
+
return (0, runtime_credentials_1.upsertRuntimeCredentialConfig)(agent, {
|
|
5033
|
+
...(current.ok ? current.config : {}),
|
|
5034
|
+
telegramBotToken: botToken,
|
|
5035
|
+
telegramAuthorizedUserId: authorizedUserId,
|
|
5036
|
+
telegramAuthorizedChatId: authorizedChatId,
|
|
5037
|
+
}, providerCliNow(deps));
|
|
5038
|
+
}, () => "private Telegram coordinates stored");
|
|
5039
|
+
progress.updateDetail("enabling Telegram in agent.json");
|
|
5040
|
+
enableAgentSense(agent, "telegram", deps);
|
|
5041
|
+
}
|
|
5042
|
+
finally {
|
|
5043
|
+
progress.end();
|
|
5044
|
+
}
|
|
5045
|
+
const syncSummary = pushAgentBundleAfterCliMutation(agent, deps);
|
|
5046
|
+
return writeCapabilityOutcome(deps, {
|
|
5047
|
+
subtitle: `${agent}'s private Telegram sense is configured.`,
|
|
5048
|
+
summary: `Telegram is ready for ${agent}.`,
|
|
5049
|
+
whatChanged: [
|
|
5050
|
+
"Capability: private Telegram sense",
|
|
5051
|
+
`Stored: ${stored.itemPath}`,
|
|
5052
|
+
"agent.json: senses.telegram.enabled = true",
|
|
5053
|
+
"token and numeric IDs were not printed",
|
|
5054
|
+
...(syncSummary ? [syncSummary] : []),
|
|
5055
|
+
],
|
|
5056
|
+
nextMoves: ["Run ouro up so the daemon starts the Telegram long poll."],
|
|
5057
|
+
fallbackLines: [
|
|
5058
|
+
`Telegram connected for ${agent}`,
|
|
5059
|
+
"capability: private Telegram sense",
|
|
5060
|
+
`stored: ${stored.itemPath}`,
|
|
5061
|
+
"agent.json: senses.telegram.enabled = true",
|
|
5062
|
+
"token and numeric IDs were not printed",
|
|
5063
|
+
"",
|
|
5064
|
+
"Next: run `ouro up` so the daemon starts the Telegram long poll.",
|
|
5065
|
+
...(syncSummary ? [syncSummary] : []),
|
|
5066
|
+
],
|
|
5067
|
+
});
|
|
5068
|
+
}
|
|
5000
5069
|
async function executeConnectWorkbench(agent, deps) {
|
|
5001
5070
|
const { workbenchMcpCommand, workbenchHasStaleBundleEntry } = readConnectBaySenseFlags(agent, deps);
|
|
5002
5071
|
const changed = removeWorkbenchMcpRegistration(agent, deps);
|
|
@@ -5045,6 +5114,8 @@ async function executeConnect(command, deps) {
|
|
|
5045
5114
|
return executeConnectVoice(command.agent, deps);
|
|
5046
5115
|
if (command.target === "a2a")
|
|
5047
5116
|
return executeConnectA2A(command.agent, deps);
|
|
5117
|
+
if (command.target === "telegram")
|
|
5118
|
+
return executeConnectTelegram(command.agent, deps);
|
|
5048
5119
|
if (command.target === "workbench")
|
|
5049
5120
|
return executeConnectWorkbench(command.agent, deps);
|
|
5050
5121
|
const progress = createHumanCommandProgress(deps, "connect");
|
|
@@ -5417,6 +5488,7 @@ async function executeAuthRun(command, deps) {
|
|
|
5417
5488
|
agentName: command.agent,
|
|
5418
5489
|
provider,
|
|
5419
5490
|
promptInput: deps.promptInput,
|
|
5491
|
+
promptSecret: deps.promptSecret,
|
|
5420
5492
|
onProgress: (message) => progress.updateDetail(message),
|
|
5421
5493
|
});
|
|
5422
5494
|
}
|
|
@@ -6406,7 +6478,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
6406
6478
|
}
|
|
6407
6479
|
if (command.kind === "mail.sync-cache") {
|
|
6408
6480
|
const { runHostedMailCacheSync } = await Promise.resolve().then(() => __importStar(require("../../mailroom/cache-sync-cli")));
|
|
6409
|
-
const text = await runHostedMailCacheSync(command.agent);
|
|
6481
|
+
const text = await runHostedMailCacheSync(command.agent, deps.writeStdout);
|
|
6410
6482
|
deps.writeStdout(text);
|
|
6411
6483
|
return text;
|
|
6412
6484
|
}
|
|
@@ -445,6 +445,11 @@ const SUBCOMMAND_HELP = {
|
|
|
445
445
|
usage: "ouro connect a2a [--agent <name>]",
|
|
446
446
|
example: "ouro connect a2a --agent <agent>",
|
|
447
447
|
},
|
|
448
|
+
"connect telegram": {
|
|
449
|
+
description: "Connect a private Telegram bot and enable the Telegram sense",
|
|
450
|
+
usage: "ouro connect telegram [--agent <name>]",
|
|
451
|
+
example: "ouro connect telegram --agent <agent>",
|
|
452
|
+
},
|
|
448
453
|
"connect workbench": {
|
|
449
454
|
description: "Verify native Ouro Workbench runtime injection and clean stale bundle entries",
|
|
450
455
|
usage: "ouro connect workbench [--agent <name>]",
|
|
@@ -541,7 +546,7 @@ const SUBCOMMAND_HELP = {
|
|
|
541
546
|
example: "ouro mail backfill-indexes --agent <agent>",
|
|
542
547
|
},
|
|
543
548
|
"mail sync-cache": {
|
|
544
|
-
description: "Converge the local decrypted search cache against
|
|
549
|
+
description: "Converge the local decrypted search cache against stable hosted Mailroom authority in up to three passes, using 20 bounded body-read workers, foreground settlement progress, a 30-second heartbeat, and durable missing-key receipts.",
|
|
545
550
|
usage: "ouro mail sync-cache [--agent <name>]",
|
|
546
551
|
example: "ouro mail sync-cache --agent <agent>",
|
|
547
552
|
},
|
|
@@ -86,7 +86,7 @@ function extractLaneFlag(args) {
|
|
|
86
86
|
return { lane: value, rest };
|
|
87
87
|
}
|
|
88
88
|
function isAgentProvider(value) {
|
|
89
|
-
return value === "azure" || value === "anthropic" || value === "minimax" || value === "openai-codex" || value === "github-copilot";
|
|
89
|
+
return value === "azure" || value === "anthropic" || value === "minimax" || value === "openai-codex" || value === "github-copilot" || value === "openai-compatible" || value === "openai-compatible-gemini";
|
|
90
90
|
}
|
|
91
91
|
function usage() {
|
|
92
92
|
return [
|
|
@@ -103,7 +103,7 @@ function usage() {
|
|
|
103
103
|
" ouro -v|--version",
|
|
104
104
|
" ouro auth [--agent <name>] [--provider <provider>]",
|
|
105
105
|
" ouro account ensure [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]",
|
|
106
|
-
" ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a|workbench] [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]",
|
|
106
|
+
" ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a|telegram|workbench] [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]",
|
|
107
107
|
" ouro mail import-mbox --file <path> [--owner-email <email>] [--source <label>] [--agent <name>] [--foreground]",
|
|
108
108
|
" ouro mail backfill-indexes [--agent <name>] [--foreground]",
|
|
109
109
|
" ouro auth verify [--agent <name>] [--provider <provider>]",
|
|
@@ -1035,9 +1035,11 @@ function normalizeConnectTarget(value) {
|
|
|
1035
1035
|
return "voice";
|
|
1036
1036
|
if (value === "a2a" || value === "agent2agent" || value === "agent-to-agent")
|
|
1037
1037
|
return "a2a";
|
|
1038
|
+
if (value === "telegram")
|
|
1039
|
+
return "telegram";
|
|
1038
1040
|
if (value === "workbench" || value === "ouro-workbench" || value === "terminal-workbench")
|
|
1039
1041
|
return "workbench";
|
|
1040
|
-
throw new Error("Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a|workbench] [--agent <name>]");
|
|
1042
|
+
throw new Error("Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a|telegram|workbench] [--agent <name>]");
|
|
1041
1043
|
}
|
|
1042
1044
|
function extractMailSourceFlags(args, usageText) {
|
|
1043
1045
|
const rest = [];
|
|
@@ -1090,7 +1092,7 @@ function extractMailSourceFlags(args, usageText) {
|
|
|
1090
1092
|
};
|
|
1091
1093
|
}
|
|
1092
1094
|
function parseConnectCommand(args) {
|
|
1093
|
-
const usageText = "Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a|workbench] [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]";
|
|
1095
|
+
const usageText = "Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a|telegram|workbench] [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]";
|
|
1094
1096
|
const { agent, rest: afterAgent } = extractAgentFlag(args);
|
|
1095
1097
|
const mailFlags = extractMailSourceFlags(afterAgent, usageText);
|
|
1096
1098
|
if (mailFlags.rest.length > 1)
|