@ouro.bot/cli 0.1.0-alpha.9 → 0.1.0-alpha.91
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/AdoptionSpecialist.ouro/agent.json +70 -9
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +147 -205
- package/assets/ouroboros.png +0 -0
- package/changelog.json +536 -0
- package/dist/heart/active-work.js +251 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/commitments.js +109 -0
- package/dist/heart/config.js +68 -23
- package/dist/heart/core.js +452 -93
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/auth-flow.js +430 -0
- package/dist/heart/daemon/daemon-cli.js +1738 -269
- package/dist/heart/daemon/daemon-entry.js +55 -6
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon.js +216 -10
- package/dist/heart/daemon/hatch-animation.js +10 -3
- package/dist/heart/daemon/hatch-flow.js +7 -82
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/launchd.js +159 -0
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/ouro-bot-entry.js +0 -0
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-entry.js +0 -0
- package/dist/heart/daemon/ouro-path-installer.js +260 -0
- package/dist/heart/daemon/ouro-uti.js +11 -2
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/process-manager.js +14 -1
- package/dist/heart/daemon/run-hooks.js +37 -0
- package/dist/heart/daemon/runtime-logging.js +58 -15
- package/dist/heart/daemon/runtime-metadata.js +219 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/sense-manager.js +307 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +202 -0
- package/dist/heart/daemon/specialist-orchestrator.js +53 -84
- package/dist/heart/daemon/specialist-prompt.js +63 -11
- package/dist/heart/daemon/specialist-tools.js +211 -60
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/thoughts.js +507 -0
- package/dist/heart/daemon/update-checker.js +111 -0
- package/dist/heart/daemon/update-hooks.js +138 -0
- package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/identity.js +126 -21
- package/dist/heart/kicks.js +1 -19
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +191 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/providers/anthropic.js +74 -9
- package/dist/heart/providers/azure.js +86 -7
- package/dist/heart/providers/github-copilot.js +149 -0
- package/dist/heart/providers/minimax.js +4 -0
- package/dist/heart/providers/openai-codex.js +12 -3
- package/dist/heart/safe-workspace.js +362 -0
- package/dist/heart/sense-truth.js +61 -0
- package/dist/heart/session-activity.js +169 -0
- package/dist/heart/session-recall.js +116 -0
- package/dist/heart/streaming.js +100 -22
- package/dist/heart/target-resolution.js +123 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +14 -2
- package/dist/mind/bundle-manifest.js +70 -0
- package/dist/mind/context.js +57 -11
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +35 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/store-file.js +19 -0
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/memory.js +27 -26
- package/dist/mind/obligation-steering.js +31 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +467 -77
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/index.js +12 -0
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/coding/feedback.js +180 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +69 -4
- package/dist/repertoire/coding/spawner.js +21 -3
- package/dist/repertoire/coding/tools.js +105 -2
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/guardrails.js +290 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +195 -0
- package/dist/repertoire/skills.js +3 -26
- package/dist/repertoire/tasks/board.js +12 -0
- package/dist/repertoire/tasks/index.js +23 -9
- package/dist/repertoire/tasks/transitions.js +1 -2
- package/dist/repertoire/tools-base.js +714 -249
- package/dist/repertoire/tools-bluebubbles.js +93 -0
- package/dist/repertoire/tools-teams.js +58 -25
- package/dist/repertoire/tools.js +106 -53
- package/dist/senses/bluebubbles-client.js +210 -5
- package/dist/senses/bluebubbles-entry.js +2 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +339 -0
- package/dist/senses/bluebubbles-model.js +12 -4
- package/dist/senses/bluebubbles-mutation-log.js +45 -5
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +894 -45
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +400 -164
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/debug-activity.js +154 -0
- package/dist/senses/inner-dialog-worker.js +47 -18
- package/dist/senses/inner-dialog.js +377 -83
- package/dist/senses/pipeline.js +307 -0
- package/dist/senses/teams.js +573 -129
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +14 -3
- package/subagents/README.md +4 -70
- package/dist/heart/daemon/specialist-session.js +0 -142
- package/dist/heart/daemon/subagent-installer.js +0 -125
- package/dist/inner-worker-entry.js +0 -4
- package/subagents/work-doer.md +0 -233
- package/subagents/work-merger.md +0 -624
- package/subagents/work-planner.md +0 -373
package/dist/senses/cli.js
CHANGED
|
@@ -33,11 +33,18 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.MarkdownStreamer = exports.InputController = exports.Spinner = void 0;
|
|
36
|
+
exports.MarkdownStreamer = exports.InputController = exports.Spinner = exports.StreamingWordWrapper = exports.wrapCliText = exports.formatEchoedInputSummary = void 0;
|
|
37
|
+
exports.formatPendingPrefix = formatPendingPrefix;
|
|
38
|
+
exports.getCliContinuityIngressTexts = getCliContinuityIngressTexts;
|
|
39
|
+
exports.pauseActiveSpinner = pauseActiveSpinner;
|
|
40
|
+
exports.resumeActiveSpinner = resumeActiveSpinner;
|
|
41
|
+
exports.setActiveSpinner = setActiveSpinner;
|
|
37
42
|
exports.handleSigint = handleSigint;
|
|
38
43
|
exports.addHistory = addHistory;
|
|
39
44
|
exports.renderMarkdown = renderMarkdown;
|
|
40
45
|
exports.createCliCallbacks = createCliCallbacks;
|
|
46
|
+
exports.createDebouncedLines = createDebouncedLines;
|
|
47
|
+
exports.runCliSession = runCliSession;
|
|
41
48
|
exports.main = main;
|
|
42
49
|
const readline = __importStar(require("readline"));
|
|
43
50
|
const os = __importStar(require("os"));
|
|
@@ -49,9 +56,9 @@ const format_1 = require("../mind/format");
|
|
|
49
56
|
const config_1 = require("../heart/config");
|
|
50
57
|
const context_1 = require("../mind/context");
|
|
51
58
|
const pending_1 = require("../mind/pending");
|
|
52
|
-
const prompt_refresh_1 = require("../mind/prompt-refresh");
|
|
53
59
|
const commands_1 = require("./commands");
|
|
54
60
|
const identity_1 = require("../heart/identity");
|
|
61
|
+
const mcp_manager_1 = require("../repertoire/mcp-manager");
|
|
55
62
|
const nerves_1 = require("../nerves");
|
|
56
63
|
const store_file_1 = require("../mind/friends/store-file");
|
|
57
64
|
const resolver_1 = require("../mind/friends/resolver");
|
|
@@ -59,7 +66,42 @@ const tokens_1 = require("../mind/friends/tokens");
|
|
|
59
66
|
const cli_logging_1 = require("../nerves/cli-logging");
|
|
60
67
|
const runtime_1 = require("../nerves/runtime");
|
|
61
68
|
const trust_gate_1 = require("./trust-gate");
|
|
69
|
+
const pipeline_1 = require("./pipeline");
|
|
70
|
+
const channel_1 = require("../mind/friends/channel");
|
|
62
71
|
const session_lock_1 = require("./session-lock");
|
|
72
|
+
const update_hooks_1 = require("../heart/daemon/update-hooks");
|
|
73
|
+
const bundle_meta_1 = require("../heart/daemon/hooks/bundle-meta");
|
|
74
|
+
const bundle_manifest_1 = require("../mind/bundle-manifest");
|
|
75
|
+
const cli_layout_1 = require("./cli-layout");
|
|
76
|
+
var cli_layout_2 = require("./cli-layout");
|
|
77
|
+
Object.defineProperty(exports, "formatEchoedInputSummary", { enumerable: true, get: function () { return cli_layout_2.formatEchoedInputSummary; } });
|
|
78
|
+
Object.defineProperty(exports, "wrapCliText", { enumerable: true, get: function () { return cli_layout_2.wrapCliText; } });
|
|
79
|
+
Object.defineProperty(exports, "StreamingWordWrapper", { enumerable: true, get: function () { return cli_layout_2.StreamingWordWrapper; } });
|
|
80
|
+
/**
|
|
81
|
+
* Format pending messages as content-prefix strings for injection into
|
|
82
|
+
* the next user message. Self-messages (from === agentName) become
|
|
83
|
+
* `[inner thought: {content}]`, inter-agent messages become
|
|
84
|
+
* `[message from {name}: {content}]`.
|
|
85
|
+
*/
|
|
86
|
+
function formatPendingPrefix(messages, agentName) {
|
|
87
|
+
return messages
|
|
88
|
+
.map((msg) => msg.from === agentName
|
|
89
|
+
? `[inner thought: ${msg.content}]`
|
|
90
|
+
: `[message from ${msg.from}: ${msg.content}]`)
|
|
91
|
+
.join("\n");
|
|
92
|
+
}
|
|
93
|
+
function getCliContinuityIngressTexts(input) {
|
|
94
|
+
const trimmed = input.trim();
|
|
95
|
+
return trimmed ? [trimmed] : [];
|
|
96
|
+
}
|
|
97
|
+
// Module-level active spinner for log coordination.
|
|
98
|
+
// The terminal log sink calls these to avoid interleaving with spinner output.
|
|
99
|
+
let _activeSpinner = null;
|
|
100
|
+
/* v8 ignore start -- spinner coordination: exercised at runtime, not unit-testable without real terminal @preserve */
|
|
101
|
+
function pauseActiveSpinner() { _activeSpinner?.pause(); }
|
|
102
|
+
function resumeActiveSpinner() { _activeSpinner?.resume(); }
|
|
103
|
+
/* v8 ignore stop */
|
|
104
|
+
function setActiveSpinner(s) { _activeSpinner = s; }
|
|
63
105
|
// spinner that only touches stderr, cleans up after itself
|
|
64
106
|
// exported for direct testability (stop-without-start branch)
|
|
65
107
|
class Spinner {
|
|
@@ -70,12 +112,14 @@ class Spinner {
|
|
|
70
112
|
msg = "";
|
|
71
113
|
phrases = null;
|
|
72
114
|
lastPhrase = "";
|
|
115
|
+
stopped = false;
|
|
73
116
|
constructor(m = "working", phrases) {
|
|
74
117
|
this.msg = m;
|
|
75
118
|
if (phrases && phrases.length > 0)
|
|
76
119
|
this.phrases = phrases;
|
|
77
120
|
}
|
|
78
121
|
start() {
|
|
122
|
+
this.stopped = false;
|
|
79
123
|
process.stderr.write("\r\x1b[K");
|
|
80
124
|
this.spin();
|
|
81
125
|
this.iv = setInterval(() => this.spin(), 80);
|
|
@@ -84,15 +128,37 @@ class Spinner {
|
|
|
84
128
|
}
|
|
85
129
|
}
|
|
86
130
|
spin() {
|
|
87
|
-
|
|
131
|
+
// Guard: clearInterval can't prevent already-dequeued callbacks
|
|
132
|
+
/* v8 ignore next -- race guard: timer callback fires after stop() @preserve */
|
|
133
|
+
if (this.stopped)
|
|
134
|
+
return;
|
|
135
|
+
process.stderr.write(`\r\x1b[K${this.frames[this.i]} ${this.msg}... `);
|
|
88
136
|
this.i = (this.i + 1) % this.frames.length;
|
|
89
137
|
}
|
|
90
138
|
rotatePhrase() {
|
|
139
|
+
/* v8 ignore next -- race guard: timer callback fires after stop() @preserve */
|
|
140
|
+
if (this.stopped)
|
|
141
|
+
return;
|
|
91
142
|
const next = (0, phrases_1.pickPhrase)(this.phrases, this.lastPhrase);
|
|
92
143
|
this.lastPhrase = next;
|
|
93
144
|
this.msg = next;
|
|
94
145
|
}
|
|
146
|
+
/* v8 ignore start -- pause/resume: exercised at runtime via log sink coordination @preserve */
|
|
147
|
+
/** Clear the spinner line temporarily so other output can print cleanly. */
|
|
148
|
+
pause() {
|
|
149
|
+
if (this.stopped)
|
|
150
|
+
return;
|
|
151
|
+
process.stderr.write("\r\x1b[K");
|
|
152
|
+
}
|
|
153
|
+
/** Restore the spinner line after a pause. */
|
|
154
|
+
resume() {
|
|
155
|
+
if (this.stopped)
|
|
156
|
+
return;
|
|
157
|
+
this.spin();
|
|
158
|
+
}
|
|
159
|
+
/* v8 ignore stop */
|
|
95
160
|
stop(ok) {
|
|
161
|
+
this.stopped = true;
|
|
96
162
|
if (this.iv) {
|
|
97
163
|
clearInterval(this.iv);
|
|
98
164
|
this.iv = null;
|
|
@@ -279,41 +345,54 @@ function createCliCallbacks() {
|
|
|
279
345
|
meta: {},
|
|
280
346
|
});
|
|
281
347
|
let currentSpinner = null;
|
|
282
|
-
|
|
348
|
+
function setSpinner(s) { currentSpinner = s; setActiveSpinner(s); }
|
|
283
349
|
let hadToolRun = false;
|
|
284
350
|
let textDirty = false; // true when text/reasoning was written without a trailing newline
|
|
285
351
|
const streamer = new MarkdownStreamer();
|
|
352
|
+
const wrapper = new cli_layout_1.StreamingWordWrapper();
|
|
286
353
|
return {
|
|
287
354
|
onModelStart: () => {
|
|
288
355
|
currentSpinner?.stop();
|
|
289
|
-
|
|
290
|
-
hadReasoning = false;
|
|
356
|
+
setSpinner(null);
|
|
291
357
|
textDirty = false;
|
|
292
358
|
streamer.reset();
|
|
359
|
+
wrapper.reset();
|
|
293
360
|
const phrases = (0, phrases_1.getPhrases)();
|
|
294
361
|
const pool = hadToolRun ? phrases.followup : phrases.thinking;
|
|
295
362
|
const first = (0, phrases_1.pickPhrase)(pool);
|
|
296
|
-
|
|
363
|
+
setSpinner(new Spinner(first, pool));
|
|
297
364
|
currentSpinner.start();
|
|
298
365
|
},
|
|
299
366
|
onModelStreamStart: () => {
|
|
300
|
-
|
|
301
|
-
|
|
367
|
+
// No-op: content callbacks (onTextChunk, onReasoningChunk) handle
|
|
368
|
+
// stopping the spinner. onModelStreamStart fires too early and
|
|
369
|
+
// doesn't fire at all for final_answer tool streaming.
|
|
370
|
+
},
|
|
371
|
+
onClearText: () => {
|
|
372
|
+
streamer.reset();
|
|
373
|
+
wrapper.reset();
|
|
302
374
|
},
|
|
303
375
|
onTextChunk: (text) => {
|
|
304
|
-
if
|
|
305
|
-
|
|
306
|
-
|
|
376
|
+
// Stop spinner if still running — final_answer streaming and Anthropic
|
|
377
|
+
// tool-only responses bypass onModelStreamStart, so the spinner would
|
|
378
|
+
// otherwise keep running (and its \r writes overwrite response text).
|
|
379
|
+
if (currentSpinner) {
|
|
380
|
+
currentSpinner.stop();
|
|
381
|
+
setSpinner(null);
|
|
307
382
|
}
|
|
308
383
|
const rendered = streamer.push(text);
|
|
309
|
-
|
|
310
|
-
|
|
384
|
+
/* v8 ignore start -- wrapper integration: tested via cli.test.ts onTextChunk tests @preserve */
|
|
385
|
+
if (rendered) {
|
|
386
|
+
const wrapped = wrapper.push(rendered);
|
|
387
|
+
if (wrapped)
|
|
388
|
+
process.stdout.write(wrapped);
|
|
389
|
+
}
|
|
390
|
+
/* v8 ignore stop */
|
|
311
391
|
textDirty = text.length > 0 && !text.endsWith("\n");
|
|
312
392
|
},
|
|
313
|
-
onReasoningChunk: (
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
textDirty = text.length > 0 && !text.endsWith("\n");
|
|
393
|
+
onReasoningChunk: (_text) => {
|
|
394
|
+
// Keep reasoning private in the CLI surface. The spinner continues to
|
|
395
|
+
// represent active thinking until actual tool or answer output arrives.
|
|
317
396
|
},
|
|
318
397
|
onToolStart: (_name, _args) => {
|
|
319
398
|
// Stop the model-start spinner: when the model returns only tool calls
|
|
@@ -328,13 +407,13 @@ function createCliCallbacks() {
|
|
|
328
407
|
}
|
|
329
408
|
const toolPhrases = (0, phrases_1.getPhrases)().tool;
|
|
330
409
|
const first = (0, phrases_1.pickPhrase)(toolPhrases);
|
|
331
|
-
|
|
410
|
+
setSpinner(new Spinner(first, toolPhrases));
|
|
332
411
|
currentSpinner.start();
|
|
333
412
|
hadToolRun = true;
|
|
334
413
|
},
|
|
335
414
|
onToolEnd: (name, argSummary, success) => {
|
|
336
415
|
currentSpinner?.stop();
|
|
337
|
-
|
|
416
|
+
setSpinner(null);
|
|
338
417
|
const msg = (0, format_1.formatToolResult)(name, argSummary, success);
|
|
339
418
|
const color = success ? "\x1b[32m" : "\x1b[31m";
|
|
340
419
|
process.stderr.write(`${color}${msg}\x1b[0m\n`);
|
|
@@ -342,17 +421,17 @@ function createCliCallbacks() {
|
|
|
342
421
|
onError: (error, severity) => {
|
|
343
422
|
if (severity === "transient") {
|
|
344
423
|
currentSpinner?.fail(error.message);
|
|
345
|
-
|
|
424
|
+
setSpinner(null);
|
|
346
425
|
}
|
|
347
426
|
else {
|
|
348
427
|
currentSpinner?.stop();
|
|
349
|
-
|
|
428
|
+
setSpinner(null);
|
|
350
429
|
process.stderr.write(`\x1b[31m${(0, format_1.formatError)(error)}\x1b[0m\n`);
|
|
351
430
|
}
|
|
352
431
|
},
|
|
353
432
|
onKick: () => {
|
|
354
433
|
currentSpinner?.stop();
|
|
355
|
-
|
|
434
|
+
setSpinner(null);
|
|
356
435
|
if (textDirty) {
|
|
357
436
|
process.stdout.write("\n");
|
|
358
437
|
textDirty = false;
|
|
@@ -361,95 +440,101 @@ function createCliCallbacks() {
|
|
|
361
440
|
},
|
|
362
441
|
flushMarkdown: () => {
|
|
363
442
|
currentSpinner?.stop();
|
|
364
|
-
|
|
443
|
+
setSpinner(null);
|
|
444
|
+
/* v8 ignore start -- wrapper flush: tested via cli.test.ts flushMarkdown tests @preserve */
|
|
365
445
|
const remaining = streamer.flush();
|
|
366
|
-
if (remaining)
|
|
367
|
-
|
|
446
|
+
if (remaining) {
|
|
447
|
+
const wrapped = wrapper.push(remaining);
|
|
448
|
+
if (wrapped)
|
|
449
|
+
process.stdout.write(wrapped);
|
|
450
|
+
}
|
|
451
|
+
const tail = wrapper.flush();
|
|
452
|
+
if (tail)
|
|
453
|
+
process.stdout.write(tail);
|
|
454
|
+
/* v8 ignore stop */
|
|
368
455
|
},
|
|
369
456
|
};
|
|
370
457
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
(0, commands_1.registerDefaultCommands)(registry);
|
|
379
|
-
// Resolve context kernel (identity + channel) for CLI
|
|
380
|
-
const friendsPath = path.join((0, identity_1.getAgentRoot)(), "friends");
|
|
381
|
-
const friendStore = new store_file_1.FileFriendStore(friendsPath);
|
|
382
|
-
const username = os.userInfo().username;
|
|
383
|
-
const hostname = os.hostname();
|
|
384
|
-
const localExternalId = `${username}@${hostname}`;
|
|
385
|
-
const resolver = new resolver_1.FriendResolver(friendStore, {
|
|
386
|
-
provider: "local",
|
|
387
|
-
externalId: localExternalId,
|
|
388
|
-
displayName: username,
|
|
389
|
-
channel: "cli",
|
|
390
|
-
});
|
|
391
|
-
const resolvedContext = await resolver.resolve();
|
|
392
|
-
const cliToolContext = {
|
|
393
|
-
/* v8 ignore next -- CLI has no OAuth sign-in; this no-op satisfies the interface @preserve */
|
|
394
|
-
signin: async () => undefined,
|
|
395
|
-
context: resolvedContext,
|
|
396
|
-
friendStore,
|
|
397
|
-
summarize: (0, core_1.createSummarize)(),
|
|
398
|
-
};
|
|
399
|
-
const friendId = resolvedContext.friend.id;
|
|
400
|
-
const agentConfig = (0, identity_1.loadAgentConfig)();
|
|
401
|
-
(0, cli_logging_1.configureCliRuntimeLogger)(friendId, {
|
|
402
|
-
level: agentConfig.logging?.level,
|
|
403
|
-
sinks: agentConfig.logging?.sinks,
|
|
404
|
-
});
|
|
405
|
-
const sessPath = (0, config_1.sessionPath)(friendId, "cli", "session");
|
|
406
|
-
let sessionLock = null;
|
|
407
|
-
try {
|
|
408
|
-
sessionLock = (0, session_lock_1.acquireSessionLock)(`${sessPath}.lock`, (0, identity_1.getAgentName)());
|
|
458
|
+
// Debounced line iterator: collects rapid-fire lines (paste) into a single input.
|
|
459
|
+
// When the debounce timeout wins the race, the pending iter.next() is saved
|
|
460
|
+
// and reused in the next iteration to prevent it from silently consuming input.
|
|
461
|
+
async function* createDebouncedLines(source, debounceMs) {
|
|
462
|
+
if (debounceMs <= 0) {
|
|
463
|
+
yield* source;
|
|
464
|
+
return;
|
|
409
465
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
466
|
+
const iter = source[Symbol.asyncIterator]();
|
|
467
|
+
let pending = null;
|
|
468
|
+
while (true) {
|
|
469
|
+
const first = pending ? await pending : await iter.next();
|
|
470
|
+
pending = null;
|
|
471
|
+
if (first.done)
|
|
472
|
+
break;
|
|
473
|
+
const lines = [first.value];
|
|
474
|
+
let more = true;
|
|
475
|
+
while (more) {
|
|
476
|
+
const nextPromise = iter.next();
|
|
477
|
+
const raced = await Promise.race([
|
|
478
|
+
nextPromise.then((r) => ({ kind: "line", result: r })),
|
|
479
|
+
new Promise((r) => setTimeout(() => r({ kind: "timeout" }), debounceMs)),
|
|
480
|
+
]);
|
|
481
|
+
if (raced.kind === "timeout") {
|
|
482
|
+
pending = nextPromise;
|
|
483
|
+
more = false;
|
|
484
|
+
}
|
|
485
|
+
else if (raced.result.done) {
|
|
486
|
+
more = false;
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
lines.push(raced.result.value);
|
|
490
|
+
}
|
|
415
491
|
}
|
|
416
|
-
|
|
417
|
-
/* v8 ignore stop */
|
|
492
|
+
yield lines.join("\n");
|
|
418
493
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const drainToMessages = () => {
|
|
427
|
-
const pending = (0, pending_1.drainPending)(pendingDir);
|
|
428
|
-
if (pending.length === 0)
|
|
429
|
-
return 0;
|
|
430
|
-
for (const msg of pending) {
|
|
431
|
-
messages.push({ role: "user", name: "harness", content: `[proactive message from ${msg.from}]` });
|
|
432
|
-
messages.push({ role: "assistant", content: msg.content });
|
|
433
|
-
}
|
|
434
|
-
return pending.length;
|
|
435
|
-
};
|
|
436
|
-
// Startup drain: deliver offline messages
|
|
437
|
-
const startupCount = drainToMessages();
|
|
438
|
-
if (startupCount > 0) {
|
|
439
|
-
(0, context_1.saveSession)(sessPath, messages);
|
|
494
|
+
}
|
|
495
|
+
async function runCliSession(options) {
|
|
496
|
+
/* v8 ignore start -- integration: runCliSession is interactive, tested via E2E @preserve */
|
|
497
|
+
const pasteDebounceMs = options.pasteDebounceMs ?? 50;
|
|
498
|
+
const registry = (0, commands_1.createCommandRegistry)();
|
|
499
|
+
if (!options.disableCommands) {
|
|
500
|
+
(0, commands_1.registerDefaultCommands)(registry);
|
|
440
501
|
}
|
|
502
|
+
const messages = options.messages
|
|
503
|
+
?? [{ role: "system", content: await (0, prompt_1.buildSystem)("cli") }];
|
|
441
504
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
442
505
|
const ctrl = new InputController(rl);
|
|
443
506
|
let currentAbort = null;
|
|
444
507
|
const history = [];
|
|
445
508
|
let closed = false;
|
|
446
509
|
rl.on("close", () => { closed = true; });
|
|
447
|
-
|
|
448
|
-
|
|
510
|
+
if (options.banner !== false) {
|
|
511
|
+
const bannerText = typeof options.banner === "string"
|
|
512
|
+
? options.banner
|
|
513
|
+
: `${options.agentName} (type /commands for help)`;
|
|
514
|
+
// eslint-disable-next-line no-console -- terminal UX: startup banner
|
|
515
|
+
console.log(`\n${bannerText}\n`);
|
|
516
|
+
}
|
|
449
517
|
const cliCallbacks = createCliCallbacks();
|
|
450
|
-
|
|
518
|
+
// exitOnToolCall machinery: wrap execTool to detect target tool
|
|
519
|
+
let exitToolResult;
|
|
520
|
+
let exitToolFired = false;
|
|
521
|
+
const resolvedExecTool = options.execTool;
|
|
522
|
+
const wrappedExecTool = options.exitOnToolCall && resolvedExecTool
|
|
523
|
+
? async (name, args, ctx) => {
|
|
524
|
+
const result = await resolvedExecTool(name, args, ctx);
|
|
525
|
+
if (name === options.exitOnToolCall) {
|
|
526
|
+
exitToolResult = result;
|
|
527
|
+
exitToolFired = true;
|
|
528
|
+
// Abort immediately so the model doesn't generate more output
|
|
529
|
+
// (e.g. reasoning about calling final_answer after complete_adoption)
|
|
530
|
+
currentAbort?.abort();
|
|
531
|
+
}
|
|
532
|
+
return result;
|
|
533
|
+
}
|
|
534
|
+
: resolvedExecTool;
|
|
535
|
+
// Resolve toolChoiceRequired: use explicit option if set, else fall back to toggle
|
|
536
|
+
const getEffectiveToolChoiceRequired = () => options.toolChoiceRequired !== undefined ? options.toolChoiceRequired : (0, commands_1.getToolChoiceRequired)();
|
|
451
537
|
// Ctrl-C at the input prompt: clear line or warn/exit
|
|
452
|
-
// readline with terminal:true catches Ctrl-C in raw mode (no ^C echo)
|
|
453
538
|
rl.on("SIGINT", () => {
|
|
454
539
|
const rlInt = rl;
|
|
455
540
|
const currentLine = rlInt.line || "";
|
|
@@ -470,38 +555,58 @@ async function main(agentName, options) {
|
|
|
470
555
|
rl.close();
|
|
471
556
|
}
|
|
472
557
|
});
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
558
|
+
const debouncedLines = (source) => createDebouncedLines(source, pasteDebounceMs);
|
|
559
|
+
(0, runtime_1.emitNervesEvent)({
|
|
560
|
+
component: "senses",
|
|
561
|
+
event: "senses.cli_session_start",
|
|
562
|
+
message: "runCliSession started",
|
|
563
|
+
meta: { agentName: options.agentName, hasExitOnToolCall: !!options.exitOnToolCall },
|
|
564
|
+
});
|
|
565
|
+
let exitReason = "user_quit";
|
|
566
|
+
// Auto-first-turn: process the last user message immediately so the agent
|
|
567
|
+
// speaks first (e.g. specialist greeting). Only triggers when explicitly opted in.
|
|
568
|
+
if (options.autoFirstTurn && messages.length > 0 && messages[messages.length - 1]?.role === "user") {
|
|
569
|
+
currentAbort = new AbortController();
|
|
570
|
+
const traceId = (0, nerves_1.createTraceId)();
|
|
571
|
+
ctrl.suppress(() => currentAbort.abort());
|
|
572
|
+
let result;
|
|
573
|
+
try {
|
|
574
|
+
result = await (0, core_1.runAgent)(messages, cliCallbacks, options.skipSystemPromptRefresh ? undefined : "cli", currentAbort.signal, {
|
|
575
|
+
toolChoiceRequired: getEffectiveToolChoiceRequired(),
|
|
576
|
+
traceId,
|
|
577
|
+
tools: options.tools,
|
|
578
|
+
execTool: wrappedExecTool,
|
|
579
|
+
toolContext: options.toolContext,
|
|
580
|
+
});
|
|
478
581
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
if (
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
582
|
+
catch (err) {
|
|
583
|
+
// AbortError (Ctrl-C) -- silently continue to prompt
|
|
584
|
+
// All other errors: show the user what happened
|
|
585
|
+
if (!(err instanceof DOMException && err.name === "AbortError")) {
|
|
586
|
+
process.stderr.write(`\x1b[31m${err instanceof Error ? err.message : String(err)}\x1b[0m\n`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
cliCallbacks.flushMarkdown();
|
|
590
|
+
ctrl.restore();
|
|
591
|
+
currentAbort = null;
|
|
592
|
+
if (exitToolFired) {
|
|
593
|
+
exitReason = "tool_exit";
|
|
594
|
+
rl.close();
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
const lastMsg = messages[messages.length - 1];
|
|
598
|
+
if (lastMsg?.role === "assistant" && !(typeof lastMsg.content === "string" ? lastMsg.content : "").trim()) {
|
|
599
|
+
process.stderr.write("\x1b[33m(empty response)\x1b[0m\n");
|
|
600
|
+
}
|
|
601
|
+
process.stdout.write("\n\n");
|
|
602
|
+
if (options.onTurnEnd) {
|
|
603
|
+
await options.onTurnEnd(messages, result ?? { usage: undefined });
|
|
501
604
|
}
|
|
502
|
-
yield lines.join("\n");
|
|
503
605
|
}
|
|
504
606
|
}
|
|
607
|
+
if (!exitToolFired) {
|
|
608
|
+
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
609
|
+
}
|
|
505
610
|
try {
|
|
506
611
|
for await (const input of debouncedLines(rl)) {
|
|
507
612
|
if (closed)
|
|
@@ -510,20 +615,18 @@ async function main(agentName, options) {
|
|
|
510
615
|
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
511
616
|
continue;
|
|
512
617
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
618
|
+
// Optional input gate (e.g. trust gate in main)
|
|
619
|
+
if (options.onInput) {
|
|
620
|
+
const gate = options.onInput(input);
|
|
621
|
+
if (!gate.allowed) {
|
|
622
|
+
if (gate.reply) {
|
|
623
|
+
process.stdout.write(`${gate.reply}\n`);
|
|
624
|
+
}
|
|
625
|
+
if (closed)
|
|
626
|
+
break;
|
|
627
|
+
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
628
|
+
continue;
|
|
522
629
|
}
|
|
523
|
-
if (closed)
|
|
524
|
-
break;
|
|
525
|
-
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
526
|
-
continue;
|
|
527
630
|
}
|
|
528
631
|
// Check for slash commands
|
|
529
632
|
const parsed = (0, commands_1.parseSlashCommand)(input);
|
|
@@ -536,7 +639,7 @@ async function main(agentName, options) {
|
|
|
536
639
|
else if (dispatchResult.result.action === "new") {
|
|
537
640
|
messages.length = 0;
|
|
538
641
|
messages.push({ role: "system", content: await (0, prompt_1.buildSystem)("cli") });
|
|
539
|
-
|
|
642
|
+
await options.onNewSession?.();
|
|
540
643
|
// eslint-disable-next-line no-console -- terminal UX: session cleared
|
|
541
644
|
console.log("session cleared");
|
|
542
645
|
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
@@ -550,55 +653,188 @@ async function main(agentName, options) {
|
|
|
550
653
|
}
|
|
551
654
|
}
|
|
552
655
|
}
|
|
553
|
-
// Re-style the echoed input lines
|
|
554
|
-
// For multiline paste, each line was echoed separately — erase them all
|
|
656
|
+
// Re-style the echoed input lines without leaving wrapped paste remnants behind.
|
|
555
657
|
const cols = process.stdout.columns || 80;
|
|
556
|
-
|
|
557
|
-
let echoRows = 0;
|
|
558
|
-
for (const line of inputLines) {
|
|
559
|
-
echoRows += Math.ceil((2 + line.length) / cols); // "> " prefix + line content
|
|
560
|
-
}
|
|
561
|
-
process.stdout.write(`\x1b[${echoRows}A\x1b[K` + `\x1b[1m> ${inputLines[0]}${inputLines.length > 1 ? ` (+${inputLines.length - 1} lines)` : ""}\x1b[0m\n\n`);
|
|
562
|
-
messages.push({ role: "user", content: input });
|
|
658
|
+
process.stdout.write((0, cli_layout_1.formatEchoedInputSummary)(input, cols));
|
|
563
659
|
addHistory(history, input);
|
|
564
660
|
currentAbort = new AbortController();
|
|
565
|
-
const traceId = (0, nerves_1.createTraceId)();
|
|
566
661
|
ctrl.suppress(() => currentAbort.abort());
|
|
567
662
|
let result;
|
|
568
663
|
try {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
664
|
+
if (options.runTurn) {
|
|
665
|
+
// Pipeline-based turn: the runTurn callback handles user message assembly,
|
|
666
|
+
// pending drain, trust gate, runAgent, postTurn, and token accumulation.
|
|
667
|
+
result = await options.runTurn(messages, input, cliCallbacks, currentAbort.signal);
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
// Legacy path: inline runAgent (used by adoption specialist and tests)
|
|
671
|
+
const prefix = options.getContentPrefix?.();
|
|
672
|
+
messages.push({ role: "user", content: prefix ? `${prefix}\n\n${input}` : input });
|
|
673
|
+
const traceId = (0, nerves_1.createTraceId)();
|
|
674
|
+
result = await (0, core_1.runAgent)(messages, cliCallbacks, options.skipSystemPromptRefresh ? undefined : "cli", currentAbort.signal, {
|
|
675
|
+
toolChoiceRequired: getEffectiveToolChoiceRequired(),
|
|
676
|
+
traceId,
|
|
677
|
+
tools: options.tools,
|
|
678
|
+
execTool: wrappedExecTool,
|
|
679
|
+
toolContext: options.toolContext,
|
|
680
|
+
});
|
|
681
|
+
}
|
|
574
682
|
}
|
|
575
|
-
catch {
|
|
576
|
-
// AbortError
|
|
683
|
+
catch (err) {
|
|
684
|
+
// AbortError (Ctrl-C) -- silently return to prompt
|
|
685
|
+
// All other errors: show the user what happened
|
|
686
|
+
if (!(err instanceof DOMException && err.name === "AbortError")) {
|
|
687
|
+
process.stderr.write(`\x1b[31m${err instanceof Error ? err.message : String(err)}\x1b[0m\n`);
|
|
688
|
+
}
|
|
577
689
|
}
|
|
578
690
|
cliCallbacks.flushMarkdown();
|
|
579
691
|
ctrl.restore();
|
|
580
692
|
currentAbort = null;
|
|
693
|
+
// Check if exit tool was fired during this turn
|
|
694
|
+
if (exitToolFired) {
|
|
695
|
+
exitReason = "tool_exit";
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
581
698
|
// Safety net: never silently swallow an empty response
|
|
582
699
|
const lastMsg = messages[messages.length - 1];
|
|
583
700
|
if (lastMsg?.role === "assistant" && !(typeof lastMsg.content === "string" ? lastMsg.content : "").trim()) {
|
|
584
701
|
process.stderr.write("\x1b[33m(empty response)\x1b[0m\n");
|
|
585
702
|
}
|
|
586
703
|
process.stdout.write("\n\n");
|
|
587
|
-
(
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
// Post-turn: refresh system prompt so active sessions metadata is current
|
|
592
|
-
await (0, prompt_refresh_1.refreshSystemPrompt)(messages, "cli", undefined, resolvedContext);
|
|
704
|
+
// Post-turn hook (session persistence, pending drain, prompt refresh, etc.)
|
|
705
|
+
if (options.onTurnEnd) {
|
|
706
|
+
await options.onTurnEnd(messages, result ?? { usage: undefined });
|
|
707
|
+
}
|
|
593
708
|
if (closed)
|
|
594
709
|
break;
|
|
595
710
|
process.stdout.write("\x1b[36m> \x1b[0m");
|
|
596
711
|
}
|
|
597
712
|
}
|
|
598
713
|
finally {
|
|
599
|
-
sessionLock?.release();
|
|
600
714
|
rl.close();
|
|
601
|
-
|
|
602
|
-
|
|
715
|
+
if (options.banner !== false) {
|
|
716
|
+
// eslint-disable-next-line no-console -- terminal UX: goodbye
|
|
717
|
+
console.log("bye");
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
/* v8 ignore stop */
|
|
721
|
+
return { exitReason, toolResult: exitToolResult };
|
|
722
|
+
}
|
|
723
|
+
async function main(agentName, options) {
|
|
724
|
+
if (agentName)
|
|
725
|
+
(0, identity_1.setAgentName)(agentName);
|
|
726
|
+
const pasteDebounceMs = options?.pasteDebounceMs ?? 50;
|
|
727
|
+
// Register spinner hooks so log output clears the spinner before printing
|
|
728
|
+
(0, nerves_1.registerSpinnerHooks)(pauseActiveSpinner, resumeActiveSpinner);
|
|
729
|
+
// Fallback: apply pending updates for daemon-less direct CLI usage
|
|
730
|
+
(0, update_hooks_1.registerUpdateHook)(bundle_meta_1.bundleMetaHook);
|
|
731
|
+
await (0, update_hooks_1.applyPendingUpdates)((0, identity_1.getAgentBundlesRoot)(), (0, bundle_manifest_1.getPackageVersion)());
|
|
732
|
+
// Fail fast if provider is misconfigured (triggers human-readable error + exit)
|
|
733
|
+
(0, core_1.getProvider)();
|
|
734
|
+
// Resolve context kernel (identity + channel) for CLI
|
|
735
|
+
const friendsPath = path.join((0, identity_1.getAgentRoot)(), "friends");
|
|
736
|
+
const friendStore = new store_file_1.FileFriendStore(friendsPath);
|
|
737
|
+
const username = os.userInfo().username;
|
|
738
|
+
const hostname = os.hostname();
|
|
739
|
+
const localExternalId = `${username}@${hostname}`;
|
|
740
|
+
const resolver = new resolver_1.FriendResolver(friendStore, {
|
|
741
|
+
provider: "local",
|
|
742
|
+
externalId: localExternalId,
|
|
743
|
+
displayName: username,
|
|
744
|
+
channel: "cli",
|
|
745
|
+
});
|
|
746
|
+
const resolvedContext = await resolver.resolve();
|
|
747
|
+
const friendId = resolvedContext.friend.id;
|
|
748
|
+
const agentConfig = (0, identity_1.loadAgentConfig)();
|
|
749
|
+
(0, cli_logging_1.configureCliRuntimeLogger)(friendId, {
|
|
750
|
+
level: agentConfig.logging?.level,
|
|
751
|
+
sinks: agentConfig.logging?.sinks,
|
|
752
|
+
});
|
|
753
|
+
const sessPath = (0, config_1.sessionPath)(friendId, "cli", "session");
|
|
754
|
+
let sessionLock = null;
|
|
755
|
+
try {
|
|
756
|
+
sessionLock = (0, session_lock_1.acquireSessionLock)(`${sessPath}.lock`, (0, identity_1.getAgentName)());
|
|
757
|
+
}
|
|
758
|
+
catch (error) {
|
|
759
|
+
/* v8 ignore start -- integration: main() is interactive, lock tested in session-lock.test.ts @preserve */
|
|
760
|
+
if (error instanceof session_lock_1.SessionLockError) {
|
|
761
|
+
process.stderr.write(`${error.message}\n`);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
throw error;
|
|
765
|
+
/* v8 ignore stop */
|
|
766
|
+
}
|
|
767
|
+
// Load existing session or start fresh
|
|
768
|
+
const existing = (0, context_1.loadSession)(sessPath);
|
|
769
|
+
let sessionState = existing?.state;
|
|
770
|
+
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
|
|
771
|
+
const sessionMessages = existing?.messages && existing.messages.length > 0
|
|
772
|
+
? existing.messages
|
|
773
|
+
: [{ role: "system", content: await (0, prompt_1.buildSystem)("cli", { mcpManager }, resolvedContext) }];
|
|
774
|
+
// Per-turn pipeline input: CLI capabilities and pending dir
|
|
775
|
+
const cliCapabilities = (0, channel_1.getChannelCapabilities)("cli");
|
|
776
|
+
const currentAgentName = (0, identity_1.getAgentName)();
|
|
777
|
+
const pendingDir = (0, pending_1.getPendingDir)(currentAgentName, friendId, "cli", "session");
|
|
778
|
+
const summarize = (0, core_1.createSummarize)();
|
|
779
|
+
try {
|
|
780
|
+
await runCliSession({
|
|
781
|
+
agentName: currentAgentName,
|
|
782
|
+
pasteDebounceMs,
|
|
783
|
+
messages: sessionMessages,
|
|
784
|
+
runTurn: async (messages, userInput, callbacks, signal) => {
|
|
785
|
+
// Run the full per-turn pipeline: resolve -> gate -> session -> drain -> runAgent -> postTurn -> tokens
|
|
786
|
+
// User message passed via input.messages so the pipeline can prepend pending messages to it.
|
|
787
|
+
const result = await (0, pipeline_1.handleInboundTurn)({
|
|
788
|
+
channel: "cli",
|
|
789
|
+
sessionKey: "session",
|
|
790
|
+
capabilities: cliCapabilities,
|
|
791
|
+
messages: [{ role: "user", content: userInput }],
|
|
792
|
+
continuityIngressTexts: getCliContinuityIngressTexts(userInput),
|
|
793
|
+
callbacks,
|
|
794
|
+
friendResolver: { resolve: () => Promise.resolve(resolvedContext) },
|
|
795
|
+
sessionLoader: { loadOrCreate: () => Promise.resolve({ messages, sessionPath: sessPath, state: sessionState }) },
|
|
796
|
+
pendingDir,
|
|
797
|
+
friendStore,
|
|
798
|
+
provider: "local",
|
|
799
|
+
externalId: localExternalId,
|
|
800
|
+
enforceTrustGate: trust_gate_1.enforceTrustGate,
|
|
801
|
+
drainPending: pending_1.drainPending,
|
|
802
|
+
drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)(currentAgentName, deferredFriendId),
|
|
803
|
+
runAgent: (msgs, cb, channel, sig, opts) => (0, core_1.runAgent)(msgs, cb, channel, sig, {
|
|
804
|
+
...opts,
|
|
805
|
+
toolContext: {
|
|
806
|
+
/* v8 ignore next -- default no-op signin; pipeline provides the real one @preserve */
|
|
807
|
+
signin: async () => undefined,
|
|
808
|
+
...opts?.toolContext,
|
|
809
|
+
summarize,
|
|
810
|
+
},
|
|
811
|
+
}),
|
|
812
|
+
postTurn: (turnMessages, sessionPathArg, usage, hooks, state) => {
|
|
813
|
+
(0, context_1.postTurn)(turnMessages, sessionPathArg, usage, hooks, state);
|
|
814
|
+
sessionState = state;
|
|
815
|
+
},
|
|
816
|
+
accumulateFriendTokens: tokens_1.accumulateFriendTokens,
|
|
817
|
+
signal,
|
|
818
|
+
runAgentOptions: {
|
|
819
|
+
toolChoiceRequired: (0, commands_1.getToolChoiceRequired)(),
|
|
820
|
+
traceId: (0, nerves_1.createTraceId)(),
|
|
821
|
+
mcpManager,
|
|
822
|
+
},
|
|
823
|
+
});
|
|
824
|
+
// Handle gate rejection: display auto-reply if present
|
|
825
|
+
if (!result.gateResult.allowed) {
|
|
826
|
+
if ("autoReply" in result.gateResult && result.gateResult.autoReply) {
|
|
827
|
+
process.stdout.write(`${result.gateResult.autoReply}\n`);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
return { usage: result.usage };
|
|
831
|
+
},
|
|
832
|
+
onNewSession: () => {
|
|
833
|
+
(0, context_1.deleteSession)(sessPath);
|
|
834
|
+
},
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
finally {
|
|
838
|
+
sessionLock?.release();
|
|
603
839
|
}
|
|
604
840
|
}
|