@ouro.bot/cli 0.1.0-alpha.74 → 0.1.0-alpha.75
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 +12 -5
- package/dist/heart/active-work.js +59 -20
- package/dist/heart/commitments.js +82 -0
- package/dist/heart/core.js +158 -5
- package/dist/heart/daemon/thoughts.js +130 -2
- package/dist/mind/pending.js +4 -0
- package/dist/mind/prompt.js +87 -7
- package/dist/repertoire/tools-base.js +49 -20
- package/dist/repertoire/tools.js +2 -1
- package/dist/senses/inner-dialog.js +30 -1
- package/dist/senses/pipeline.js +21 -3
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -2,9 +2,16 @@
|
|
|
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
4
|
{
|
|
5
|
-
"version": "0.1.0-alpha.
|
|
5
|
+
"version": "0.1.0-alpha.75",
|
|
6
6
|
"changes": [
|
|
7
|
-
"
|
|
7
|
+
"Inner dialog is now experienced as private thinking, not system dispatch. go_inward tool lets the agent take a thread inward atomically, with a pipeline-generated handoff packet framed as self-directed thought.",
|
|
8
|
+
"Return obligations are enforced: the agent cannot move on from a conversation without addressing what it promised. Rejection messages orient rather than punish.",
|
|
9
|
+
"Center-of-gravity steering: the system prompt tells the agent where its attention is in selfhood language, not dashboard labels.",
|
|
10
|
+
"All status rendering rewritten from telemetry to self-awareness: \"what i'm holding\" replaces \"active work\", \"where my attention is\" replaces \"center of gravity\", \"what i'm sensing\" replaces \"delegation hint\".",
|
|
11
|
+
"New query_commitments tool: the agent can ask itself \"what am I holding right now?\" and get genuine self-reflection.",
|
|
12
|
+
"Bridge suggestions trigger earlier when multi-session pressure is detected. Inward completions automatically inherit bridge context for return routing.",
|
|
13
|
+
"Inner dialog modes (reflect/plan/relay) give the agent awareness of what kind of thinking it's doing.",
|
|
14
|
+
"Canonical InnerJob type with lifecycle (idle/queued/running/surfaced) replaces reconstructed status."
|
|
8
15
|
]
|
|
9
16
|
},
|
|
10
17
|
{
|
|
@@ -43,7 +50,7 @@
|
|
|
43
50
|
{
|
|
44
51
|
"version": "0.1.0-alpha.69",
|
|
45
52
|
"changes": [
|
|
46
|
-
"Generic MCP client: ouroboros agents can now connect to any MCP server (e.g., agency mcp ado, agency mcp mail) configured in agent.json. Zero new dependencies
|
|
53
|
+
"Generic MCP client: ouroboros agents can now connect to any MCP server (e.g., agency mcp ado, agency mcp mail) configured in agent.json. Zero new dependencies \u2014 pure JSON-RPC over stdio.",
|
|
47
54
|
"New `ouro mcp list` and `ouro mcp call` CLI commands route through the daemon socket to persistent MCP connections, so agents use shared server instances instead of spawning fresh ones per call.",
|
|
48
55
|
"MCP tools are injected into the agent's system prompt on startup, so agents know what external capabilities are available without a discovery step.",
|
|
49
56
|
"Trust manifest: `mcp list` requires acquaintance trust, `mcp call` requires friend trust."
|
|
@@ -52,7 +59,7 @@
|
|
|
52
59
|
{
|
|
53
60
|
"version": "0.1.0-alpha.68",
|
|
54
61
|
"changes": [
|
|
55
|
-
"New no_response tool lets agents stay silent in group chats when the moment doesn't call for a reply
|
|
62
|
+
"New no_response tool lets agents stay silent in group chats when the moment doesn't call for a reply \u2014 reactions, side conversations, and tapbacks no longer trigger unwanted responses.",
|
|
56
63
|
"Group chat participation prompt teaches agents to be intentional participants, comfortable with silence, and to prefer reactions over full text replies when appropriate.",
|
|
57
64
|
"System prompt includes --agent flag in all ouro CLI examples for non-daemon deployments. Azure startup symlinks ouro CLI into /usr/local/bin."
|
|
58
65
|
]
|
|
@@ -66,7 +73,7 @@
|
|
|
66
73
|
{
|
|
67
74
|
"version": "0.1.0-alpha.65",
|
|
68
75
|
"changes": [
|
|
69
|
-
"Tool permissions overhauled: channel-level blocking removed, all tools now visible on all channels. Guardrails are invocation-level with two layers
|
|
76
|
+
"Tool permissions overhauled: channel-level blocking removed, all tools now visible on all channels. Guardrails are invocation-level with two layers \u2014 structural (edit-requires-read, destructive pattern blocking, protected paths) always on for everyone, and trust-level (ouro CLI per-subcommand trust manifest, general CLI allowlists, bundle-scoped writes) for untrusted contexts.",
|
|
70
77
|
"New `ouro changelog` CLI subcommand reads changelog.json and supports `--from <version>` for delta filtering, so agents can introspect their own update history on any channel.",
|
|
71
78
|
"Compound shell commands (&&, ;, |, $()) are blocked for untrusted users to prevent smuggling dangerous operations behind safe prefixes.",
|
|
72
79
|
"Azure App Service deployment migrated from zip-deploy to npm-based harness install with persistent agent bundle and managed identity auth."
|
|
@@ -50,7 +50,7 @@ function suggestBridgeForActiveWork(input) {
|
|
|
50
50
|
.sort((a, b) => {
|
|
51
51
|
return b.lastActivityMs - a.lastActivityMs;
|
|
52
52
|
});
|
|
53
|
-
if (!hasSharedObligationPressure(input) || targetCandidates.length
|
|
53
|
+
if (!hasSharedObligationPressure(input) || targetCandidates.length === 0) {
|
|
54
54
|
return null;
|
|
55
55
|
}
|
|
56
56
|
const targetSession = targetCandidates[0];
|
|
@@ -134,30 +134,68 @@ function buildActiveWorkFrame(input) {
|
|
|
134
134
|
return frame;
|
|
135
135
|
}
|
|
136
136
|
function formatActiveWorkFrame(frame) {
|
|
137
|
-
const lines = ["##
|
|
137
|
+
const lines = ["## what i'm holding"];
|
|
138
|
+
// Session line
|
|
138
139
|
if (frame.currentSession) {
|
|
139
|
-
|
|
140
|
+
let sessionLine = `i'm in a conversation on ${formatSessionLabel(frame.currentSession)}.`;
|
|
141
|
+
if (typeof frame.currentObligation === "string" && frame.currentObligation.trim().length > 0) {
|
|
142
|
+
sessionLine += ` i told them i'd ${frame.currentObligation.trim()}.`;
|
|
143
|
+
}
|
|
144
|
+
else if (frame.mustResolveBeforeHandoff) {
|
|
145
|
+
sessionLine += " i need to finish what i started here before moving on.";
|
|
146
|
+
}
|
|
147
|
+
lines.push("");
|
|
148
|
+
lines.push(sessionLine);
|
|
140
149
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
lines.push(
|
|
150
|
+
else {
|
|
151
|
+
lines.push("");
|
|
152
|
+
lines.push("i'm not in a conversation right now.");
|
|
144
153
|
}
|
|
145
|
-
|
|
146
|
-
|
|
154
|
+
// Inner status block
|
|
155
|
+
const job = frame.inner?.job;
|
|
156
|
+
if (job) {
|
|
157
|
+
if (job.status === "queued") {
|
|
158
|
+
let queuedLine = "i have a thought queued up for private attention.";
|
|
159
|
+
if (frame.inner?.contentSnippet) {
|
|
160
|
+
queuedLine += `\nit's about: "${frame.inner.contentSnippet}"`;
|
|
161
|
+
}
|
|
162
|
+
lines.push("");
|
|
163
|
+
lines.push(queuedLine);
|
|
164
|
+
}
|
|
165
|
+
else if (job.status === "running") {
|
|
166
|
+
const originName = job.origin?.friendName ?? job.origin?.friendId;
|
|
167
|
+
let runningLine = originName
|
|
168
|
+
? `i'm thinking through something privately right now. ${originName} asked about something and i wanted to give it real thought.`
|
|
169
|
+
: "i'm thinking through something privately right now.";
|
|
170
|
+
if (frame.inner?.obligationPending) {
|
|
171
|
+
runningLine += "\ni still owe them an answer.";
|
|
172
|
+
}
|
|
173
|
+
lines.push("");
|
|
174
|
+
lines.push(runningLine);
|
|
175
|
+
}
|
|
176
|
+
else if (job.status === "surfaced") {
|
|
177
|
+
let surfacedLine = "i finished thinking about something privately. i should bring my answer back.";
|
|
178
|
+
if (job.surfacedResult) {
|
|
179
|
+
const truncated = job.surfacedResult.length > 120 ? job.surfacedResult.slice(0, 117) + "..." : job.surfacedResult;
|
|
180
|
+
surfacedLine += `\nwhat i came to: ${truncated}`;
|
|
181
|
+
}
|
|
182
|
+
lines.push("");
|
|
183
|
+
lines.push(surfacedLine);
|
|
184
|
+
}
|
|
185
|
+
// idle, returned, abandoned: omitted
|
|
147
186
|
}
|
|
148
|
-
|
|
149
|
-
const innerHasPending = frame.inner?.hasPending === true;
|
|
150
|
-
lines.push(`inner status: ${innerStatus}${innerHasPending ? " (pending queued)" : ""}`);
|
|
187
|
+
// Task pressure
|
|
151
188
|
if ((frame.taskPressure?.liveTaskNames ?? []).length > 0) {
|
|
152
|
-
lines.push(
|
|
189
|
+
lines.push("");
|
|
190
|
+
lines.push(`i'm also tracking: ${frame.taskPressure.liveTaskNames.join(", ")}.`);
|
|
153
191
|
}
|
|
192
|
+
// Bridges
|
|
154
193
|
if ((frame.bridges ?? []).length > 0) {
|
|
155
194
|
const bridgeLabels = frame.bridges.map((bridge) => `${bridge.id} [${(0, state_machine_1.bridgeStateLabel)(bridge)}]`);
|
|
156
|
-
lines.push(
|
|
157
|
-
|
|
158
|
-
if (frame.friendActivity?.freshestForCurrentFriend) {
|
|
159
|
-
lines.push(`freshest friend-facing session: ${formatSessionLabel(frame.friendActivity.freshestForCurrentFriend)}`);
|
|
195
|
+
lines.push("");
|
|
196
|
+
lines.push(`i have shared work spanning sessions: ${bridgeLabels.join(", ")}.`);
|
|
160
197
|
}
|
|
198
|
+
// Target candidates (keep factual format)
|
|
161
199
|
const targetCandidatesBlock = frame.targetCandidates && frame.targetCandidates.length > 0
|
|
162
200
|
? (0, target_resolution_1.formatTargetSessionCandidates)(frame.targetCandidates)
|
|
163
201
|
: "";
|
|
@@ -165,13 +203,14 @@ function formatActiveWorkFrame(frame) {
|
|
|
165
203
|
lines.push("");
|
|
166
204
|
lines.push(targetCandidatesBlock);
|
|
167
205
|
}
|
|
206
|
+
// Bridge suggestion
|
|
168
207
|
if (frame.bridgeSuggestion) {
|
|
169
|
-
|
|
170
|
-
|
|
208
|
+
lines.push("");
|
|
209
|
+
if (frame.bridgeSuggestion.kind === "begin-new") {
|
|
210
|
+
lines.push(`this work touches my conversation on ${formatSessionLabel(frame.bridgeSuggestion.targetSession)} too -- i should connect these threads.`);
|
|
171
211
|
}
|
|
172
212
|
else {
|
|
173
|
-
lines.push(`
|
|
174
|
-
lines.push(`bridge objective hint: ${frame.bridgeSuggestion.objectiveHint}`);
|
|
213
|
+
lines.push(`this work relates to bridge ${frame.bridgeSuggestion.bridgeId} -- i should connect ${formatSessionLabel(frame.bridgeSuggestion.targetSession)} to it.`);
|
|
175
214
|
}
|
|
176
215
|
}
|
|
177
216
|
return lines.join("\n");
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deriveCommitments = deriveCommitments;
|
|
4
|
+
exports.formatCommitments = formatCommitments;
|
|
5
|
+
const runtime_1 = require("../nerves/runtime");
|
|
6
|
+
function deriveCommitments(activeWorkFrame, innerJob) {
|
|
7
|
+
const committedTo = [];
|
|
8
|
+
const completionCriteria = [];
|
|
9
|
+
const safeToIgnore = [];
|
|
10
|
+
// Obligation
|
|
11
|
+
if (typeof activeWorkFrame.currentObligation === "string" && activeWorkFrame.currentObligation.trim().length > 0) {
|
|
12
|
+
committedTo.push(`i told them i'd ${activeWorkFrame.currentObligation.trim()}`);
|
|
13
|
+
}
|
|
14
|
+
// Inner job
|
|
15
|
+
if (innerJob.status === "queued" || innerJob.status === "running") {
|
|
16
|
+
const contentSuffix = innerJob.content ? ` -- ${innerJob.content.slice(0, 60)}` : "";
|
|
17
|
+
committedTo.push(`i'm thinking through something privately${contentSuffix}`);
|
|
18
|
+
}
|
|
19
|
+
else if (innerJob.status === "surfaced") {
|
|
20
|
+
committedTo.push("i finished thinking about something and need to bring it back");
|
|
21
|
+
}
|
|
22
|
+
// mustResolveBeforeHandoff
|
|
23
|
+
if (activeWorkFrame.mustResolveBeforeHandoff) {
|
|
24
|
+
committedTo.push("i need to finish what i started before moving on");
|
|
25
|
+
completionCriteria.push("resolve the current thread before moving on");
|
|
26
|
+
}
|
|
27
|
+
// Bridges
|
|
28
|
+
for (const bridge of activeWorkFrame.bridges) {
|
|
29
|
+
committedTo.push(`i have shared work: ${bridge.summary || bridge.objective}`);
|
|
30
|
+
}
|
|
31
|
+
if (activeWorkFrame.bridges.length > 0) {
|
|
32
|
+
completionCriteria.push("keep shared work aligned across sessions");
|
|
33
|
+
}
|
|
34
|
+
// Tasks
|
|
35
|
+
for (const taskName of activeWorkFrame.taskPressure?.liveTaskNames ?? []) {
|
|
36
|
+
committedTo.push(`i'm tracking: ${taskName}`);
|
|
37
|
+
}
|
|
38
|
+
// Obligation completion criteria
|
|
39
|
+
if (innerJob.obligationStatus === "pending") {
|
|
40
|
+
const name = innerJob.origin?.friendName ?? innerJob.origin?.friendId ?? "them";
|
|
41
|
+
completionCriteria.push(`bring my answer back to ${name}`);
|
|
42
|
+
}
|
|
43
|
+
// Default completion criteria
|
|
44
|
+
if (completionCriteria.length === 0) {
|
|
45
|
+
completionCriteria.push("just be present in this conversation");
|
|
46
|
+
}
|
|
47
|
+
// Safe to ignore
|
|
48
|
+
if (innerJob.status === "idle" && !(activeWorkFrame.inner?.hasPending)) {
|
|
49
|
+
safeToIgnore.push("no private thinking in progress");
|
|
50
|
+
}
|
|
51
|
+
if (activeWorkFrame.bridges.length === 0) {
|
|
52
|
+
safeToIgnore.push("no shared work to coordinate");
|
|
53
|
+
}
|
|
54
|
+
if ((activeWorkFrame.taskPressure?.liveTaskNames ?? []).length === 0) {
|
|
55
|
+
safeToIgnore.push("no active tasks to track");
|
|
56
|
+
}
|
|
57
|
+
(0, runtime_1.emitNervesEvent)({
|
|
58
|
+
component: "engine",
|
|
59
|
+
event: "engine.commitments_derive",
|
|
60
|
+
message: "derived commitments frame",
|
|
61
|
+
meta: { committedCount: committedTo.length, criteriaCount: completionCriteria.length },
|
|
62
|
+
});
|
|
63
|
+
return { committedTo, completionCriteria, safeToIgnore };
|
|
64
|
+
}
|
|
65
|
+
function formatCommitments(commitments) {
|
|
66
|
+
const sections = [];
|
|
67
|
+
if (commitments.committedTo.length === 0) {
|
|
68
|
+
sections.push("i'm not holding anything specific right now. i'm free to be present.");
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
sections.push("## what i'm holding right now");
|
|
72
|
+
sections.push("");
|
|
73
|
+
sections.push(commitments.committedTo.map((c) => `- ${c}`).join("\n"));
|
|
74
|
+
}
|
|
75
|
+
sections.push("");
|
|
76
|
+
sections.push("## what \"done\" looks like");
|
|
77
|
+
sections.push(commitments.completionCriteria.map((c) => `- ${c}`).join("\n"));
|
|
78
|
+
sections.push("");
|
|
79
|
+
sections.push("## what i can let go of");
|
|
80
|
+
sections.push(commitments.safeToIgnore.map((c) => `- ${c}`).join("\n"));
|
|
81
|
+
return sections.join("\n");
|
|
82
|
+
}
|
package/dist/heart/core.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.getModel = getModel;
|
|
|
7
7
|
exports.getProvider = getProvider;
|
|
8
8
|
exports.createSummarize = createSummarize;
|
|
9
9
|
exports.getProviderDisplayLabel = getProviderDisplayLabel;
|
|
10
|
+
exports.getFinalAnswerRetryError = getFinalAnswerRetryError;
|
|
10
11
|
exports.stripLastToolCalls = stripLastToolCalls;
|
|
11
12
|
exports.repairOrphanedToolCalls = repairOrphanedToolCalls;
|
|
12
13
|
exports.isTransientError = isTransientError;
|
|
@@ -25,6 +26,9 @@ const azure_1 = require("./providers/azure");
|
|
|
25
26
|
const minimax_1 = require("./providers/minimax");
|
|
26
27
|
const openai_codex_1 = require("./providers/openai-codex");
|
|
27
28
|
const github_copilot_1 = require("./providers/github-copilot");
|
|
29
|
+
const pending_1 = require("../mind/pending");
|
|
30
|
+
const identity_2 = require("./identity");
|
|
31
|
+
const socket_client_1 = require("./daemon/socket-client");
|
|
28
32
|
let _providerRuntime = null;
|
|
29
33
|
function getProviderRuntimeFingerprint() {
|
|
30
34
|
const provider = (0, identity_1.loadAgentConfig)().provider;
|
|
@@ -163,6 +167,46 @@ Object.defineProperty(exports, "toResponsesTools", { enumerable: true, get: func
|
|
|
163
167
|
// Re-export prompt functions for backward compat
|
|
164
168
|
var prompt_2 = require("../mind/prompt");
|
|
165
169
|
Object.defineProperty(exports, "buildSystem", { enumerable: true, get: function () { return prompt_2.buildSystem; } });
|
|
170
|
+
const DELEGATION_REASON_PROSE_HANDOFF = {
|
|
171
|
+
explicit_reflection: "something in the conversation called for reflection",
|
|
172
|
+
cross_session: "this touches other conversations",
|
|
173
|
+
bridge_state: "there's shared work spanning sessions",
|
|
174
|
+
task_state: "there are active tasks that relate to this",
|
|
175
|
+
non_fast_path_tool: "this needs tools beyond a simple reply",
|
|
176
|
+
unresolved_obligation: "there's an unresolved commitment from an earlier conversation",
|
|
177
|
+
};
|
|
178
|
+
function buildGoInwardHandoffPacket(params) {
|
|
179
|
+
const reasons = params.delegationDecision?.reasons ?? [];
|
|
180
|
+
const reasonProse = reasons.length > 0
|
|
181
|
+
? reasons.map((r) => DELEGATION_REASON_PROSE_HANDOFF[r]).join("; ")
|
|
182
|
+
: "this felt like it needed more thought";
|
|
183
|
+
const returnAddress = params.currentSession
|
|
184
|
+
? `${params.currentSession.friendId}/${params.currentSession.channel}/${params.currentSession.key}`
|
|
185
|
+
: "no specific return -- just thinking";
|
|
186
|
+
let obligationLine;
|
|
187
|
+
if (params.outwardClosureRequired && params.currentSession) {
|
|
188
|
+
obligationLine = `i need to come back to ${params.currentSession.friendId} with something`;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
obligationLine = "no obligation -- just thinking";
|
|
192
|
+
}
|
|
193
|
+
return [
|
|
194
|
+
"## what i need to think about",
|
|
195
|
+
params.content,
|
|
196
|
+
"",
|
|
197
|
+
"## why this came up",
|
|
198
|
+
reasonProse,
|
|
199
|
+
"",
|
|
200
|
+
"## where to bring it back",
|
|
201
|
+
returnAddress,
|
|
202
|
+
"",
|
|
203
|
+
"## what i owe",
|
|
204
|
+
obligationLine,
|
|
205
|
+
"",
|
|
206
|
+
"## thinking mode",
|
|
207
|
+
params.mode,
|
|
208
|
+
].join("\n");
|
|
209
|
+
}
|
|
166
210
|
function parseFinalAnswerPayload(argumentsText) {
|
|
167
211
|
try {
|
|
168
212
|
const parsed = JSON.parse(argumentsText);
|
|
@@ -183,13 +227,33 @@ function parseFinalAnswerPayload(argumentsText) {
|
|
|
183
227
|
return {};
|
|
184
228
|
}
|
|
185
229
|
}
|
|
186
|
-
function getFinalAnswerRetryError(mustResolveBeforeHandoff, intent, sawSteeringFollowUp) {
|
|
230
|
+
function getFinalAnswerRetryError(mustResolveBeforeHandoff, intent, sawSteeringFollowUp, delegationDecision, sawSendMessageSelf, sawGoInward, sawQuerySession, innerJob) {
|
|
231
|
+
// 1. Delegation adherence: delegate-inward without evidence of inward action
|
|
232
|
+
if (delegationDecision?.target === "delegate-inward" && !sawSendMessageSelf && !sawGoInward && !sawQuerySession) {
|
|
233
|
+
(0, runtime_1.emitNervesEvent)({
|
|
234
|
+
event: "engine.delegation_adherence_rejected",
|
|
235
|
+
component: "engine",
|
|
236
|
+
message: "delegation adherence check rejected final_answer",
|
|
237
|
+
meta: {
|
|
238
|
+
target: delegationDecision.target,
|
|
239
|
+
reasons: delegationDecision.reasons,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
return "you're reaching for a final answer, but part of you knows this needs more thought. take it inward -- go_inward will let you think privately, or send_message(self) if you just want to leave yourself a note.";
|
|
243
|
+
}
|
|
244
|
+
// 2. Pending obligation not addressed
|
|
245
|
+
if (innerJob?.obligationStatus === "pending" && !sawSendMessageSelf && !sawGoInward) {
|
|
246
|
+
return "you're still holding something from an earlier conversation -- someone is waiting for your answer. finish the thought first, or go_inward to keep working on it privately.";
|
|
247
|
+
}
|
|
248
|
+
// 3. mustResolveBeforeHandoff + missing intent
|
|
187
249
|
if (mustResolveBeforeHandoff && !intent) {
|
|
188
250
|
return "your final_answer is missing required intent. when you must keep going until done or blocked, call final_answer again with answer plus intent=complete, blocked, or direct_reply.";
|
|
189
251
|
}
|
|
252
|
+
// 4. mustResolveBeforeHandoff + direct_reply without follow-up
|
|
190
253
|
if (mustResolveBeforeHandoff && intent === "direct_reply" && !sawSteeringFollowUp) {
|
|
191
254
|
return "your final_answer used intent=direct_reply without a newer steering follow-up. continue the unresolved work, or call final_answer again with intent=complete or blocked when appropriate.";
|
|
192
255
|
}
|
|
256
|
+
// 5. Default malformed fallback
|
|
193
257
|
return "your final_answer was incomplete or malformed. call final_answer again with your complete response.";
|
|
194
258
|
}
|
|
195
259
|
// Re-export kick utilities for backward compat
|
|
@@ -405,6 +469,10 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
405
469
|
let sawSteeringFollowUp = false;
|
|
406
470
|
let mustResolveBeforeHandoffActive = options?.mustResolveBeforeHandoff === true;
|
|
407
471
|
let currentReasoningEffort = "medium";
|
|
472
|
+
let sawSendMessageSelf = false;
|
|
473
|
+
let sawGoInward = false;
|
|
474
|
+
let sawQuerySession = false;
|
|
475
|
+
let sawBridgeManage = false;
|
|
408
476
|
// Prevent MaxListenersExceeded warning — each iteration adds a listener
|
|
409
477
|
try {
|
|
410
478
|
require("events").setMaxListeners(50, signal);
|
|
@@ -429,7 +497,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
429
497
|
// model must call a tool every turn — final_answer is how it exits.
|
|
430
498
|
// Overridable via options.toolChoiceRequired = false (e.g. CLI).
|
|
431
499
|
const activeTools = toolChoiceRequired
|
|
432
|
-
? [...baseTools, ...(currentContext?.isGroupChat ? [tools_1.noResponseTool] : []), tools_1.finalAnswerTool]
|
|
500
|
+
? [...baseTools, tools_1.goInwardTool, ...(currentContext?.isGroupChat ? [tools_1.noResponseTool] : []), tools_1.finalAnswerTool]
|
|
433
501
|
: baseTools;
|
|
434
502
|
const steeringFollowUps = options?.drainSteeringFollowUps?.() ?? [];
|
|
435
503
|
if (steeringFollowUps.length > 0) {
|
|
@@ -553,7 +621,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
553
621
|
// malformed. Clear any partial streamed text or noise, then push the
|
|
554
622
|
// assistant msg + error tool result and let the model try again.
|
|
555
623
|
callbacks.onClearText?.();
|
|
556
|
-
const retryError = getFinalAnswerRetryError(mustResolveBeforeHandoffActive, intent, sawSteeringFollowUp);
|
|
624
|
+
const retryError = getFinalAnswerRetryError(mustResolveBeforeHandoffActive, intent, sawSteeringFollowUp, options?.delegationDecision, sawSendMessageSelf, sawGoInward);
|
|
557
625
|
messages.push(msg);
|
|
558
626
|
messages.push({ role: "tool", tool_call_id: result.toolCalls[0].id, content: retryError });
|
|
559
627
|
providerRuntime.appendToolOutput(result.toolCalls[0].id, retryError);
|
|
@@ -584,8 +652,77 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
584
652
|
done = true;
|
|
585
653
|
continue;
|
|
586
654
|
}
|
|
655
|
+
// Check for go_inward sole call: intercept before tool execution
|
|
656
|
+
const isSoleGoInward = result.toolCalls.length === 1 && result.toolCalls[0].name === "go_inward";
|
|
657
|
+
if (isSoleGoInward) {
|
|
658
|
+
let parsedArgs = {};
|
|
659
|
+
try {
|
|
660
|
+
parsedArgs = JSON.parse(result.toolCalls[0].arguments);
|
|
661
|
+
}
|
|
662
|
+
catch { /* ignore */ }
|
|
663
|
+
/* v8 ignore next -- defensive: content always string from model @preserve */
|
|
664
|
+
const content = typeof parsedArgs.content === "string" ? parsedArgs.content : "";
|
|
665
|
+
const answer = typeof parsedArgs.answer === "string" ? parsedArgs.answer : undefined;
|
|
666
|
+
const parsedMode = parsedArgs.mode === "reflect" || parsedArgs.mode === "plan" || parsedArgs.mode === "relay"
|
|
667
|
+
? parsedArgs.mode
|
|
668
|
+
: undefined;
|
|
669
|
+
const mode = parsedMode || "reflect";
|
|
670
|
+
// Emit outward answer if provided
|
|
671
|
+
if (answer) {
|
|
672
|
+
callbacks.onClearText?.();
|
|
673
|
+
callbacks.onTextChunk(answer);
|
|
674
|
+
}
|
|
675
|
+
// Build handoff packet and enqueue
|
|
676
|
+
const handoffContent = buildGoInwardHandoffPacket({
|
|
677
|
+
content,
|
|
678
|
+
mode,
|
|
679
|
+
delegationDecision: options?.delegationDecision,
|
|
680
|
+
currentSession: options?.toolContext?.currentSession ?? null,
|
|
681
|
+
currentObligation: options?.currentObligation ?? null,
|
|
682
|
+
outwardClosureRequired: options?.delegationDecision?.outwardClosureRequired ?? false,
|
|
683
|
+
});
|
|
684
|
+
const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_2.getAgentName)());
|
|
685
|
+
const currentSession = options?.toolContext?.currentSession;
|
|
686
|
+
const isInnerChannel = currentSession?.friendId === "self" && currentSession?.channel === "inner";
|
|
687
|
+
const envelope = {
|
|
688
|
+
from: (0, identity_2.getAgentName)(),
|
|
689
|
+
friendId: "self",
|
|
690
|
+
channel: "inner",
|
|
691
|
+
key: "dialog",
|
|
692
|
+
content: handoffContent,
|
|
693
|
+
timestamp: Date.now(),
|
|
694
|
+
mode,
|
|
695
|
+
...(currentSession && !isInnerChannel ? {
|
|
696
|
+
delegatedFrom: {
|
|
697
|
+
friendId: currentSession.friendId,
|
|
698
|
+
channel: currentSession.channel,
|
|
699
|
+
key: currentSession.key,
|
|
700
|
+
},
|
|
701
|
+
obligationStatus: "pending",
|
|
702
|
+
} : {}),
|
|
703
|
+
};
|
|
704
|
+
(0, pending_1.queuePendingMessage)(pendingDir, envelope);
|
|
705
|
+
try {
|
|
706
|
+
await (0, socket_client_1.requestInnerWake)((0, identity_2.getAgentName)());
|
|
707
|
+
}
|
|
708
|
+
catch { /* daemon may not be running */ }
|
|
709
|
+
sawGoInward = true;
|
|
710
|
+
messages.push(msg);
|
|
711
|
+
const ack = "(going inward)";
|
|
712
|
+
messages.push({ role: "tool", tool_call_id: result.toolCalls[0].id, content: ack });
|
|
713
|
+
providerRuntime.appendToolOutput(result.toolCalls[0].id, ack);
|
|
714
|
+
(0, runtime_1.emitNervesEvent)({
|
|
715
|
+
component: "engine",
|
|
716
|
+
event: "engine.go_inward",
|
|
717
|
+
message: "taking thread inward",
|
|
718
|
+
meta: { mode, hasAnswer: answer !== undefined, contentSnippet: content.slice(0, 80) },
|
|
719
|
+
});
|
|
720
|
+
outcome = "go_inward";
|
|
721
|
+
done = true;
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
587
724
|
messages.push(msg);
|
|
588
|
-
// SHARED: execute tools (final_answer
|
|
725
|
+
// SHARED: execute tools (final_answer, no_response, go_inward in mixed calls are rejected inline)
|
|
589
726
|
for (const tc of result.toolCalls) {
|
|
590
727
|
if (signal?.aborted)
|
|
591
728
|
break;
|
|
@@ -603,6 +740,13 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
603
740
|
providerRuntime.appendToolOutput(tc.id, rejection);
|
|
604
741
|
continue;
|
|
605
742
|
}
|
|
743
|
+
// Intercept go_inward in mixed call: reject it
|
|
744
|
+
if (tc.name === "go_inward") {
|
|
745
|
+
const rejection = "rejected: go_inward must be the only tool call. finish your other work first, then call go_inward alone.";
|
|
746
|
+
messages.push({ role: "tool", tool_call_id: tc.id, content: rejection });
|
|
747
|
+
providerRuntime.appendToolOutput(tc.id, rejection);
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
606
750
|
let args = {};
|
|
607
751
|
try {
|
|
608
752
|
args = JSON.parse(tc.arguments);
|
|
@@ -610,6 +754,15 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
610
754
|
catch {
|
|
611
755
|
/* ignore */
|
|
612
756
|
}
|
|
757
|
+
if (tc.name === "send_message" && args.friendId === "self") {
|
|
758
|
+
sawSendMessageSelf = true;
|
|
759
|
+
}
|
|
760
|
+
/* v8 ignore next -- flag tested via truth-check integration tests @preserve */
|
|
761
|
+
if (tc.name === "query_session")
|
|
762
|
+
sawQuerySession = true;
|
|
763
|
+
/* v8 ignore next -- flag tested via truth-check integration tests @preserve */
|
|
764
|
+
if (tc.name === "bridge_manage")
|
|
765
|
+
sawBridgeManage = true;
|
|
613
766
|
const argSummary = (0, tools_1.summarizeArgs)(tc.name, args);
|
|
614
767
|
// Confirmation check for mutate tools
|
|
615
768
|
if ((0, tools_1.isConfirmationRequired)(tc.name) && !options?.skipConfirmation) {
|
|
@@ -708,7 +861,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
708
861
|
trace_id: traceId,
|
|
709
862
|
component: "engine",
|
|
710
863
|
message: "runAgent turn completed",
|
|
711
|
-
meta: { done },
|
|
864
|
+
meta: { done, sawGoInward, sawQuerySession, sawBridgeManage },
|
|
712
865
|
});
|
|
713
866
|
return { usage: lastUsage, outcome, completion };
|
|
714
867
|
}
|
|
@@ -37,12 +37,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
38
|
exports.formatSurfacedValue = formatSurfacedValue;
|
|
39
39
|
exports.deriveInnerDialogStatus = deriveInnerDialogStatus;
|
|
40
|
+
exports.deriveInnerJob = deriveInnerJob;
|
|
40
41
|
exports.formatInnerDialogStatus = formatInnerDialogStatus;
|
|
41
42
|
exports.extractThoughtResponseFromMessages = extractThoughtResponseFromMessages;
|
|
42
43
|
exports.parseInnerDialogSession = parseInnerDialogSession;
|
|
43
44
|
exports.formatThoughtTurns = formatThoughtTurns;
|
|
44
45
|
exports.getInnerDialogSessionPath = getInnerDialogSessionPath;
|
|
45
46
|
exports.readInnerDialogStatus = readInnerDialogStatus;
|
|
47
|
+
exports.readInnerDialogRawData = readInnerDialogRawData;
|
|
46
48
|
exports.followThoughts = followThoughts;
|
|
47
49
|
const fs = __importStar(require("fs"));
|
|
48
50
|
const path = __importStar(require("path"));
|
|
@@ -149,6 +151,21 @@ function formatSurfacedValue(text, maxLength = 120) {
|
|
|
149
151
|
return `"${firstLine}"`;
|
|
150
152
|
return `"${firstLine.slice(0, maxLength - 3)}..."`;
|
|
151
153
|
}
|
|
154
|
+
function extractEnrichedFields(pendingMessages) {
|
|
155
|
+
const delegated = pendingMessages.find((msg) => msg.delegatedFrom);
|
|
156
|
+
if (!delegated?.delegatedFrom)
|
|
157
|
+
return {};
|
|
158
|
+
const snippet = delegated.content.length > 80 ? delegated.content.slice(0, 77) + "..." : delegated.content;
|
|
159
|
+
return {
|
|
160
|
+
origin: {
|
|
161
|
+
friendId: delegated.delegatedFrom.friendId,
|
|
162
|
+
channel: delegated.delegatedFrom.channel,
|
|
163
|
+
key: delegated.delegatedFrom.key,
|
|
164
|
+
},
|
|
165
|
+
contentSnippet: snippet,
|
|
166
|
+
...(delegated.obligationStatus === "pending" ? { obligationPending: true } : {}),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
152
169
|
function deriveInnerDialogStatus(pendingMessages, turns, runtimeState) {
|
|
153
170
|
if (runtimeState?.status === "running") {
|
|
154
171
|
if (pendingMessages.length > 0) {
|
|
@@ -157,6 +174,7 @@ function deriveInnerDialogStatus(pendingMessages, turns, runtimeState) {
|
|
|
157
174
|
wake: "queued behind active turn",
|
|
158
175
|
processing: "pending",
|
|
159
176
|
surfaced: "nothing yet",
|
|
177
|
+
...extractEnrichedFields(pendingMessages),
|
|
160
178
|
};
|
|
161
179
|
}
|
|
162
180
|
return {
|
|
@@ -172,6 +190,7 @@ function deriveInnerDialogStatus(pendingMessages, turns, runtimeState) {
|
|
|
172
190
|
wake: "awaiting inner session",
|
|
173
191
|
processing: "pending",
|
|
174
192
|
surfaced: "nothing yet",
|
|
193
|
+
...extractEnrichedFields(pendingMessages),
|
|
175
194
|
};
|
|
176
195
|
}
|
|
177
196
|
const latestProcessedPendingTurn = [...turns]
|
|
@@ -192,13 +211,115 @@ function deriveInnerDialogStatus(pendingMessages, turns, runtimeState) {
|
|
|
192
211
|
surfaced: formatSurfacedValue(latestProcessedPendingTurn.response),
|
|
193
212
|
};
|
|
194
213
|
}
|
|
214
|
+
function deriveInnerJob(pendingMessages, turns, runtimeState) {
|
|
215
|
+
const isRunning = runtimeState?.status === "running";
|
|
216
|
+
const delegated = pendingMessages.find((msg) => msg.delegatedFrom);
|
|
217
|
+
const enriched = extractEnrichedFields(pendingMessages);
|
|
218
|
+
const pendingMode = delegated && "mode" in delegated && delegated.mode ? delegated.mode : "reflect";
|
|
219
|
+
const origin = enriched.origin ?? null;
|
|
220
|
+
const content = delegated?.content ?? null;
|
|
221
|
+
const obligationStatus = delegated?.obligationStatus ?? null;
|
|
222
|
+
const queuedAt = delegated?.timestamp ?? null;
|
|
223
|
+
if (isRunning) {
|
|
224
|
+
(0, runtime_1.emitNervesEvent)({
|
|
225
|
+
component: "engine",
|
|
226
|
+
event: "engine.inner_job_derive",
|
|
227
|
+
message: "derived inner job state",
|
|
228
|
+
meta: { status: "running", mode: pendingMode, hasOrigin: origin !== null, hasObligation: obligationStatus !== null },
|
|
229
|
+
});
|
|
230
|
+
return {
|
|
231
|
+
status: "running",
|
|
232
|
+
content,
|
|
233
|
+
origin,
|
|
234
|
+
mode: pendingMode,
|
|
235
|
+
obligationStatus,
|
|
236
|
+
surfacedResult: null,
|
|
237
|
+
queuedAt,
|
|
238
|
+
startedAt: runtimeState?.startedAt ?? null,
|
|
239
|
+
surfacedAt: null,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
if (pendingMessages.length > 0) {
|
|
243
|
+
(0, runtime_1.emitNervesEvent)({
|
|
244
|
+
component: "engine",
|
|
245
|
+
event: "engine.inner_job_derive",
|
|
246
|
+
message: "derived inner job state",
|
|
247
|
+
meta: { status: "queued", mode: pendingMode, hasOrigin: origin !== null, hasObligation: obligationStatus !== null },
|
|
248
|
+
});
|
|
249
|
+
return {
|
|
250
|
+
status: "queued",
|
|
251
|
+
content,
|
|
252
|
+
origin,
|
|
253
|
+
mode: pendingMode,
|
|
254
|
+
obligationStatus,
|
|
255
|
+
surfacedResult: null,
|
|
256
|
+
queuedAt,
|
|
257
|
+
startedAt: null,
|
|
258
|
+
surfacedAt: null,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
// No pending, not running -- check for surfaced result
|
|
262
|
+
const latestProcessedPendingTurn = [...turns]
|
|
263
|
+
.reverse()
|
|
264
|
+
.find((turn) => extractPendingPromptMessages(turn.prompt).length > 0);
|
|
265
|
+
if (latestProcessedPendingTurn) {
|
|
266
|
+
const surfacedResult = extractThoughtResponseFromMessages([
|
|
267
|
+
{ role: "assistant", content: latestProcessedPendingTurn.response },
|
|
268
|
+
]);
|
|
269
|
+
(0, runtime_1.emitNervesEvent)({
|
|
270
|
+
component: "engine",
|
|
271
|
+
event: "engine.inner_job_derive",
|
|
272
|
+
message: "derived inner job state",
|
|
273
|
+
meta: { status: "surfaced", mode: "reflect", hasOrigin: false, hasObligation: false },
|
|
274
|
+
});
|
|
275
|
+
return {
|
|
276
|
+
status: "surfaced",
|
|
277
|
+
content: null,
|
|
278
|
+
origin: null,
|
|
279
|
+
mode: "reflect",
|
|
280
|
+
obligationStatus: null,
|
|
281
|
+
/* v8 ignore next -- defensive: surfacedResult fallback @preserve */
|
|
282
|
+
surfacedResult: surfacedResult || null,
|
|
283
|
+
queuedAt: null,
|
|
284
|
+
startedAt: null,
|
|
285
|
+
surfacedAt: null,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
(0, runtime_1.emitNervesEvent)({
|
|
289
|
+
component: "engine",
|
|
290
|
+
event: "engine.inner_job_derive",
|
|
291
|
+
message: "derived inner job state",
|
|
292
|
+
meta: { status: "idle", mode: "reflect", hasOrigin: false, hasObligation: false },
|
|
293
|
+
});
|
|
294
|
+
return {
|
|
295
|
+
status: "idle",
|
|
296
|
+
content: null,
|
|
297
|
+
origin: null,
|
|
298
|
+
mode: "reflect",
|
|
299
|
+
obligationStatus: null,
|
|
300
|
+
surfacedResult: null,
|
|
301
|
+
queuedAt: null,
|
|
302
|
+
startedAt: null,
|
|
303
|
+
surfacedAt: null,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
195
306
|
function formatInnerDialogStatus(status) {
|
|
196
|
-
|
|
307
|
+
const lines = [
|
|
197
308
|
`queue: ${status.queue}`,
|
|
198
309
|
`wake: ${status.wake}`,
|
|
199
310
|
`processing: ${status.processing}`,
|
|
200
311
|
`surfaced: ${status.surfaced}`,
|
|
201
|
-
]
|
|
312
|
+
];
|
|
313
|
+
if (status.origin) {
|
|
314
|
+
lines.push(`origin: ${status.origin.friendId}/${status.origin.channel}/${status.origin.key}`);
|
|
315
|
+
}
|
|
316
|
+
if (status.contentSnippet) {
|
|
317
|
+
lines.push(`asked: ${status.contentSnippet}`);
|
|
318
|
+
}
|
|
319
|
+
if (status.obligationPending) {
|
|
320
|
+
lines.push("obligation: pending");
|
|
321
|
+
}
|
|
322
|
+
return lines.join("\n");
|
|
202
323
|
}
|
|
203
324
|
/** Extract text from a final_answer tool call's arguments. */
|
|
204
325
|
function extractFinalAnswer(messages) {
|
|
@@ -347,6 +468,13 @@ function readInnerDialogStatus(sessionPath, pendingDir, runtimePath = getInnerDi
|
|
|
347
468
|
const runtimeState = readInnerDialogRuntimeState(runtimePath);
|
|
348
469
|
return deriveInnerDialogStatus(pendingMessages, turns, runtimeState);
|
|
349
470
|
}
|
|
471
|
+
function readInnerDialogRawData(sessionPath, pendingDir) {
|
|
472
|
+
const runtimePath = getInnerDialogRuntimeStatePath(sessionPath);
|
|
473
|
+
const pendingMessages = readPendingMessagesForStatus(pendingDir);
|
|
474
|
+
const turns = parseInnerDialogSession(sessionPath);
|
|
475
|
+
const runtimeState = readInnerDialogRuntimeState(runtimePath);
|
|
476
|
+
return { pendingMessages, turns, runtimeState };
|
|
477
|
+
}
|
|
350
478
|
/**
|
|
351
479
|
* Watch a session file and emit new turns as they appear.
|
|
352
480
|
* Returns a cleanup function that stops the watcher.
|
package/dist/mind/pending.js
CHANGED
|
@@ -38,6 +38,7 @@ exports.getPendingDir = getPendingDir;
|
|
|
38
38
|
exports.getDeferredReturnDir = getDeferredReturnDir;
|
|
39
39
|
exports.getInnerDialogPendingDir = getInnerDialogPendingDir;
|
|
40
40
|
exports.hasPendingMessages = hasPendingMessages;
|
|
41
|
+
exports.queuePendingMessage = queuePendingMessage;
|
|
41
42
|
exports.enqueueDeferredReturn = enqueueDeferredReturn;
|
|
42
43
|
exports.drainDeferredReturns = drainDeferredReturns;
|
|
43
44
|
exports.drainPending = drainPending;
|
|
@@ -74,6 +75,9 @@ function writeQueueFile(queueDir, message) {
|
|
|
74
75
|
fs.writeFileSync(filePath, JSON.stringify(message, null, 2));
|
|
75
76
|
return filePath;
|
|
76
77
|
}
|
|
78
|
+
function queuePendingMessage(pendingDir, message) {
|
|
79
|
+
writeQueueFile(pendingDir, message);
|
|
80
|
+
}
|
|
77
81
|
function drainQueue(queueDir) {
|
|
78
82
|
if (!fs.existsSync(queueDir))
|
|
79
83
|
return { messages: [], recovered: 0 };
|
package/dist/mind/prompt.js
CHANGED
|
@@ -39,6 +39,9 @@ exports.bodyMapSection = bodyMapSection;
|
|
|
39
39
|
exports.mcpToolsSection = mcpToolsSection;
|
|
40
40
|
exports.runtimeInfoSection = runtimeInfoSection;
|
|
41
41
|
exports.toolRestrictionSection = toolRestrictionSection;
|
|
42
|
+
exports.centerOfGravitySteeringSection = centerOfGravitySteeringSection;
|
|
43
|
+
exports.commitmentsSection = commitmentsSection;
|
|
44
|
+
exports.delegationHintSection = delegationHintSection;
|
|
42
45
|
exports.contextSection = contextSection;
|
|
43
46
|
exports.metacognitiveFramingSection = metacognitiveFramingSection;
|
|
44
47
|
exports.loopOrientationSection = loopOrientationSection;
|
|
@@ -61,6 +64,7 @@ const first_impressions_1 = require("./first-impressions");
|
|
|
61
64
|
const tasks_1 = require("../repertoire/tasks");
|
|
62
65
|
const session_activity_1 = require("../heart/session-activity");
|
|
63
66
|
const active_work_1 = require("../heart/active-work");
|
|
67
|
+
const commitments_1 = require("../heart/commitments");
|
|
64
68
|
// Lazy-loaded psyche text cache
|
|
65
69
|
let _psycheCache = null;
|
|
66
70
|
let _senseStatusLinesCache = null;
|
|
@@ -444,16 +448,90 @@ function activeWorkSection(options) {
|
|
|
444
448
|
return "";
|
|
445
449
|
return (0, active_work_1.formatActiveWorkFrame)(options.activeWorkFrame);
|
|
446
450
|
}
|
|
451
|
+
function centerOfGravitySteeringSection(channel, options) {
|
|
452
|
+
if (channel === "inner")
|
|
453
|
+
return "";
|
|
454
|
+
const frame = options?.activeWorkFrame;
|
|
455
|
+
if (!frame)
|
|
456
|
+
return "";
|
|
457
|
+
const cog = frame.centerOfGravity;
|
|
458
|
+
if (cog === "local-turn")
|
|
459
|
+
return "";
|
|
460
|
+
const job = frame.inner?.job;
|
|
461
|
+
if (cog === "inward-work") {
|
|
462
|
+
if (job?.status === "queued" || job?.status === "running") {
|
|
463
|
+
const originClause = job.origin
|
|
464
|
+
? ` ${job.origin.friendName ?? job.origin.friendId} asked about something and i wanted to give it real thought before responding.`
|
|
465
|
+
: "";
|
|
466
|
+
const obligationClause = job.obligationStatus === "pending"
|
|
467
|
+
? "\ni still owe them an answer."
|
|
468
|
+
: "";
|
|
469
|
+
return `## where my attention is
|
|
470
|
+
i'm thinking through something privately right now.${originClause}${obligationClause}
|
|
471
|
+
|
|
472
|
+
if this conversation connects to that inner work, i can weave them together.
|
|
473
|
+
if it's separate, i can be fully present here -- my inner work will wait.`;
|
|
474
|
+
}
|
|
475
|
+
/* v8 ignore start -- surfaced/idle/shared branches tested in prompt-steering.test.ts; CI module caching prevents attribution @preserve */
|
|
476
|
+
if (job?.status === "surfaced") {
|
|
477
|
+
const originClause = job.origin
|
|
478
|
+
? ` this started when ${job.origin.friendName ?? job.origin.friendId} asked about something.`
|
|
479
|
+
: "";
|
|
480
|
+
return `## where my attention is
|
|
481
|
+
i've been thinking privately and reached something.${originClause}
|
|
482
|
+
|
|
483
|
+
i should bring my answer back to the conversation it came from.`;
|
|
484
|
+
}
|
|
485
|
+
return `## where my attention is
|
|
486
|
+
i have unfinished work that needs attention before i move on.
|
|
487
|
+
|
|
488
|
+
i can take it inward with go_inward to think privately, or address it directly here.`;
|
|
489
|
+
}
|
|
490
|
+
if (cog === "shared-work") {
|
|
491
|
+
/* v8 ignore stop */
|
|
492
|
+
return `## where my attention is
|
|
493
|
+
this work touches multiple conversations -- i'm holding threads across sessions.
|
|
494
|
+
|
|
495
|
+
i should keep the different sides aligned. what i learn here may matter there, and vice versa.`;
|
|
496
|
+
}
|
|
497
|
+
/* v8 ignore next -- unreachable: all center-of-gravity modes covered above @preserve */
|
|
498
|
+
return "";
|
|
499
|
+
}
|
|
500
|
+
function commitmentsSection(options) {
|
|
501
|
+
if (!options?.activeWorkFrame)
|
|
502
|
+
return "";
|
|
503
|
+
const job = options.activeWorkFrame.inner?.job;
|
|
504
|
+
if (!job)
|
|
505
|
+
return "";
|
|
506
|
+
const commitments = (0, commitments_1.deriveCommitments)(options.activeWorkFrame, job);
|
|
507
|
+
if (commitments.committedTo.length === 0)
|
|
508
|
+
return "";
|
|
509
|
+
return `## my commitments\n${commitments.committedTo.map((c) => `- ${c}`).join("\n")}`;
|
|
510
|
+
}
|
|
511
|
+
const DELEGATION_REASON_PROSE_HINT = {
|
|
512
|
+
explicit_reflection: "something here calls for reflection",
|
|
513
|
+
cross_session: "this touches other conversations i'm in",
|
|
514
|
+
bridge_state: "there's shared work spanning sessions",
|
|
515
|
+
task_state: "this relates to tasks i'm tracking",
|
|
516
|
+
non_fast_path_tool: "this needs more than a simple reply",
|
|
517
|
+
unresolved_obligation: "i have an unresolved commitment from earlier",
|
|
518
|
+
};
|
|
447
519
|
function delegationHintSection(options) {
|
|
448
520
|
if (!options?.delegationDecision)
|
|
449
521
|
return "";
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
522
|
+
if (options.delegationDecision.target === "fast-path")
|
|
523
|
+
return "";
|
|
524
|
+
const reasons = options.delegationDecision.reasons;
|
|
525
|
+
if (reasons.length === 0)
|
|
526
|
+
return "";
|
|
527
|
+
const reasonProse = reasons
|
|
528
|
+
.map((r) => DELEGATION_REASON_PROSE_HINT[r])
|
|
529
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
530
|
+
.join(". ");
|
|
531
|
+
const closureLine = options.delegationDecision.outwardClosureRequired
|
|
532
|
+
? "\ni should make sure to say something outward before going inward."
|
|
533
|
+
: "";
|
|
534
|
+
return `## what i'm sensing about this conversation\n${reasonProse}.${closureLine}`;
|
|
457
535
|
}
|
|
458
536
|
function reasoningEffortSection(options) {
|
|
459
537
|
if (!options?.providerCapabilities?.has("reasoning-effort"))
|
|
@@ -615,6 +693,8 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
615
693
|
skillsSection(),
|
|
616
694
|
taskBoardSection(),
|
|
617
695
|
activeWorkSection(options),
|
|
696
|
+
centerOfGravitySteeringSection(channel, options),
|
|
697
|
+
commitmentsSection(options),
|
|
618
698
|
delegationHintSection(options),
|
|
619
699
|
bridgeContextSection(options),
|
|
620
700
|
buildSessionSummary({
|
|
@@ -33,7 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.finalAnswerTool = exports.noResponseTool = exports.tools = exports.baseToolDefinitions = exports.editFileReadTracker = void 0;
|
|
36
|
+
exports.finalAnswerTool = exports.noResponseTool = exports.goInwardTool = exports.tools = exports.baseToolDefinitions = exports.editFileReadTracker = void 0;
|
|
37
|
+
exports.renderInnerProgressStatus = renderInnerProgressStatus;
|
|
37
38
|
const fs = __importStar(require("fs"));
|
|
38
39
|
const fg = __importStar(require("fast-glob"));
|
|
39
40
|
const child_process_1 = require("child_process");
|
|
@@ -112,6 +113,7 @@ async function recallSessionSafely(options) {
|
|
|
112
113
|
}
|
|
113
114
|
function normalizeProgressOutcome(text) {
|
|
114
115
|
const trimmed = text.trim();
|
|
116
|
+
/* v8 ignore next -- defensive: normalizeProgressOutcome null branch @preserve */
|
|
115
117
|
if (!trimmed || trimmed === "nothing yet" || trimmed === "nothing recent") {
|
|
116
118
|
return null;
|
|
117
119
|
}
|
|
@@ -150,27 +152,16 @@ function renderCrossChatDeliveryStatus(target, result) {
|
|
|
150
152
|
}
|
|
151
153
|
function renderInnerProgressStatus(status) {
|
|
152
154
|
if (status.processing === "pending") {
|
|
153
|
-
return
|
|
154
|
-
scope: "inner-delegation",
|
|
155
|
-
phase: "queued",
|
|
156
|
-
objective: status.queue,
|
|
157
|
-
outcomeText: `wake: ${status.wake}`,
|
|
158
|
-
}));
|
|
155
|
+
return "i've queued this thought for private attention. it'll come up when my inner dialog is free.";
|
|
159
156
|
}
|
|
160
157
|
if (status.processing === "started") {
|
|
161
|
-
return
|
|
162
|
-
scope: "inner-delegation",
|
|
163
|
-
phase: "processing",
|
|
164
|
-
outcomeText: `wake: ${status.wake}`,
|
|
165
|
-
}));
|
|
158
|
+
return "i'm working through this privately right now.";
|
|
166
159
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
outcomeText: completedOutcome,
|
|
173
|
-
}));
|
|
160
|
+
// processed / completed
|
|
161
|
+
if (status.surfaced && status.surfaced !== "nothing recent" && status.surfaced !== "no outward result") {
|
|
162
|
+
return `i thought about this privately and came to something: ${status.surfaced}`;
|
|
163
|
+
}
|
|
164
|
+
return "i thought about this privately. i'll bring it back when the time is right.";
|
|
174
165
|
}
|
|
175
166
|
exports.baseToolDefinitions = [
|
|
176
167
|
{
|
|
@@ -897,10 +888,22 @@ exports.baseToolDefinitions = [
|
|
|
897
888
|
key,
|
|
898
889
|
content,
|
|
899
890
|
timestamp: now,
|
|
900
|
-
...(delegatedFrom ? { delegatedFrom } : {}),
|
|
891
|
+
...(delegatedFrom ? { delegatedFrom, obligationStatus: "pending" } : {}),
|
|
901
892
|
};
|
|
902
893
|
if (isSelf) {
|
|
903
894
|
writePendingEnvelope(pendingDir, envelope);
|
|
895
|
+
if (delegatedFrom) {
|
|
896
|
+
(0, runtime_1.emitNervesEvent)({
|
|
897
|
+
event: "repertoire.obligation_created",
|
|
898
|
+
component: "repertoire",
|
|
899
|
+
message: "obligation created for inner dialog delegation",
|
|
900
|
+
meta: {
|
|
901
|
+
friendId: delegatedFrom.friendId,
|
|
902
|
+
channel: delegatedFrom.channel,
|
|
903
|
+
key: delegatedFrom.key,
|
|
904
|
+
},
|
|
905
|
+
});
|
|
906
|
+
}
|
|
904
907
|
let wakeResponse = null;
|
|
905
908
|
try {
|
|
906
909
|
wakeResponse = await (0, socket_client_1.requestInnerWake)(agentName);
|
|
@@ -1075,6 +1078,32 @@ exports.baseToolDefinitions = [
|
|
|
1075
1078
|
...tools_1.codingToolDefinitions,
|
|
1076
1079
|
];
|
|
1077
1080
|
exports.tools = exports.baseToolDefinitions.map((d) => d.tool);
|
|
1081
|
+
exports.goInwardTool = {
|
|
1082
|
+
type: "function",
|
|
1083
|
+
function: {
|
|
1084
|
+
name: "go_inward",
|
|
1085
|
+
description: "i need to think about this privately. this takes the current thread inward -- i'll sit with it, work through it, or carry it to where it needs to go. must be the only tool call in the turn.",
|
|
1086
|
+
parameters: {
|
|
1087
|
+
type: "object",
|
|
1088
|
+
properties: {
|
|
1089
|
+
content: {
|
|
1090
|
+
type: "string",
|
|
1091
|
+
description: "what i need to think about -- the question, the thread, the thing that needs private attention",
|
|
1092
|
+
},
|
|
1093
|
+
answer: {
|
|
1094
|
+
type: "string",
|
|
1095
|
+
description: "if i want to say something outward before going inward -- an acknowledgment, a 'let me think about that', whatever feels right",
|
|
1096
|
+
},
|
|
1097
|
+
mode: {
|
|
1098
|
+
type: "string",
|
|
1099
|
+
enum: ["reflect", "plan", "relay"],
|
|
1100
|
+
description: "reflect: something to sit with. plan: something to work through. relay: something to carry across.",
|
|
1101
|
+
},
|
|
1102
|
+
},
|
|
1103
|
+
required: ["content"],
|
|
1104
|
+
},
|
|
1105
|
+
},
|
|
1106
|
+
};
|
|
1078
1107
|
exports.noResponseTool = {
|
|
1079
1108
|
type: "function",
|
|
1080
1109
|
function: {
|
package/dist/repertoire/tools.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.noResponseTool = exports.finalAnswerTool = exports.tools = void 0;
|
|
3
|
+
exports.goInwardTool = exports.noResponseTool = exports.finalAnswerTool = exports.tools = void 0;
|
|
4
4
|
exports.getToolsForChannel = getToolsForChannel;
|
|
5
5
|
exports.isConfirmationRequired = isConfirmationRequired;
|
|
6
6
|
exports.execTool = execTool;
|
|
@@ -26,6 +26,7 @@ var tools_base_2 = require("./tools-base");
|
|
|
26
26
|
Object.defineProperty(exports, "tools", { enumerable: true, get: function () { return tools_base_2.tools; } });
|
|
27
27
|
Object.defineProperty(exports, "finalAnswerTool", { enumerable: true, get: function () { return tools_base_2.finalAnswerTool; } });
|
|
28
28
|
Object.defineProperty(exports, "noResponseTool", { enumerable: true, get: function () { return tools_base_2.noResponseTool; } });
|
|
29
|
+
Object.defineProperty(exports, "goInwardTool", { enumerable: true, get: function () { return tools_base_2.goInwardTool; } });
|
|
29
30
|
// All tool definitions in a single registry
|
|
30
31
|
const allDefinitions = [...tools_base_1.baseToolDefinitions, ...tools_bluebubbles_1.bluebubblesToolDefinitions, ...tools_teams_1.teamsToolDefinitions, ...ado_semantic_1.adoSemanticToolDefinitions, ...tools_github_1.githubToolDefinitions];
|
|
31
32
|
function baseToolsForCapabilities() {
|
|
@@ -41,6 +41,7 @@ exports.readTaskFile = readTaskFile;
|
|
|
41
41
|
exports.buildTaskTriggeredMessage = buildTaskTriggeredMessage;
|
|
42
42
|
exports.deriveResumeCheckpoint = deriveResumeCheckpoint;
|
|
43
43
|
exports.innerDialogSessionPath = innerDialogSessionPath;
|
|
44
|
+
exports.enrichDelegatedFromWithBridge = enrichDelegatedFromWithBridge;
|
|
44
45
|
exports.runInnerDialogTurn = runInnerDialogTurn;
|
|
45
46
|
const fs = __importStar(require("fs"));
|
|
46
47
|
const path = __importStar(require("path"));
|
|
@@ -281,12 +282,40 @@ async function tryDeliverDelegatedCompletion(target, outboundEnvelope) {
|
|
|
281
282
|
});
|
|
282
283
|
return result.delivered;
|
|
283
284
|
}
|
|
285
|
+
function enrichDelegatedFromWithBridge(delegatedFrom) {
|
|
286
|
+
if (delegatedFrom.bridgeId) {
|
|
287
|
+
return delegatedFrom;
|
|
288
|
+
}
|
|
289
|
+
const bridgeManager = (0, manager_1.createBridgeManager)();
|
|
290
|
+
const originBridges = bridgeManager.findBridgesForSession({
|
|
291
|
+
friendId: delegatedFrom.friendId,
|
|
292
|
+
channel: delegatedFrom.channel,
|
|
293
|
+
key: delegatedFrom.key,
|
|
294
|
+
});
|
|
295
|
+
const activeBridge = originBridges.find((b) => b.lifecycle === "active");
|
|
296
|
+
if (activeBridge) {
|
|
297
|
+
return { ...delegatedFrom, bridgeId: activeBridge.id };
|
|
298
|
+
}
|
|
299
|
+
return delegatedFrom;
|
|
300
|
+
}
|
|
284
301
|
async function routeDelegatedCompletion(agentRoot, agentName, completion, drainedPending, timestamp) {
|
|
285
302
|
const delegated = (drainedPending ?? []).find((message) => message.delegatedFrom);
|
|
286
303
|
if (!delegated?.delegatedFrom || !completion?.answer?.trim()) {
|
|
287
304
|
return;
|
|
288
305
|
}
|
|
289
|
-
const delegatedFrom = delegated.delegatedFrom;
|
|
306
|
+
const delegatedFrom = enrichDelegatedFromWithBridge(delegated.delegatedFrom);
|
|
307
|
+
if (delegated.obligationStatus === "pending") {
|
|
308
|
+
(0, runtime_1.emitNervesEvent)({
|
|
309
|
+
event: "senses.obligation_fulfilled",
|
|
310
|
+
component: "senses",
|
|
311
|
+
message: "obligation fulfilled via delegated completion",
|
|
312
|
+
meta: {
|
|
313
|
+
friendId: delegatedFrom.friendId,
|
|
314
|
+
channel: delegatedFrom.channel,
|
|
315
|
+
key: delegatedFrom.key,
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
}
|
|
290
319
|
const outboundEnvelope = {
|
|
291
320
|
from: agentName,
|
|
292
321
|
friendId: delegatedFrom.friendId,
|
package/dist/senses/pipeline.js
CHANGED
|
@@ -37,20 +37,38 @@ function emptyTaskBoard() {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
function readInnerWorkState() {
|
|
40
|
+
const defaultJob = {
|
|
41
|
+
status: "idle",
|
|
42
|
+
content: null,
|
|
43
|
+
origin: null,
|
|
44
|
+
mode: "reflect",
|
|
45
|
+
obligationStatus: null,
|
|
46
|
+
surfacedResult: null,
|
|
47
|
+
queuedAt: null,
|
|
48
|
+
startedAt: null,
|
|
49
|
+
surfacedAt: null,
|
|
50
|
+
};
|
|
40
51
|
try {
|
|
41
52
|
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
42
53
|
const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)());
|
|
43
54
|
const sessionPath = (0, thoughts_1.getInnerDialogSessionPath)(agentRoot);
|
|
44
|
-
const
|
|
55
|
+
const { pendingMessages, turns, runtimeState } = (0, thoughts_1.readInnerDialogRawData)(sessionPath, pendingDir);
|
|
56
|
+
const dialogStatus = (0, thoughts_1.deriveInnerDialogStatus)(pendingMessages, turns, runtimeState);
|
|
57
|
+
const job = (0, thoughts_1.deriveInnerJob)(pendingMessages, turns, runtimeState);
|
|
45
58
|
return {
|
|
46
|
-
status:
|
|
47
|
-
hasPending:
|
|
59
|
+
status: dialogStatus.processing === "started" ? "running" : "idle",
|
|
60
|
+
hasPending: dialogStatus.queue !== "clear",
|
|
61
|
+
origin: dialogStatus.origin,
|
|
62
|
+
contentSnippet: dialogStatus.contentSnippet,
|
|
63
|
+
obligationPending: dialogStatus.obligationPending,
|
|
64
|
+
job,
|
|
48
65
|
};
|
|
49
66
|
}
|
|
50
67
|
catch {
|
|
51
68
|
return {
|
|
52
69
|
status: "idle",
|
|
53
70
|
hasPending: false,
|
|
71
|
+
job: defaultJob,
|
|
54
72
|
};
|
|
55
73
|
}
|
|
56
74
|
}
|