@ouro.bot/cli 0.1.0-alpha.46 → 0.1.0-alpha.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.json +9 -0
- package/dist/heart/daemon/daemon-cli.js +4 -76
- package/dist/heart/daemon/daemon.js +7 -0
- package/dist/heart/daemon/socket-client.js +202 -0
- package/dist/heart/daemon/thoughts.js +155 -9
- package/dist/repertoire/tools-base.js +52 -2
- package/dist/senses/inner-dialog.js +136 -98
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.47",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Self-messages now wake inner dialog now/soon instead of only queueing: the harness requests a daemon-managed inner wake when available and falls back to an inline inner turn when it is not.",
|
|
8
|
+
"`send_message(friendId=self)` and `query_session(friendId=self, channel=inner, mode=status)` now share a truthful four-line status contract: `queue`, `wake`, `processing`, and `surfaced`.",
|
|
9
|
+
"Inner dialog persists live runtime activity beside the transcript, so status checks can report active processing instead of stale last-turn history.",
|
|
10
|
+
"Inline fallback surfaced previews now extract `final_answer` text too, so completed inner turns no longer show `no outward result` when the response lived in the final tool call."
|
|
11
|
+
]
|
|
12
|
+
},
|
|
4
13
|
{
|
|
5
14
|
"version": "0.1.0-alpha.46",
|
|
6
15
|
"changes": [
|
|
@@ -41,7 +41,6 @@ exports.runOuroCli = runOuroCli;
|
|
|
41
41
|
const child_process_1 = require("child_process");
|
|
42
42
|
const crypto_1 = require("crypto");
|
|
43
43
|
const fs = __importStar(require("fs"));
|
|
44
|
-
const net = __importStar(require("net"));
|
|
45
44
|
const os = __importStar(require("os"));
|
|
46
45
|
const path = __importStar(require("path"));
|
|
47
46
|
const identity_1 = require("../identity");
|
|
@@ -65,6 +64,7 @@ const tasks_1 = require("../../repertoire/tasks");
|
|
|
65
64
|
const thoughts_1 = require("./thoughts");
|
|
66
65
|
const ouro_bot_global_installer_1 = require("./ouro-bot-global-installer");
|
|
67
66
|
const launchd_1 = require("./launchd");
|
|
67
|
+
const socket_client_1 = require("./socket-client");
|
|
68
68
|
function stringField(value) {
|
|
69
69
|
return typeof value === "string" ? value : null;
|
|
70
70
|
}
|
|
@@ -670,38 +670,6 @@ function parseOuroCommand(args) {
|
|
|
670
670
|
return parseLinkCommand(args.slice(1));
|
|
671
671
|
throw new Error(`Unknown command '${args.join(" ")}'.\n${usage()}`);
|
|
672
672
|
}
|
|
673
|
-
function defaultSendCommand(socketPath, command) {
|
|
674
|
-
return new Promise((resolve, reject) => {
|
|
675
|
-
const client = net.createConnection(socketPath);
|
|
676
|
-
let raw = "";
|
|
677
|
-
client.on("connect", () => {
|
|
678
|
-
client.write(JSON.stringify(command));
|
|
679
|
-
client.end();
|
|
680
|
-
});
|
|
681
|
-
client.on("data", (chunk) => {
|
|
682
|
-
raw += chunk.toString("utf-8");
|
|
683
|
-
});
|
|
684
|
-
client.on("error", reject);
|
|
685
|
-
client.on("end", () => {
|
|
686
|
-
const trimmed = raw.trim();
|
|
687
|
-
if (trimmed.length === 0 && command.kind === "daemon.stop") {
|
|
688
|
-
resolve({ ok: true, message: "daemon stopped" });
|
|
689
|
-
return;
|
|
690
|
-
}
|
|
691
|
-
if (trimmed.length === 0) {
|
|
692
|
-
reject(new Error("Daemon returned empty response."));
|
|
693
|
-
return;
|
|
694
|
-
}
|
|
695
|
-
try {
|
|
696
|
-
const parsed = JSON.parse(trimmed);
|
|
697
|
-
resolve(parsed);
|
|
698
|
-
}
|
|
699
|
-
catch (error) {
|
|
700
|
-
reject(error);
|
|
701
|
-
}
|
|
702
|
-
});
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
673
|
function defaultStartDaemonProcess(socketPath) {
|
|
706
674
|
const entry = path.join((0, identity_1.getRepoRoot)(), "dist", "heart", "daemon", "daemon-entry.js");
|
|
707
675
|
const child = (0, child_process_1.spawn)("node", [entry, "--socket", socketPath], {
|
|
@@ -715,46 +683,6 @@ function defaultWriteStdout(text) {
|
|
|
715
683
|
// eslint-disable-next-line no-console -- terminal UX: CLI command output
|
|
716
684
|
console.log(text);
|
|
717
685
|
}
|
|
718
|
-
function defaultCheckSocketAlive(socketPath) {
|
|
719
|
-
return new Promise((resolve) => {
|
|
720
|
-
const client = net.createConnection(socketPath);
|
|
721
|
-
let raw = "";
|
|
722
|
-
let done = false;
|
|
723
|
-
const finalize = (alive) => {
|
|
724
|
-
if (done)
|
|
725
|
-
return;
|
|
726
|
-
done = true;
|
|
727
|
-
resolve(alive);
|
|
728
|
-
};
|
|
729
|
-
if ("setTimeout" in client && typeof client.setTimeout === "function") {
|
|
730
|
-
client.setTimeout(800, () => {
|
|
731
|
-
client.destroy();
|
|
732
|
-
finalize(false);
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
client.on("connect", () => {
|
|
736
|
-
client.write(JSON.stringify({ kind: "daemon.status" }));
|
|
737
|
-
client.end();
|
|
738
|
-
});
|
|
739
|
-
client.on("data", (chunk) => {
|
|
740
|
-
raw += chunk.toString("utf-8");
|
|
741
|
-
});
|
|
742
|
-
client.on("error", () => finalize(false));
|
|
743
|
-
client.on("end", () => {
|
|
744
|
-
if (raw.trim().length === 0) {
|
|
745
|
-
finalize(false);
|
|
746
|
-
return;
|
|
747
|
-
}
|
|
748
|
-
try {
|
|
749
|
-
JSON.parse(raw);
|
|
750
|
-
finalize(true);
|
|
751
|
-
}
|
|
752
|
-
catch {
|
|
753
|
-
finalize(false);
|
|
754
|
-
}
|
|
755
|
-
});
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
686
|
function defaultCleanupStaleSocket(socketPath) {
|
|
759
687
|
if (fs.existsSync(socketPath)) {
|
|
760
688
|
fs.unlinkSync(socketPath);
|
|
@@ -1075,13 +1003,13 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
1075
1003
|
}
|
|
1076
1004
|
}
|
|
1077
1005
|
/* v8 ignore stop */
|
|
1078
|
-
function createDefaultOuroCliDeps(socketPath =
|
|
1006
|
+
function createDefaultOuroCliDeps(socketPath = socket_client_1.DEFAULT_DAEMON_SOCKET_PATH) {
|
|
1079
1007
|
return {
|
|
1080
1008
|
socketPath,
|
|
1081
|
-
sendCommand:
|
|
1009
|
+
sendCommand: socket_client_1.sendDaemonCommand,
|
|
1082
1010
|
startDaemonProcess: defaultStartDaemonProcess,
|
|
1083
1011
|
writeStdout: defaultWriteStdout,
|
|
1084
|
-
checkSocketAlive:
|
|
1012
|
+
checkSocketAlive: socket_client_1.checkDaemonSocketAlive,
|
|
1085
1013
|
cleanupStaleSocket: defaultCleanupStaleSocket,
|
|
1086
1014
|
fallbackPendingMessage: defaultFallbackPendingMessage,
|
|
1087
1015
|
installSubagents: defaultInstallSubagents,
|
|
@@ -443,6 +443,13 @@ class OuroDaemon {
|
|
|
443
443
|
data: messages,
|
|
444
444
|
};
|
|
445
445
|
}
|
|
446
|
+
case "inner.wake":
|
|
447
|
+
await this.processManager.startAgent(command.agent);
|
|
448
|
+
this.processManager.sendToAgent?.(command.agent, { type: "message" });
|
|
449
|
+
return {
|
|
450
|
+
ok: true,
|
|
451
|
+
message: `woke inner dialog for ${command.agent}`,
|
|
452
|
+
};
|
|
446
453
|
case "chat.connect":
|
|
447
454
|
await this.processManager.startAgent(command.agent);
|
|
448
455
|
return {
|
|
@@ -0,0 +1,202 @@
|
|
|
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.DEFAULT_DAEMON_SOCKET_PATH = void 0;
|
|
37
|
+
exports.sendDaemonCommand = sendDaemonCommand;
|
|
38
|
+
exports.checkDaemonSocketAlive = checkDaemonSocketAlive;
|
|
39
|
+
exports.requestInnerWake = requestInnerWake;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const net = __importStar(require("net"));
|
|
42
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
+
exports.DEFAULT_DAEMON_SOCKET_PATH = "/tmp/ouroboros-daemon.sock";
|
|
44
|
+
function sendDaemonCommand(socketPath, command) {
|
|
45
|
+
(0, runtime_1.emitNervesEvent)({
|
|
46
|
+
component: "daemon",
|
|
47
|
+
event: "daemon.socket_command_start",
|
|
48
|
+
message: "sending daemon command over socket",
|
|
49
|
+
meta: { socketPath, kind: command.kind },
|
|
50
|
+
});
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const client = net.createConnection(socketPath);
|
|
53
|
+
let raw = "";
|
|
54
|
+
client.on("connect", () => {
|
|
55
|
+
client.write(JSON.stringify(command));
|
|
56
|
+
client.end();
|
|
57
|
+
});
|
|
58
|
+
client.on("data", (chunk) => {
|
|
59
|
+
raw += chunk.toString("utf-8");
|
|
60
|
+
});
|
|
61
|
+
client.on("error", (error) => {
|
|
62
|
+
(0, runtime_1.emitNervesEvent)({
|
|
63
|
+
level: "error",
|
|
64
|
+
component: "daemon",
|
|
65
|
+
event: "daemon.socket_command_error",
|
|
66
|
+
message: "daemon socket command failed",
|
|
67
|
+
meta: {
|
|
68
|
+
socketPath,
|
|
69
|
+
kind: command.kind,
|
|
70
|
+
error: error.message,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
reject(error);
|
|
74
|
+
});
|
|
75
|
+
client.on("end", () => {
|
|
76
|
+
const trimmed = raw.trim();
|
|
77
|
+
if (trimmed.length === 0 && command.kind === "daemon.stop") {
|
|
78
|
+
(0, runtime_1.emitNervesEvent)({
|
|
79
|
+
component: "daemon",
|
|
80
|
+
event: "daemon.socket_command_end",
|
|
81
|
+
message: "daemon socket command completed",
|
|
82
|
+
meta: { socketPath, kind: command.kind, ok: true },
|
|
83
|
+
});
|
|
84
|
+
resolve({ ok: true, message: "daemon stopped" });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (trimmed.length === 0) {
|
|
88
|
+
const error = new Error("Daemon returned empty response.");
|
|
89
|
+
(0, runtime_1.emitNervesEvent)({
|
|
90
|
+
level: "error",
|
|
91
|
+
component: "daemon",
|
|
92
|
+
event: "daemon.socket_command_error",
|
|
93
|
+
message: "daemon socket command returned empty response",
|
|
94
|
+
meta: {
|
|
95
|
+
socketPath,
|
|
96
|
+
kind: command.kind,
|
|
97
|
+
error: error.message,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
reject(error);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(trimmed);
|
|
105
|
+
(0, runtime_1.emitNervesEvent)({
|
|
106
|
+
component: "daemon",
|
|
107
|
+
event: "daemon.socket_command_end",
|
|
108
|
+
message: "daemon socket command completed",
|
|
109
|
+
meta: {
|
|
110
|
+
socketPath,
|
|
111
|
+
kind: command.kind,
|
|
112
|
+
ok: parsed.ok,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
resolve(parsed);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
(0, runtime_1.emitNervesEvent)({
|
|
119
|
+
level: "error",
|
|
120
|
+
component: "daemon",
|
|
121
|
+
event: "daemon.socket_command_error",
|
|
122
|
+
message: "daemon socket command returned invalid JSON",
|
|
123
|
+
meta: {
|
|
124
|
+
socketPath,
|
|
125
|
+
kind: command.kind,
|
|
126
|
+
error: error instanceof Error ? error.message : String(error),
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
reject(error);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function checkDaemonSocketAlive(socketPath) {
|
|
135
|
+
(0, runtime_1.emitNervesEvent)({
|
|
136
|
+
component: "daemon",
|
|
137
|
+
event: "daemon.socket_alive_check_start",
|
|
138
|
+
message: "checking daemon socket health",
|
|
139
|
+
meta: { socketPath },
|
|
140
|
+
});
|
|
141
|
+
return new Promise((resolve) => {
|
|
142
|
+
const client = net.createConnection(socketPath);
|
|
143
|
+
let raw = "";
|
|
144
|
+
let done = false;
|
|
145
|
+
const finalize = (alive) => {
|
|
146
|
+
if (done)
|
|
147
|
+
return;
|
|
148
|
+
done = true;
|
|
149
|
+
(0, runtime_1.emitNervesEvent)({
|
|
150
|
+
component: "daemon",
|
|
151
|
+
event: "daemon.socket_alive_check_end",
|
|
152
|
+
message: "daemon socket health check completed",
|
|
153
|
+
meta: { socketPath, alive },
|
|
154
|
+
});
|
|
155
|
+
resolve(alive);
|
|
156
|
+
};
|
|
157
|
+
if ("setTimeout" in client && typeof client.setTimeout === "function") {
|
|
158
|
+
client.setTimeout(800, () => {
|
|
159
|
+
client.destroy();
|
|
160
|
+
finalize(false);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
client.on("connect", () => {
|
|
164
|
+
client.write(JSON.stringify({ kind: "daemon.status" }));
|
|
165
|
+
client.end();
|
|
166
|
+
});
|
|
167
|
+
client.on("data", (chunk) => {
|
|
168
|
+
raw += chunk.toString("utf-8");
|
|
169
|
+
});
|
|
170
|
+
client.on("error", () => finalize(false));
|
|
171
|
+
client.on("end", () => {
|
|
172
|
+
if (raw.trim().length === 0) {
|
|
173
|
+
finalize(false);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
JSON.parse(raw);
|
|
178
|
+
finalize(true);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
finalize(false);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async function requestInnerWake(agent, socketPath = exports.DEFAULT_DAEMON_SOCKET_PATH) {
|
|
187
|
+
const socketAvailable = fs.existsSync(socketPath);
|
|
188
|
+
(0, runtime_1.emitNervesEvent)({
|
|
189
|
+
component: "daemon",
|
|
190
|
+
event: "daemon.inner_wake_request",
|
|
191
|
+
message: "requesting daemon-managed inner wake",
|
|
192
|
+
meta: {
|
|
193
|
+
agent,
|
|
194
|
+
socketPath,
|
|
195
|
+
socketAvailable,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
if (!socketAvailable) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
return sendDaemonCommand(socketPath, { kind: "inner.wake", agent });
|
|
202
|
+
}
|
|
@@ -35,9 +35,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
};
|
|
36
36
|
})();
|
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.formatSurfacedValue = formatSurfacedValue;
|
|
39
|
+
exports.deriveInnerDialogStatus = deriveInnerDialogStatus;
|
|
40
|
+
exports.formatInnerDialogStatus = formatInnerDialogStatus;
|
|
41
|
+
exports.extractThoughtResponseFromMessages = extractThoughtResponseFromMessages;
|
|
38
42
|
exports.parseInnerDialogSession = parseInnerDialogSession;
|
|
39
43
|
exports.formatThoughtTurns = formatThoughtTurns;
|
|
40
44
|
exports.getInnerDialogSessionPath = getInnerDialogSessionPath;
|
|
45
|
+
exports.readInnerDialogStatus = readInnerDialogStatus;
|
|
41
46
|
exports.followThoughts = followThoughts;
|
|
42
47
|
const fs = __importStar(require("fs"));
|
|
43
48
|
const path = __importStar(require("path"));
|
|
@@ -58,6 +63,20 @@ function contentToText(content) {
|
|
|
58
63
|
})
|
|
59
64
|
.join("\n");
|
|
60
65
|
}
|
|
66
|
+
function extractToolFunction(toolCall) {
|
|
67
|
+
if (!toolCall || typeof toolCall !== "object" || !("function" in toolCall))
|
|
68
|
+
return null;
|
|
69
|
+
const maybeFunction = toolCall.function;
|
|
70
|
+
if (!maybeFunction || typeof maybeFunction !== "object")
|
|
71
|
+
return null;
|
|
72
|
+
const name = "name" in maybeFunction && typeof maybeFunction.name === "string"
|
|
73
|
+
? maybeFunction.name
|
|
74
|
+
: undefined;
|
|
75
|
+
const argumentsValue = "arguments" in maybeFunction && typeof maybeFunction.arguments === "string"
|
|
76
|
+
? maybeFunction.arguments
|
|
77
|
+
: undefined;
|
|
78
|
+
return { name, arguments: argumentsValue };
|
|
79
|
+
}
|
|
61
80
|
function classifyTurn(userText) {
|
|
62
81
|
if (userText.includes("waking up."))
|
|
63
82
|
return { type: "boot" };
|
|
@@ -71,13 +90,108 @@ function extractToolNames(messages) {
|
|
|
71
90
|
for (const msg of messages) {
|
|
72
91
|
if (msg.role === "assistant" && Array.isArray(msg.tool_calls)) {
|
|
73
92
|
for (const tc of msg.tool_calls) {
|
|
74
|
-
|
|
75
|
-
|
|
93
|
+
const toolFunction = extractToolFunction(tc);
|
|
94
|
+
if (toolFunction?.name && toolFunction.name !== "final_answer")
|
|
95
|
+
names.push(toolFunction.name);
|
|
76
96
|
}
|
|
77
97
|
}
|
|
78
98
|
}
|
|
79
99
|
return names;
|
|
80
100
|
}
|
|
101
|
+
function extractPendingPromptMessages(prompt) {
|
|
102
|
+
return prompt
|
|
103
|
+
.split("\n")
|
|
104
|
+
.map((line) => line.trim())
|
|
105
|
+
.filter((line) => line.startsWith("[pending from "))
|
|
106
|
+
.map((line) => {
|
|
107
|
+
const separator = line.indexOf("]: ");
|
|
108
|
+
return separator >= 0 ? line.slice(separator + 3).trim() : "";
|
|
109
|
+
})
|
|
110
|
+
.filter((line) => line.length > 0);
|
|
111
|
+
}
|
|
112
|
+
function readPendingMessagesForStatus(pendingDir) {
|
|
113
|
+
if (!fs.existsSync(pendingDir))
|
|
114
|
+
return [];
|
|
115
|
+
let entries;
|
|
116
|
+
try {
|
|
117
|
+
entries = fs.readdirSync(pendingDir);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
const files = [
|
|
123
|
+
...entries.filter((entry) => entry.endsWith(".json.processing")),
|
|
124
|
+
...entries.filter((entry) => entry.endsWith(".json") && !entry.endsWith(".json.processing")),
|
|
125
|
+
].sort((a, b) => a.localeCompare(b));
|
|
126
|
+
const messages = [];
|
|
127
|
+
for (const file of files) {
|
|
128
|
+
try {
|
|
129
|
+
const raw = fs.readFileSync(path.join(pendingDir, file), "utf-8");
|
|
130
|
+
const parsed = JSON.parse(raw);
|
|
131
|
+
if (typeof parsed.content === "string") {
|
|
132
|
+
messages.push(parsed);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// unreadable pending files should not break status queries
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return messages;
|
|
140
|
+
}
|
|
141
|
+
function formatSurfacedValue(text, maxLength = 120) {
|
|
142
|
+
const firstLine = text
|
|
143
|
+
.split("\n")
|
|
144
|
+
.map((line) => line.trim())
|
|
145
|
+
.find((line) => line.length > 0);
|
|
146
|
+
if (!firstLine)
|
|
147
|
+
return "no outward result";
|
|
148
|
+
if (firstLine.length <= maxLength)
|
|
149
|
+
return `"${firstLine}"`;
|
|
150
|
+
return `"${firstLine.slice(0, maxLength - 3)}..."`;
|
|
151
|
+
}
|
|
152
|
+
function deriveInnerDialogStatus(pendingMessages, turns, runtimeState) {
|
|
153
|
+
if (runtimeState?.status === "running") {
|
|
154
|
+
return {
|
|
155
|
+
queue: pendingMessages.length > 0 ? "queued to inner/dialog" : "clear",
|
|
156
|
+
wake: "in progress",
|
|
157
|
+
processing: "started",
|
|
158
|
+
surfaced: "nothing yet",
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (pendingMessages.length > 0) {
|
|
162
|
+
return {
|
|
163
|
+
queue: "queued to inner/dialog",
|
|
164
|
+
wake: "awaiting inner session",
|
|
165
|
+
processing: "pending",
|
|
166
|
+
surfaced: "nothing yet",
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const latestProcessedPendingTurn = [...turns]
|
|
170
|
+
.reverse()
|
|
171
|
+
.find((turn) => extractPendingPromptMessages(turn.prompt).length > 0);
|
|
172
|
+
if (!latestProcessedPendingTurn) {
|
|
173
|
+
return {
|
|
174
|
+
queue: "clear",
|
|
175
|
+
wake: "idle",
|
|
176
|
+
processing: "idle",
|
|
177
|
+
surfaced: "nothing recent",
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
queue: "clear",
|
|
182
|
+
wake: "completed",
|
|
183
|
+
processing: "processed",
|
|
184
|
+
surfaced: formatSurfacedValue(latestProcessedPendingTurn.response),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function formatInnerDialogStatus(status) {
|
|
188
|
+
return [
|
|
189
|
+
`queue: ${status.queue}`,
|
|
190
|
+
`wake: ${status.wake}`,
|
|
191
|
+
`processing: ${status.processing}`,
|
|
192
|
+
`surfaced: ${status.surfaced}`,
|
|
193
|
+
].join("\n");
|
|
194
|
+
}
|
|
81
195
|
/** Extract text from a final_answer tool call's arguments. */
|
|
82
196
|
function extractFinalAnswer(messages) {
|
|
83
197
|
for (let k = messages.length - 1; k >= 0; k--) {
|
|
@@ -85,10 +199,11 @@ function extractFinalAnswer(messages) {
|
|
|
85
199
|
if (msg.role !== "assistant" || !Array.isArray(msg.tool_calls))
|
|
86
200
|
continue;
|
|
87
201
|
for (const tc of msg.tool_calls) {
|
|
88
|
-
|
|
202
|
+
const toolFunction = extractToolFunction(tc);
|
|
203
|
+
if (toolFunction?.name !== "final_answer")
|
|
89
204
|
continue;
|
|
90
205
|
try {
|
|
91
|
-
const parsed = JSON.parse(
|
|
206
|
+
const parsed = JSON.parse(toolFunction.arguments ?? "{}");
|
|
92
207
|
if (typeof parsed.answer === "string" && parsed.answer.trim())
|
|
93
208
|
return parsed.answer.trim();
|
|
94
209
|
}
|
|
@@ -99,6 +214,13 @@ function extractFinalAnswer(messages) {
|
|
|
99
214
|
}
|
|
100
215
|
return "";
|
|
101
216
|
}
|
|
217
|
+
function extractThoughtResponseFromMessages(messages) {
|
|
218
|
+
const assistantMsgs = messages.filter((message) => message.role === "assistant");
|
|
219
|
+
const lastAssistant = assistantMsgs.reverse().find((message) => contentToText(message.content).trim().length > 0);
|
|
220
|
+
return lastAssistant
|
|
221
|
+
? contentToText(lastAssistant.content).trim()
|
|
222
|
+
: extractFinalAnswer(messages);
|
|
223
|
+
}
|
|
102
224
|
function parseInnerDialogSession(sessionPath) {
|
|
103
225
|
(0, runtime_1.emitNervesEvent)({
|
|
104
226
|
component: "daemon",
|
|
@@ -147,11 +269,7 @@ function parseInnerDialogSession(sessionPath) {
|
|
|
147
269
|
}
|
|
148
270
|
// Find the last assistant text response in this turn.
|
|
149
271
|
// With tool_choice="required", the response may be inside a final_answer tool call.
|
|
150
|
-
const
|
|
151
|
-
const lastAssistant = assistantMsgs.reverse().find((m) => contentToText(m.content).trim().length > 0);
|
|
152
|
-
const response = lastAssistant
|
|
153
|
-
? contentToText(lastAssistant.content).trim()
|
|
154
|
-
: extractFinalAnswer(turnMessages);
|
|
272
|
+
const response = extractThoughtResponseFromMessages(turnMessages);
|
|
155
273
|
const tools = extractToolNames(turnMessages);
|
|
156
274
|
turns.push({
|
|
157
275
|
type: classification.type,
|
|
@@ -193,6 +311,34 @@ function formatThoughtTurns(turns, lastN) {
|
|
|
193
311
|
function getInnerDialogSessionPath(agentRoot) {
|
|
194
312
|
return path.join(agentRoot, "state", "sessions", "self", "inner", "dialog.json");
|
|
195
313
|
}
|
|
314
|
+
function getInnerDialogRuntimeStatePath(sessionPath) {
|
|
315
|
+
return path.join(path.dirname(sessionPath), "runtime.json");
|
|
316
|
+
}
|
|
317
|
+
function readInnerDialogRuntimeState(runtimePath) {
|
|
318
|
+
try {
|
|
319
|
+
const raw = fs.readFileSync(runtimePath, "utf-8");
|
|
320
|
+
const parsed = JSON.parse(raw);
|
|
321
|
+
if (parsed.status !== "running" && parsed.status !== "idle")
|
|
322
|
+
return null;
|
|
323
|
+
return {
|
|
324
|
+
status: parsed.status,
|
|
325
|
+
reason: parsed.reason === "boot" || parsed.reason === "heartbeat" || parsed.reason === "instinct"
|
|
326
|
+
? parsed.reason
|
|
327
|
+
: undefined,
|
|
328
|
+
startedAt: typeof parsed.startedAt === "string" ? parsed.startedAt : undefined,
|
|
329
|
+
lastCompletedAt: typeof parsed.lastCompletedAt === "string" ? parsed.lastCompletedAt : undefined,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function readInnerDialogStatus(sessionPath, pendingDir, runtimePath = getInnerDialogRuntimeStatePath(sessionPath)) {
|
|
337
|
+
const pendingMessages = readPendingMessagesForStatus(pendingDir);
|
|
338
|
+
const turns = parseInnerDialogSession(sessionPath);
|
|
339
|
+
const runtimeState = readInnerDialogRuntimeState(runtimePath);
|
|
340
|
+
return deriveInnerDialogStatus(pendingMessages, turns, runtimeState);
|
|
341
|
+
}
|
|
196
342
|
/**
|
|
197
343
|
* Watch a session file and emit new turns as they appear.
|
|
198
344
|
* Returns a cleanup function that stops the watcher.
|
|
@@ -42,6 +42,8 @@ const skills_1 = require("./skills");
|
|
|
42
42
|
const config_1 = require("../heart/config");
|
|
43
43
|
const runtime_1 = require("../nerves/runtime");
|
|
44
44
|
const identity_1 = require("../heart/identity");
|
|
45
|
+
const socket_client_1 = require("../heart/daemon/socket-client");
|
|
46
|
+
const thoughts_1 = require("../heart/daemon/thoughts");
|
|
45
47
|
const tools_1 = require("./coding/tools");
|
|
46
48
|
const memory_1 = require("../mind/memory");
|
|
47
49
|
const pending_1 = require("../mind/pending");
|
|
@@ -596,6 +598,7 @@ exports.baseToolDefinitions = [
|
|
|
596
598
|
channel: { type: "string", description: "the channel: cli, teams, or inner" },
|
|
597
599
|
key: { type: "string", description: "session key (defaults to 'session')" },
|
|
598
600
|
messageCount: { type: "string", description: "how many recent messages to return (default 20)" },
|
|
601
|
+
mode: { type: "string", enum: ["transcript", "status"], description: "transcript (default) or lightweight status for self/inner checks" },
|
|
599
602
|
},
|
|
600
603
|
required: ["friendId", "channel"],
|
|
601
604
|
},
|
|
@@ -607,6 +610,15 @@ exports.baseToolDefinitions = [
|
|
|
607
610
|
const channel = args.channel;
|
|
608
611
|
const key = args.key || "session";
|
|
609
612
|
const count = parseInt(args.messageCount || "20", 10);
|
|
613
|
+
const mode = args.mode || "transcript";
|
|
614
|
+
if (mode === "status") {
|
|
615
|
+
if (friendId !== "self" || channel !== "inner") {
|
|
616
|
+
return "status mode is only available for self/inner dialog.";
|
|
617
|
+
}
|
|
618
|
+
const sessionPath = (0, thoughts_1.getInnerDialogSessionPath)((0, identity_1.getAgentRoot)());
|
|
619
|
+
const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)());
|
|
620
|
+
return (0, thoughts_1.formatInnerDialogStatus)((0, thoughts_1.readInnerDialogStatus)(sessionPath, pendingDir));
|
|
621
|
+
}
|
|
610
622
|
const sessFile = (0, config_1.resolveSessionPath)(friendId, channel, key);
|
|
611
623
|
const raw = fs.readFileSync(sessFile, "utf-8");
|
|
612
624
|
const data = JSON.parse(raw);
|
|
@@ -650,7 +662,7 @@ exports.baseToolDefinitions = [
|
|
|
650
662
|
},
|
|
651
663
|
},
|
|
652
664
|
},
|
|
653
|
-
handler: async (args) => {
|
|
665
|
+
handler: async (args, ctx) => {
|
|
654
666
|
const friendId = args.friendId;
|
|
655
667
|
const channel = args.channel;
|
|
656
668
|
const key = args.key || "session";
|
|
@@ -675,8 +687,46 @@ exports.baseToolDefinitions = [
|
|
|
675
687
|
timestamp: now,
|
|
676
688
|
};
|
|
677
689
|
fs.writeFileSync(filePath, JSON.stringify(envelope, null, 2));
|
|
690
|
+
if (isSelf) {
|
|
691
|
+
let wakeResponse = null;
|
|
692
|
+
try {
|
|
693
|
+
wakeResponse = await (0, socket_client_1.requestInnerWake)(agentName);
|
|
694
|
+
}
|
|
695
|
+
catch {
|
|
696
|
+
wakeResponse = null;
|
|
697
|
+
}
|
|
698
|
+
if (!wakeResponse?.ok) {
|
|
699
|
+
const { runInnerDialogTurn } = await Promise.resolve().then(() => __importStar(require("../senses/inner-dialog")));
|
|
700
|
+
if (ctx?.context?.channel.channel === "inner") {
|
|
701
|
+
queueMicrotask(() => {
|
|
702
|
+
void runInnerDialogTurn({ reason: "instinct" });
|
|
703
|
+
});
|
|
704
|
+
return (0, thoughts_1.formatInnerDialogStatus)({
|
|
705
|
+
queue: "queued to inner/dialog",
|
|
706
|
+
wake: "inline scheduled",
|
|
707
|
+
processing: "pending",
|
|
708
|
+
surfaced: "nothing yet",
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
const turnResult = await runInnerDialogTurn({ reason: "instinct" });
|
|
713
|
+
return (0, thoughts_1.formatInnerDialogStatus)({
|
|
714
|
+
queue: "queued to inner/dialog",
|
|
715
|
+
wake: "inline fallback",
|
|
716
|
+
processing: "processed",
|
|
717
|
+
surfaced: (0, thoughts_1.formatSurfacedValue)((0, thoughts_1.extractThoughtResponseFromMessages)(turnResult?.messages ?? [])),
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return (0, thoughts_1.formatInnerDialogStatus)({
|
|
722
|
+
queue: "queued to inner/dialog",
|
|
723
|
+
wake: "daemon requested",
|
|
724
|
+
processing: "pending",
|
|
725
|
+
surfaced: "nothing yet",
|
|
726
|
+
});
|
|
727
|
+
}
|
|
678
728
|
const preview = content.length > 80 ? content.slice(0, 80) + "…" : content;
|
|
679
|
-
const target =
|
|
729
|
+
const target = `${channel}/${key}`;
|
|
680
730
|
return `message queued for delivery to ${friendId} on ${target}. preview: "${preview}". it will be delivered when their session is next active.`;
|
|
681
731
|
},
|
|
682
732
|
},
|
|
@@ -219,6 +219,31 @@ function createInnerDialogCallbacks() {
|
|
|
219
219
|
function innerDialogSessionPath() {
|
|
220
220
|
return (0, config_1.sessionPath)(pending_1.INNER_DIALOG_PENDING.friendId, pending_1.INNER_DIALOG_PENDING.channel, pending_1.INNER_DIALOG_PENDING.key);
|
|
221
221
|
}
|
|
222
|
+
function innerDialogRuntimeStatePath(sessionFilePath) {
|
|
223
|
+
return path.join(path.dirname(sessionFilePath), "runtime.json");
|
|
224
|
+
}
|
|
225
|
+
function writeInnerDialogRuntimeState(sessionFilePath, state) {
|
|
226
|
+
const filePath = innerDialogRuntimeStatePath(sessionFilePath);
|
|
227
|
+
try {
|
|
228
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
229
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
(0, runtime_1.emitNervesEvent)({
|
|
233
|
+
level: "warn",
|
|
234
|
+
component: "senses",
|
|
235
|
+
event: "senses.inner_dialog_runtime_state_error",
|
|
236
|
+
message: "failed to write inner dialog runtime state",
|
|
237
|
+
meta: {
|
|
238
|
+
status: state.status,
|
|
239
|
+
reason: state.reason ?? null,
|
|
240
|
+
path: filePath,
|
|
241
|
+
/* v8 ignore next -- Node fs APIs throw Error objects for mkdirSync/writeFileSync failures @preserve */
|
|
242
|
+
error: error instanceof Error ? error.message : String(error),
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
222
247
|
// Self-referencing friend record for inner dialog (agent talking to itself).
|
|
223
248
|
// No real friend to resolve -- this satisfies the pipeline's friend resolver contract.
|
|
224
249
|
function createSelfFriend(agentName) {
|
|
@@ -249,106 +274,119 @@ async function runInnerDialogTurn(options) {
|
|
|
249
274
|
const now = options?.now ?? (() => new Date());
|
|
250
275
|
const reason = options?.reason ?? "heartbeat";
|
|
251
276
|
const sessionFilePath = innerDialogSessionPath();
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
userContent
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (options?.taskId) {
|
|
278
|
-
const taskContent = readTaskFile((0, identity_1.getAgentRoot)(), options.taskId);
|
|
279
|
-
userContent = buildTaskTriggeredMessage(options.taskId, taskContent, state.checkpoint);
|
|
277
|
+
writeInnerDialogRuntimeState(sessionFilePath, {
|
|
278
|
+
status: "running",
|
|
279
|
+
reason,
|
|
280
|
+
startedAt: now().toISOString(),
|
|
281
|
+
});
|
|
282
|
+
try {
|
|
283
|
+
const loaded = (0, context_1.loadSession)(sessionFilePath);
|
|
284
|
+
const existingMessages = loaded?.messages ? [...loaded.messages] : [];
|
|
285
|
+
const instincts = options?.instincts ?? loadInnerDialogInstincts();
|
|
286
|
+
const state = {
|
|
287
|
+
cycleCount: 1,
|
|
288
|
+
resting: false,
|
|
289
|
+
lastHeartbeatAt: now().toISOString(),
|
|
290
|
+
};
|
|
291
|
+
// ── Adapter concern: build user message ──────────────────────────
|
|
292
|
+
let userContent;
|
|
293
|
+
if (existingMessages.length === 0) {
|
|
294
|
+
// Fresh session: bootstrap message with non-canonical cleanup nudge
|
|
295
|
+
const aspirations = readAspirations((0, identity_1.getAgentRoot)());
|
|
296
|
+
const nonCanonical = (0, bundle_manifest_1.findNonCanonicalBundlePaths)((0, identity_1.getAgentRoot)());
|
|
297
|
+
const cleanupNudge = buildNonCanonicalCleanupNudge(nonCanonical);
|
|
298
|
+
userContent = [
|
|
299
|
+
buildInnerDialogBootstrapMessage(aspirations, "No prior inner dialog session found."),
|
|
300
|
+
cleanupNudge,
|
|
301
|
+
].filter(Boolean).join("\n\n");
|
|
280
302
|
}
|
|
281
303
|
else {
|
|
282
|
-
|
|
304
|
+
// Resumed session: task-triggered or instinct message with checkpoint context
|
|
305
|
+
const assistantTurns = existingMessages.filter((message) => message.role === "assistant").length;
|
|
306
|
+
state.cycleCount = assistantTurns + 1;
|
|
307
|
+
state.checkpoint = deriveResumeCheckpoint(existingMessages);
|
|
308
|
+
if (options?.taskId) {
|
|
309
|
+
const taskContent = readTaskFile((0, identity_1.getAgentRoot)(), options.taskId);
|
|
310
|
+
userContent = buildTaskTriggeredMessage(options.taskId, taskContent, state.checkpoint);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
userContent = buildInstinctUserMessage(instincts, reason, state);
|
|
314
|
+
}
|
|
283
315
|
}
|
|
316
|
+
const userMessage = { role: "user", content: userContent };
|
|
317
|
+
// ── Session loader: wraps existing session logic ──────────────────
|
|
318
|
+
const innerCapabilities = (0, channel_1.getChannelCapabilities)("inner");
|
|
319
|
+
const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)());
|
|
320
|
+
const selfFriend = createSelfFriend((0, identity_1.getAgentName)());
|
|
321
|
+
const selfContext = { friend: selfFriend, channel: innerCapabilities };
|
|
322
|
+
const sessionLoader = {
|
|
323
|
+
loadOrCreate: async () => {
|
|
324
|
+
if (existingMessages.length > 0) {
|
|
325
|
+
return { messages: existingMessages, sessionPath: sessionFilePath };
|
|
326
|
+
}
|
|
327
|
+
// Fresh session: build system prompt
|
|
328
|
+
const systemPrompt = await (0, prompt_1.buildSystem)("inner", { toolChoiceRequired: true });
|
|
329
|
+
return {
|
|
330
|
+
messages: [{ role: "system", content: systemPrompt }],
|
|
331
|
+
sessionPath: sessionFilePath,
|
|
332
|
+
};
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
// ── Call shared pipeline ──────────────────────────────────────────
|
|
336
|
+
const callbacks = createInnerDialogCallbacks();
|
|
337
|
+
const traceId = (0, nerves_1.createTraceId)();
|
|
338
|
+
const result = await (0, pipeline_1.handleInboundTurn)({
|
|
339
|
+
channel: "inner",
|
|
340
|
+
capabilities: innerCapabilities,
|
|
341
|
+
messages: [userMessage],
|
|
342
|
+
continuityIngressTexts: [],
|
|
343
|
+
callbacks,
|
|
344
|
+
friendResolver: { resolve: () => Promise.resolve(selfContext) },
|
|
345
|
+
sessionLoader,
|
|
346
|
+
pendingDir,
|
|
347
|
+
friendStore: createNoOpFriendStore(),
|
|
348
|
+
enforceTrustGate: trust_gate_1.enforceTrustGate,
|
|
349
|
+
drainPending: pending_1.drainPending,
|
|
350
|
+
runAgent: core_1.runAgent,
|
|
351
|
+
postTurn: context_1.postTurn,
|
|
352
|
+
accumulateFriendTokens: tokens_1.accumulateFriendTokens,
|
|
353
|
+
signal: options?.signal,
|
|
354
|
+
runAgentOptions: {
|
|
355
|
+
traceId,
|
|
356
|
+
toolChoiceRequired: true,
|
|
357
|
+
skipConfirmation: true,
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
const resultMessages = result.messages ?? [];
|
|
361
|
+
const assistantPreview = extractAssistantPreview(resultMessages);
|
|
362
|
+
const toolCalls = extractToolCallNames(resultMessages);
|
|
363
|
+
(0, runtime_1.emitNervesEvent)({
|
|
364
|
+
component: "senses",
|
|
365
|
+
event: "senses.inner_dialog_turn",
|
|
366
|
+
message: "inner dialog turn completed",
|
|
367
|
+
meta: {
|
|
368
|
+
reason,
|
|
369
|
+
session: sessionFilePath,
|
|
370
|
+
...(options?.taskId && { taskId: options.taskId }),
|
|
371
|
+
...(assistantPreview && { assistantPreview }),
|
|
372
|
+
...(toolCalls.length > 0 && { toolCalls }),
|
|
373
|
+
...(result.usage && {
|
|
374
|
+
promptTokens: result.usage.input_tokens,
|
|
375
|
+
completionTokens: result.usage.output_tokens,
|
|
376
|
+
totalTokens: result.usage.total_tokens,
|
|
377
|
+
}),
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
return {
|
|
381
|
+
messages: resultMessages,
|
|
382
|
+
usage: result.usage,
|
|
383
|
+
sessionPath: result.sessionPath ?? sessionFilePath,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
finally {
|
|
387
|
+
writeInnerDialogRuntimeState(sessionFilePath, {
|
|
388
|
+
status: "idle",
|
|
389
|
+
lastCompletedAt: now().toISOString(),
|
|
390
|
+
});
|
|
284
391
|
}
|
|
285
|
-
const userMessage = { role: "user", content: userContent };
|
|
286
|
-
// ── Session loader: wraps existing session logic ──────────────────
|
|
287
|
-
const innerCapabilities = (0, channel_1.getChannelCapabilities)("inner");
|
|
288
|
-
const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)());
|
|
289
|
-
const selfFriend = createSelfFriend((0, identity_1.getAgentName)());
|
|
290
|
-
const selfContext = { friend: selfFriend, channel: innerCapabilities };
|
|
291
|
-
const sessionLoader = {
|
|
292
|
-
loadOrCreate: async () => {
|
|
293
|
-
if (existingMessages.length > 0) {
|
|
294
|
-
return { messages: existingMessages, sessionPath: sessionFilePath };
|
|
295
|
-
}
|
|
296
|
-
// Fresh session: build system prompt
|
|
297
|
-
const systemPrompt = await (0, prompt_1.buildSystem)("inner", { toolChoiceRequired: true });
|
|
298
|
-
return {
|
|
299
|
-
messages: [{ role: "system", content: systemPrompt }],
|
|
300
|
-
sessionPath: sessionFilePath,
|
|
301
|
-
};
|
|
302
|
-
},
|
|
303
|
-
};
|
|
304
|
-
// ── Call shared pipeline ──────────────────────────────────────────
|
|
305
|
-
const callbacks = createInnerDialogCallbacks();
|
|
306
|
-
const traceId = (0, nerves_1.createTraceId)();
|
|
307
|
-
const result = await (0, pipeline_1.handleInboundTurn)({
|
|
308
|
-
channel: "inner",
|
|
309
|
-
capabilities: innerCapabilities,
|
|
310
|
-
messages: [userMessage],
|
|
311
|
-
continuityIngressTexts: [],
|
|
312
|
-
callbacks,
|
|
313
|
-
friendResolver: { resolve: () => Promise.resolve(selfContext) },
|
|
314
|
-
sessionLoader,
|
|
315
|
-
pendingDir,
|
|
316
|
-
friendStore: createNoOpFriendStore(),
|
|
317
|
-
enforceTrustGate: trust_gate_1.enforceTrustGate,
|
|
318
|
-
drainPending: pending_1.drainPending,
|
|
319
|
-
runAgent: core_1.runAgent,
|
|
320
|
-
postTurn: context_1.postTurn,
|
|
321
|
-
accumulateFriendTokens: tokens_1.accumulateFriendTokens,
|
|
322
|
-
signal: options?.signal,
|
|
323
|
-
runAgentOptions: {
|
|
324
|
-
traceId,
|
|
325
|
-
toolChoiceRequired: true,
|
|
326
|
-
skipConfirmation: true,
|
|
327
|
-
},
|
|
328
|
-
});
|
|
329
|
-
const resultMessages = result.messages ?? [];
|
|
330
|
-
const assistantPreview = extractAssistantPreview(resultMessages);
|
|
331
|
-
const toolCalls = extractToolCallNames(resultMessages);
|
|
332
|
-
(0, runtime_1.emitNervesEvent)({
|
|
333
|
-
component: "senses",
|
|
334
|
-
event: "senses.inner_dialog_turn",
|
|
335
|
-
message: "inner dialog turn completed",
|
|
336
|
-
meta: {
|
|
337
|
-
reason,
|
|
338
|
-
session: sessionFilePath,
|
|
339
|
-
...(options?.taskId && { taskId: options.taskId }),
|
|
340
|
-
...(assistantPreview && { assistantPreview }),
|
|
341
|
-
...(toolCalls.length > 0 && { toolCalls }),
|
|
342
|
-
...(result.usage && {
|
|
343
|
-
promptTokens: result.usage.input_tokens,
|
|
344
|
-
completionTokens: result.usage.output_tokens,
|
|
345
|
-
totalTokens: result.usage.total_tokens,
|
|
346
|
-
}),
|
|
347
|
-
},
|
|
348
|
-
});
|
|
349
|
-
return {
|
|
350
|
-
messages: resultMessages,
|
|
351
|
-
usage: result.usage,
|
|
352
|
-
sessionPath: result.sessionPath ?? sessionFilePath,
|
|
353
|
-
};
|
|
354
392
|
}
|