@ouro.bot/cli 0.1.0-alpha.2 → 0.1.0-alpha.200
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 +187 -191
- package/SerpentGuide.ouro/agent.json +82 -0
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +6 -3
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +2 -2
- package/assets/ouroboros.png +0 -0
- package/changelog.json +1248 -0
- package/dist/heart/active-work.js +832 -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/cares.js +179 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config.js +114 -28
- package/dist/heart/core.js +689 -120
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-config-check.js +149 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/auth-flow.js +477 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/daemon-cli.js +2897 -193
- package/dist/heart/daemon/daemon-entry.js +194 -8
- package/dist/heart/daemon/daemon-health.js +136 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +231 -0
- package/dist/heart/daemon/daemon-tombstone.js +123 -0
- package/dist/heart/daemon/daemon.js +488 -13
- package/dist/heart/daemon/habit-migration.js +181 -0
- package/dist/heart/daemon/habit-parser.js +119 -0
- package/dist/heart/daemon/habit-scheduler.js +369 -0
- package/dist/heart/daemon/hatch-animation.js +35 -0
- package/dist/heart/daemon/hatch-flow.js +32 -122
- package/dist/heart/daemon/hatch-specialist.js +7 -2
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +206 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/launchd.js +165 -0
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/mcp-server.js +591 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-bot-wrapper.js +4 -3
- package/dist/heart/daemon/ouro-path-installer.js +221 -0
- package/dist/heart/daemon/ouro-uti.js +11 -2
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/outlook-http.js +439 -0
- package/dist/heart/daemon/outlook-read.js +1618 -0
- package/dist/heart/daemon/outlook-render.js +1032 -0
- package/dist/heart/daemon/outlook-types.js +7 -0
- package/dist/heart/daemon/outlook-view.js +194 -0
- package/dist/heart/daemon/process-manager.js +94 -1
- package/dist/heart/daemon/run-hooks.js +39 -0
- package/dist/heart/daemon/runtime-logging.js +61 -14
- package/dist/heart/daemon/runtime-metadata.js +219 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +314 -0
- package/dist/heart/daemon/session-id-resolver.js +131 -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 +129 -0
- package/dist/heart/daemon/specialist-prompt.js +101 -0
- package/dist/heart/daemon/specialist-tools.js +283 -0
- package/dist/heart/daemon/staged-restart.js +138 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- 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 +220 -52
- package/dist/heart/intentions.js +173 -0
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +225 -0
- package/dist/heart/presence.js +185 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +178 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +199 -36
- package/dist/heart/providers/azure.js +116 -10
- package/dist/heart/providers/github-copilot.js +157 -0
- package/dist/heart/providers/minimax.js +33 -3
- package/dist/heart/providers/openai-codex.js +51 -16
- package/dist/heart/sense-truth.js +61 -0
- package/dist/heart/session-activity.js +172 -0
- package/dist/heart/session-recall.js +216 -0
- package/dist/heart/start-of-turn-packet.js +232 -0
- package/dist/heart/streaming.js +120 -33
- package/dist/heart/sync.js +217 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +122 -32
- package/dist/mind/bundle-manifest.js +77 -1
- package/dist/mind/context.js +125 -14
- package/dist/mind/{memory.js → diary.js} +69 -56
- package/dist/mind/episodes.js +119 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +64 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/store-file.js +36 -0
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +9 -1
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/obligations.js +138 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +861 -105
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/nerves/coverage/audit.js +1 -1
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +63 -3
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-Ck8agNeO.js +61 -0
- package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/ado-semantic.js +11 -5
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +301 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +210 -4
- package/dist/repertoire/coding/spawner.js +58 -12
- package/dist/repertoire/coding/tools.js +209 -7
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/guardrails.js +291 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +251 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +14 -23
- package/dist/repertoire/tasks/board.js +43 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +28 -10
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +11 -28
- package/dist/repertoire/tools-base.js +1360 -271
- package/dist/repertoire/tools-bluebubbles.js +94 -0
- package/dist/repertoire/tools-github.js +1 -6
- package/dist/repertoire/tools-teams.js +65 -59
- package/dist/repertoire/tools.js +318 -91
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +97 -0
- package/dist/senses/bluebubbles-client.js +532 -0
- package/dist/senses/bluebubbles-entry.js +13 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +339 -0
- package/dist/senses/bluebubbles-model.js +261 -0
- package/dist/senses/bluebubbles-mutation-log.js +116 -0
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +1327 -0
- package/dist/senses/cli/adapter.js +274 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +349 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +83 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +198 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +687 -262
- package/dist/senses/commands.js +65 -1
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +112 -19
- package/dist/senses/inner-dialog.js +592 -84
- package/dist/senses/pipeline.js +693 -0
- package/dist/senses/shared-turn.js +196 -0
- package/dist/senses/surface-tool.js +95 -0
- package/dist/senses/teams.js +817 -151
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +24 -7
- package/skills/configure-dev-tools.md +81 -0
- package/subagents/README.md +4 -70
- package/AdoptionSpecialist.ouro/agent.json +0 -20
- 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/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
package/dist/heart/streaming.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.SettleStreamer = exports.SettleParser = void 0;
|
|
4
4
|
exports.toResponsesInput = toResponsesInput;
|
|
5
5
|
exports.toResponsesTools = toResponsesTools;
|
|
6
6
|
exports.streamChatCompletion = streamChatCompletion;
|
|
7
7
|
exports.streamResponsesApi = streamResponsesApi;
|
|
8
8
|
const runtime_1 = require("../nerves/runtime");
|
|
9
9
|
// Character-level state machine that extracts the answer value from
|
|
10
|
-
// `
|
|
10
|
+
// `settle` tool call JSON arguments as they stream in.
|
|
11
11
|
// Scans for prefix `"answer":"` or `"answer": "` in the character stream,
|
|
12
12
|
// then emits text handling JSON escapes, stopping at unescaped closing `"`.
|
|
13
|
-
class
|
|
13
|
+
class SettleParser {
|
|
14
14
|
// Possible prefixes to match (with and without space after colon)
|
|
15
15
|
static PREFIXES = ['"answer":"', '"answer": "'];
|
|
16
16
|
// Buffer of characters seen so far (pre-activation only)
|
|
@@ -29,7 +29,7 @@ class FinalAnswerParser {
|
|
|
29
29
|
if (!this._active) {
|
|
30
30
|
this.buf += ch;
|
|
31
31
|
// Check if any prefix has been fully matched in the buffer
|
|
32
|
-
for (const prefix of
|
|
32
|
+
for (const prefix of SettleParser.PREFIXES) {
|
|
33
33
|
if (this.buf.endsWith(prefix)) {
|
|
34
34
|
this._active = true;
|
|
35
35
|
break;
|
|
@@ -76,7 +76,96 @@ class FinalAnswerParser {
|
|
|
76
76
|
return out;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
exports.
|
|
79
|
+
exports.SettleParser = SettleParser;
|
|
80
|
+
// Shared helper: wraps SettleParser with onClearText + onTextChunk wiring.
|
|
81
|
+
// Used by all streaming providers (Chat Completions, Responses API, Anthropic)
|
|
82
|
+
// so the eager-match settle streaming pattern lives in one place.
|
|
83
|
+
class SettleStreamer {
|
|
84
|
+
parser = new SettleParser();
|
|
85
|
+
_detected = false;
|
|
86
|
+
callbacks;
|
|
87
|
+
enabled;
|
|
88
|
+
constructor(callbacks, enabled = true) {
|
|
89
|
+
this.callbacks = callbacks;
|
|
90
|
+
this.enabled = enabled;
|
|
91
|
+
}
|
|
92
|
+
get detected() { return this._detected; }
|
|
93
|
+
get streamed() { return this.parser.active; }
|
|
94
|
+
/** Mark settle as detected. Calls onClearText on the callbacks. */
|
|
95
|
+
activate() {
|
|
96
|
+
if (!this.enabled)
|
|
97
|
+
return;
|
|
98
|
+
if (this._detected)
|
|
99
|
+
return;
|
|
100
|
+
this._detected = true;
|
|
101
|
+
this.callbacks.onClearText?.();
|
|
102
|
+
}
|
|
103
|
+
/** Feed an argument delta through the parser. Emits text via onTextChunk. */
|
|
104
|
+
processDelta(delta) {
|
|
105
|
+
if (!this.enabled)
|
|
106
|
+
return;
|
|
107
|
+
if (!this._detected)
|
|
108
|
+
return;
|
|
109
|
+
const text = this.parser.process(delta);
|
|
110
|
+
if (text)
|
|
111
|
+
this.callbacks.onTextChunk(text);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.SettleStreamer = SettleStreamer;
|
|
115
|
+
function toResponsesUserContent(content) {
|
|
116
|
+
if (typeof content === "string") {
|
|
117
|
+
return content;
|
|
118
|
+
}
|
|
119
|
+
if (!Array.isArray(content)) {
|
|
120
|
+
return "";
|
|
121
|
+
}
|
|
122
|
+
const parts = [];
|
|
123
|
+
for (const part of content) {
|
|
124
|
+
if (!part || typeof part !== "object") {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
128
|
+
parts.push({ type: "input_text", text: part.text });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (part.type === "image_url") {
|
|
132
|
+
const imageUrl = typeof part.image_url?.url === "string" ? part.image_url.url : "";
|
|
133
|
+
if (!imageUrl)
|
|
134
|
+
continue;
|
|
135
|
+
parts.push({
|
|
136
|
+
type: "input_image",
|
|
137
|
+
image_url: imageUrl,
|
|
138
|
+
detail: part.image_url?.detail ?? "auto",
|
|
139
|
+
});
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (part.type === "input_audio" &&
|
|
143
|
+
typeof part.input_audio?.data === "string" &&
|
|
144
|
+
(part.input_audio.format === "mp3" || part.input_audio.format === "wav")) {
|
|
145
|
+
parts.push({
|
|
146
|
+
type: "input_audio",
|
|
147
|
+
input_audio: {
|
|
148
|
+
data: part.input_audio.data,
|
|
149
|
+
format: part.input_audio.format,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (part.type === "file") {
|
|
155
|
+
const fileRecord = { type: "input_file" };
|
|
156
|
+
if (typeof part.file?.file_data === "string")
|
|
157
|
+
fileRecord.file_data = part.file.file_data;
|
|
158
|
+
if (typeof part.file?.file_id === "string")
|
|
159
|
+
fileRecord.file_id = part.file.file_id;
|
|
160
|
+
if (typeof part.file?.filename === "string")
|
|
161
|
+
fileRecord.filename = part.file.filename;
|
|
162
|
+
if (typeof part.file?.file_data === "string" || typeof part.file?.file_id === "string") {
|
|
163
|
+
parts.push(fileRecord);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return parts.length > 0 ? parts : "";
|
|
168
|
+
}
|
|
80
169
|
function toResponsesInput(messages) {
|
|
81
170
|
let instructions = "";
|
|
82
171
|
const input = [];
|
|
@@ -90,7 +179,7 @@ function toResponsesInput(messages) {
|
|
|
90
179
|
}
|
|
91
180
|
if (msg.role === "user") {
|
|
92
181
|
const u = msg;
|
|
93
|
-
input.push({ role: "user", content:
|
|
182
|
+
input.push({ role: "user", content: toResponsesUserContent(u.content) });
|
|
94
183
|
continue;
|
|
95
184
|
}
|
|
96
185
|
if (msg.role === "assistant") {
|
|
@@ -102,10 +191,16 @@ function toResponsesInput(messages) {
|
|
|
102
191
|
}
|
|
103
192
|
}
|
|
104
193
|
if (a.content) {
|
|
105
|
-
|
|
194
|
+
const assistantItem = { role: "assistant", content: typeof a.content === "string" ? a.content : "" };
|
|
195
|
+
if (a.phase)
|
|
196
|
+
assistantItem.phase = a.phase;
|
|
197
|
+
input.push(assistantItem);
|
|
106
198
|
}
|
|
107
199
|
if (a.tool_calls) {
|
|
108
200
|
for (const tc of a.tool_calls) {
|
|
201
|
+
/* v8 ignore next -- type narrowing: OpenAI SDK only emits function tool_calls @preserve */
|
|
202
|
+
if (tc.type !== "function")
|
|
203
|
+
continue;
|
|
109
204
|
input.push({
|
|
110
205
|
type: "function_call",
|
|
111
206
|
call_id: tc.id,
|
|
@@ -138,7 +233,7 @@ function toResponsesTools(ccTools) {
|
|
|
138
233
|
strict: false,
|
|
139
234
|
}));
|
|
140
235
|
}
|
|
141
|
-
async function streamChatCompletion(client, createParams, callbacks, signal) {
|
|
236
|
+
async function streamChatCompletion(client, createParams, callbacks, signal, eagerSettleStreaming = true) {
|
|
142
237
|
(0, runtime_1.emitNervesEvent)({
|
|
143
238
|
component: "engine",
|
|
144
239
|
event: "engine.stream_start",
|
|
@@ -152,8 +247,7 @@ async function streamChatCompletion(client, createParams, callbacks, signal) {
|
|
|
152
247
|
let toolCalls = {};
|
|
153
248
|
let streamStarted = false;
|
|
154
249
|
let usage;
|
|
155
|
-
const
|
|
156
|
-
let finalAnswerDetected = false;
|
|
250
|
+
const answerStreamer = new SettleStreamer(callbacks, eagerSettleStreaming);
|
|
157
251
|
// State machine for parsing inline <think> tags (MiniMax pattern)
|
|
158
252
|
let contentBuf = "";
|
|
159
253
|
let inThinkTag = false;
|
|
@@ -268,24 +362,21 @@ async function streamChatCompletion(client, createParams, callbacks, signal) {
|
|
|
268
362
|
toolCalls[tc.index].id = tc.id;
|
|
269
363
|
if (tc.function?.name) {
|
|
270
364
|
toolCalls[tc.index].name = tc.function.name;
|
|
271
|
-
// Detect
|
|
365
|
+
// Detect settle tool call on first name delta.
|
|
272
366
|
// Only activate streaming if this is the sole tool call (index 0
|
|
273
367
|
// and no other indices seen). Mixed calls are rejected by core.ts.
|
|
274
|
-
if (tc.function.name === "
|
|
368
|
+
if (tc.function.name === "settle" && !answerStreamer.detected
|
|
275
369
|
&& tc.index === 0 && Object.keys(toolCalls).length === 1) {
|
|
276
|
-
|
|
277
|
-
callbacks.onClearText?.();
|
|
370
|
+
answerStreamer.activate();
|
|
278
371
|
}
|
|
279
372
|
}
|
|
280
373
|
if (tc.function?.arguments) {
|
|
281
374
|
toolCalls[tc.index].arguments += tc.function.arguments;
|
|
282
|
-
// Feed
|
|
375
|
+
// Feed settle argument deltas to the parser for progressive
|
|
283
376
|
// streaming, but only when it appears to be the sole tool call.
|
|
284
|
-
if (
|
|
377
|
+
if (answerStreamer.detected && toolCalls[tc.index].name === "settle"
|
|
285
378
|
&& Object.keys(toolCalls).length === 1) {
|
|
286
|
-
|
|
287
|
-
if (text)
|
|
288
|
-
callbacks.onTextChunk(text);
|
|
379
|
+
answerStreamer.processDelta(tc.function.arguments);
|
|
289
380
|
}
|
|
290
381
|
}
|
|
291
382
|
}
|
|
@@ -299,10 +390,10 @@ async function streamChatCompletion(client, createParams, callbacks, signal) {
|
|
|
299
390
|
toolCalls: Object.values(toolCalls),
|
|
300
391
|
outputItems: [],
|
|
301
392
|
usage,
|
|
302
|
-
|
|
393
|
+
settleStreamed: answerStreamer.streamed,
|
|
303
394
|
};
|
|
304
395
|
}
|
|
305
|
-
async function streamResponsesApi(client, createParams, callbacks, signal) {
|
|
396
|
+
async function streamResponsesApi(client, createParams, callbacks, signal, eagerSettleStreaming = true) {
|
|
306
397
|
(0, runtime_1.emitNervesEvent)({
|
|
307
398
|
component: "engine",
|
|
308
399
|
event: "engine.stream_start",
|
|
@@ -317,9 +408,8 @@ async function streamResponsesApi(client, createParams, callbacks, signal) {
|
|
|
317
408
|
const outputItems = [];
|
|
318
409
|
let currentToolCall = null;
|
|
319
410
|
let usage;
|
|
320
|
-
const
|
|
411
|
+
const answerStreamer = new SettleStreamer(callbacks, eagerSettleStreaming);
|
|
321
412
|
let functionCallCount = 0;
|
|
322
|
-
let finalAnswerDetected = false;
|
|
323
413
|
for await (const event of response) {
|
|
324
414
|
if (signal?.aborted)
|
|
325
415
|
break;
|
|
@@ -348,12 +438,11 @@ async function streamResponsesApi(client, createParams, callbacks, signal) {
|
|
|
348
438
|
name: String(event.item.name),
|
|
349
439
|
arguments: "",
|
|
350
440
|
};
|
|
351
|
-
// Detect
|
|
441
|
+
// Detect settle function call -- clear any streamed noise.
|
|
352
442
|
// Only activate when this is the first (and so far only) function call.
|
|
353
443
|
// Mixed calls are rejected by core.ts; no need to stream their args.
|
|
354
|
-
if (String(event.item.name) === "
|
|
355
|
-
|
|
356
|
-
callbacks.onClearText?.();
|
|
444
|
+
if (String(event.item.name) === "settle" && functionCallCount === 1) {
|
|
445
|
+
answerStreamer.activate();
|
|
357
446
|
}
|
|
358
447
|
}
|
|
359
448
|
break;
|
|
@@ -361,13 +450,11 @@ async function streamResponsesApi(client, createParams, callbacks, signal) {
|
|
|
361
450
|
case "response.function_call_arguments.delta": {
|
|
362
451
|
if (currentToolCall) {
|
|
363
452
|
currentToolCall.arguments += event.delta;
|
|
364
|
-
// Feed
|
|
453
|
+
// Feed settle argument deltas to the parser for progressive
|
|
365
454
|
// streaming, but only when it appears to be the sole function call.
|
|
366
|
-
if (
|
|
455
|
+
if (answerStreamer.detected && currentToolCall.name === "settle"
|
|
367
456
|
&& functionCallCount === 1) {
|
|
368
|
-
|
|
369
|
-
if (text)
|
|
370
|
-
callbacks.onTextChunk(text);
|
|
457
|
+
answerStreamer.processDelta(String(event.delta));
|
|
371
458
|
}
|
|
372
459
|
}
|
|
373
460
|
break;
|
|
@@ -407,6 +494,6 @@ async function streamResponsesApi(client, createParams, callbacks, signal) {
|
|
|
407
494
|
toolCalls,
|
|
408
495
|
outputItems,
|
|
409
496
|
usage,
|
|
410
|
-
|
|
497
|
+
settleStreamed: answerStreamer.streamed,
|
|
411
498
|
};
|
|
412
499
|
}
|
|
@@ -0,0 +1,217 @@
|
|
|
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.runWithSyncContext = runWithSyncContext;
|
|
37
|
+
exports.trackSyncWrite = trackSyncWrite;
|
|
38
|
+
exports.drainSyncWrites = drainSyncWrites;
|
|
39
|
+
exports.preTurnPull = preTurnPull;
|
|
40
|
+
exports.postTurnPush = postTurnPush;
|
|
41
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
42
|
+
const child_process_1 = require("child_process");
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const runtime_1 = require("../nerves/runtime");
|
|
46
|
+
/**
|
|
47
|
+
* Turn-scoped write tracker using AsyncLocalStorage.
|
|
48
|
+
* Each turn runs inside its own async context (via runWithSyncContext),
|
|
49
|
+
* so concurrent turns from different senses never share or clobber each
|
|
50
|
+
* other's tracked writes.
|
|
51
|
+
*
|
|
52
|
+
* Continuity writers (episodes, obligations, cares, intentions, diary)
|
|
53
|
+
* call trackSyncWrite() after every fs write. drainSyncWrites() returns
|
|
54
|
+
* the current turn's accumulated set and clears it.
|
|
55
|
+
*/
|
|
56
|
+
const syncContext = new node_async_hooks_1.AsyncLocalStorage();
|
|
57
|
+
/**
|
|
58
|
+
* Run a function inside its own sync-write tracking context.
|
|
59
|
+
* All trackSyncWrite / drainSyncWrites calls within `fn` (including
|
|
60
|
+
* across awaits) operate on a private Set scoped to this invocation.
|
|
61
|
+
*/
|
|
62
|
+
function runWithSyncContext(fn) {
|
|
63
|
+
return syncContext.run(new Set(), fn);
|
|
64
|
+
}
|
|
65
|
+
/** Register an absolute file path written during this turn. No-op if called outside a sync context. */
|
|
66
|
+
function trackSyncWrite(absolutePath) {
|
|
67
|
+
syncContext.getStore()?.add(absolutePath);
|
|
68
|
+
}
|
|
69
|
+
/** Return all paths written this turn and clear the set. Returns [] if called outside a sync context. */
|
|
70
|
+
function drainSyncWrites() {
|
|
71
|
+
const store = syncContext.getStore();
|
|
72
|
+
if (!store)
|
|
73
|
+
return [];
|
|
74
|
+
const paths = [...store];
|
|
75
|
+
store.clear();
|
|
76
|
+
return paths;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Pre-turn pull: sync the agent bundle from remote before assembling the start-of-turn packet.
|
|
80
|
+
*/
|
|
81
|
+
function preTurnPull(agentRoot, config) {
|
|
82
|
+
(0, runtime_1.emitNervesEvent)({
|
|
83
|
+
component: "heart",
|
|
84
|
+
event: "heart.sync_pull_start",
|
|
85
|
+
message: "pre-turn pull starting",
|
|
86
|
+
meta: { agentRoot, remote: config.remote },
|
|
87
|
+
});
|
|
88
|
+
try {
|
|
89
|
+
(0, child_process_1.execFileSync)("git", ["pull", config.remote], {
|
|
90
|
+
cwd: agentRoot,
|
|
91
|
+
stdio: "pipe",
|
|
92
|
+
timeout: 30000,
|
|
93
|
+
});
|
|
94
|
+
(0, runtime_1.emitNervesEvent)({
|
|
95
|
+
component: "heart",
|
|
96
|
+
event: "heart.sync_pull_end",
|
|
97
|
+
message: "pre-turn pull complete",
|
|
98
|
+
meta: { agentRoot },
|
|
99
|
+
});
|
|
100
|
+
return { ok: true };
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
104
|
+
(0, runtime_1.emitNervesEvent)({
|
|
105
|
+
component: "heart",
|
|
106
|
+
event: "heart.sync_pull_error",
|
|
107
|
+
message: "pre-turn pull failed",
|
|
108
|
+
meta: { agentRoot, error },
|
|
109
|
+
});
|
|
110
|
+
return { ok: false, error };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Post-turn push: commit and push files written during this turn.
|
|
115
|
+
* Receives an explicit list of absolute paths (from drainSyncWrites) instead of
|
|
116
|
+
* discovering changes via git status. Empty list = no changes = skip.
|
|
117
|
+
*/
|
|
118
|
+
function postTurnPush(agentRoot, config, writtenPaths) {
|
|
119
|
+
(0, runtime_1.emitNervesEvent)({
|
|
120
|
+
component: "heart",
|
|
121
|
+
event: "heart.sync_push_start",
|
|
122
|
+
message: "post-turn push starting",
|
|
123
|
+
meta: { agentRoot, remote: config.remote },
|
|
124
|
+
});
|
|
125
|
+
if (writtenPaths.length === 0) {
|
|
126
|
+
(0, runtime_1.emitNervesEvent)({
|
|
127
|
+
component: "heart",
|
|
128
|
+
event: "heart.sync_push_end",
|
|
129
|
+
message: "post-turn push: no changes to sync",
|
|
130
|
+
meta: { agentRoot },
|
|
131
|
+
});
|
|
132
|
+
return { ok: true };
|
|
133
|
+
}
|
|
134
|
+
// Convert absolute paths to relative (for git add)
|
|
135
|
+
const relativePaths = writtenPaths
|
|
136
|
+
.map((p) => path.relative(agentRoot, p))
|
|
137
|
+
.filter((p) => p.length > 0 && !p.startsWith(".."));
|
|
138
|
+
if (relativePaths.length === 0) {
|
|
139
|
+
(0, runtime_1.emitNervesEvent)({
|
|
140
|
+
component: "heart",
|
|
141
|
+
event: "heart.sync_push_end",
|
|
142
|
+
message: "post-turn push: no in-bundle paths to sync",
|
|
143
|
+
meta: { agentRoot },
|
|
144
|
+
});
|
|
145
|
+
return { ok: true };
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
// Stage only the specific files written during this turn
|
|
149
|
+
(0, child_process_1.execFileSync)("git", ["add", "--", ...relativePaths], {
|
|
150
|
+
cwd: agentRoot,
|
|
151
|
+
stdio: "pipe",
|
|
152
|
+
timeout: 10000,
|
|
153
|
+
});
|
|
154
|
+
(0, child_process_1.execFileSync)("git", ["commit", "-m", "sync: post-turn update"], {
|
|
155
|
+
cwd: agentRoot,
|
|
156
|
+
stdio: "pipe",
|
|
157
|
+
timeout: 10000,
|
|
158
|
+
});
|
|
159
|
+
try {
|
|
160
|
+
(0, child_process_1.execFileSync)("git", ["push", config.remote], {
|
|
161
|
+
cwd: agentRoot,
|
|
162
|
+
stdio: "pipe",
|
|
163
|
+
timeout: 30000,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// Push rejected -- try pull-rebase-push
|
|
168
|
+
try {
|
|
169
|
+
(0, child_process_1.execFileSync)("git", ["pull", "--rebase", config.remote], {
|
|
170
|
+
cwd: agentRoot,
|
|
171
|
+
stdio: "pipe",
|
|
172
|
+
timeout: 30000,
|
|
173
|
+
});
|
|
174
|
+
(0, child_process_1.execFileSync)("git", ["push", config.remote], {
|
|
175
|
+
cwd: agentRoot,
|
|
176
|
+
stdio: "pipe",
|
|
177
|
+
timeout: 30000,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch (retryErr) {
|
|
181
|
+
// Second failure -- write pending-sync.json
|
|
182
|
+
const retryError = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
|
183
|
+
const pendingSyncPath = path.join(agentRoot, "state", "pending-sync.json");
|
|
184
|
+
fs.mkdirSync(path.join(agentRoot, "state"), { recursive: true });
|
|
185
|
+
fs.writeFileSync(pendingSyncPath, JSON.stringify({
|
|
186
|
+
error: retryError,
|
|
187
|
+
failedAt: new Date().toISOString(),
|
|
188
|
+
paths: relativePaths,
|
|
189
|
+
}, null, 2), "utf-8");
|
|
190
|
+
(0, runtime_1.emitNervesEvent)({
|
|
191
|
+
component: "heart",
|
|
192
|
+
event: "heart.sync_push_error",
|
|
193
|
+
message: "post-turn push failed after retry",
|
|
194
|
+
meta: { agentRoot, error: retryError },
|
|
195
|
+
});
|
|
196
|
+
return { ok: false, error: retryError };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
(0, runtime_1.emitNervesEvent)({
|
|
200
|
+
component: "heart",
|
|
201
|
+
event: "heart.sync_push_end",
|
|
202
|
+
message: "post-turn push complete",
|
|
203
|
+
meta: { agentRoot, changedCount: relativePaths.length },
|
|
204
|
+
});
|
|
205
|
+
return { ok: true };
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
209
|
+
(0, runtime_1.emitNervesEvent)({
|
|
210
|
+
component: "heart",
|
|
211
|
+
event: "heart.sync_push_error",
|
|
212
|
+
message: "post-turn push failed",
|
|
213
|
+
meta: { agentRoot, error },
|
|
214
|
+
});
|
|
215
|
+
return { ok: false, error };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listTargetSessionCandidates = listTargetSessionCandidates;
|
|
4
|
+
exports.formatTargetSessionCandidates = formatTargetSessionCandidates;
|
|
5
|
+
const session_recall_1 = require("./session-recall");
|
|
6
|
+
const session_activity_1 = require("./session-activity");
|
|
7
|
+
const trust_explanation_1 = require("../mind/friends/trust-explanation");
|
|
8
|
+
const runtime_1 = require("../nerves/runtime");
|
|
9
|
+
function synthesizeFriendRecord(candidate) {
|
|
10
|
+
return {
|
|
11
|
+
id: candidate.friendId,
|
|
12
|
+
name: candidate.friendName,
|
|
13
|
+
role: "stranger",
|
|
14
|
+
trustLevel: "stranger",
|
|
15
|
+
connections: [],
|
|
16
|
+
externalIds: [],
|
|
17
|
+
tenantMemberships: [],
|
|
18
|
+
toolPreferences: {},
|
|
19
|
+
notes: {},
|
|
20
|
+
totalTokens: 0,
|
|
21
|
+
createdAt: new Date(0).toISOString(),
|
|
22
|
+
updatedAt: new Date(0).toISOString(),
|
|
23
|
+
schemaVersion: 1,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function deliveryPriority(mode) {
|
|
27
|
+
if (mode === "deliver_now")
|
|
28
|
+
return 0;
|
|
29
|
+
if (mode === "queue_only")
|
|
30
|
+
return 1;
|
|
31
|
+
return 2;
|
|
32
|
+
}
|
|
33
|
+
function activityPriority(source) {
|
|
34
|
+
return source === "friend-facing" ? 0 : 1;
|
|
35
|
+
}
|
|
36
|
+
function describeDelivery(candidate) {
|
|
37
|
+
if (candidate.channel === "mcp") {
|
|
38
|
+
// MCP can't push (no proactive API), but can queue — dev tool picks up via check_response
|
|
39
|
+
return { mode: "queue_only", reason: "MCP session — message queued for dev tool to pick up via check_response" };
|
|
40
|
+
}
|
|
41
|
+
if (candidate.channel !== "bluebubbles" && candidate.channel !== "teams") {
|
|
42
|
+
return { mode: "blocked", reason: "this channel does not support proactive outward delivery yet" };
|
|
43
|
+
}
|
|
44
|
+
if (candidate.trust.level === "family" || candidate.trust.level === "friend") {
|
|
45
|
+
return { mode: "deliver_now", reason: "directly trusted target on a proactive-delivery channel" };
|
|
46
|
+
}
|
|
47
|
+
return { mode: "queue_only", reason: "visible as a live chat, but immediate delivery still needs explicit cross-chat authorization" };
|
|
48
|
+
}
|
|
49
|
+
async function listTargetSessionCandidates(input) {
|
|
50
|
+
(0, runtime_1.emitNervesEvent)({
|
|
51
|
+
component: "engine",
|
|
52
|
+
event: "engine.target_resolution_start",
|
|
53
|
+
message: "listing live target session candidates",
|
|
54
|
+
meta: {
|
|
55
|
+
sessionsDir: input.sessionsDir,
|
|
56
|
+
currentSession: input.currentSession
|
|
57
|
+
? `${input.currentSession.friendId}/${input.currentSession.channel}/${input.currentSession.key}`
|
|
58
|
+
: null,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
const activity = (0, session_activity_1.listSessionActivity)({
|
|
62
|
+
sessionsDir: input.sessionsDir,
|
|
63
|
+
friendsDir: input.friendsDir,
|
|
64
|
+
agentName: input.agentName,
|
|
65
|
+
currentSession: input.currentSession ?? null,
|
|
66
|
+
}).filter((entry) => entry.channel !== "inner");
|
|
67
|
+
const candidates = [];
|
|
68
|
+
for (const entry of activity) {
|
|
69
|
+
const friend = await input.friendStore.get(entry.friendId) ?? synthesizeFriendRecord(entry);
|
|
70
|
+
const trust = (0, trust_explanation_1.describeTrustContext)({
|
|
71
|
+
friend,
|
|
72
|
+
channel: entry.channel,
|
|
73
|
+
});
|
|
74
|
+
const recall = await (0, session_recall_1.recallSession)({
|
|
75
|
+
sessionPath: entry.sessionPath,
|
|
76
|
+
friendId: entry.friendId,
|
|
77
|
+
channel: entry.channel,
|
|
78
|
+
key: entry.key,
|
|
79
|
+
messageCount: 6,
|
|
80
|
+
summarize: input.summarize,
|
|
81
|
+
trustLevel: trust.level,
|
|
82
|
+
});
|
|
83
|
+
const snapshot = recall.kind === "ok"
|
|
84
|
+
? recall.snapshot
|
|
85
|
+
: recall.kind === "empty"
|
|
86
|
+
? "recent focus: no recent visible messages"
|
|
87
|
+
: "recent focus: session transcript unavailable";
|
|
88
|
+
const delivery = describeDelivery({
|
|
89
|
+
channel: entry.channel,
|
|
90
|
+
trust,
|
|
91
|
+
});
|
|
92
|
+
candidates.push({
|
|
93
|
+
friendId: entry.friendId,
|
|
94
|
+
friendName: entry.friendName,
|
|
95
|
+
channel: entry.channel,
|
|
96
|
+
key: entry.key,
|
|
97
|
+
sessionPath: entry.sessionPath,
|
|
98
|
+
snapshot,
|
|
99
|
+
trust,
|
|
100
|
+
delivery,
|
|
101
|
+
lastActivityAt: entry.lastActivityAt,
|
|
102
|
+
lastActivityMs: entry.lastActivityMs,
|
|
103
|
+
activitySource: entry.activitySource,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return candidates.sort((a, b) => {
|
|
107
|
+
const deliveryDiff = deliveryPriority(a.delivery.mode) - deliveryPriority(b.delivery.mode);
|
|
108
|
+
if (deliveryDiff !== 0)
|
|
109
|
+
return deliveryDiff;
|
|
110
|
+
const sourceDiff = activityPriority(a.activitySource) - activityPriority(b.activitySource);
|
|
111
|
+
if (sourceDiff !== 0)
|
|
112
|
+
return sourceDiff;
|
|
113
|
+
return b.lastActivityMs - a.lastActivityMs;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function formatTargetSessionCandidates(candidates) {
|
|
117
|
+
if (candidates.length === 0)
|
|
118
|
+
return "";
|
|
119
|
+
const lines = ["## candidate target chats"];
|
|
120
|
+
for (const candidate of candidates) {
|
|
121
|
+
lines.push(`- ${candidate.friendName} [${candidate.friendId}] via ${candidate.channel}/${candidate.key}`);
|
|
122
|
+
lines.push(` trust: ${candidate.trust.level} (${candidate.trust.basis}) — ${candidate.trust.summary}`);
|
|
123
|
+
lines.push(` delivery: ${candidate.delivery.mode} — ${candidate.delivery.reason}`);
|
|
124
|
+
lines.push(` snapshot: ${candidate.snapshot}`);
|
|
125
|
+
}
|
|
126
|
+
return lines.join("\n");
|
|
127
|
+
}
|