@ouro.bot/cli 0.1.0-alpha.15 → 0.1.0-alpha.150
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +2 -2
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +151 -205
- package/changelog.json +985 -0
- package/dist/heart/active-work.js +633 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/commitments.js +105 -0
- package/dist/heart/config.js +66 -21
- package/dist/heart/core.js +645 -112
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/auth-flow.js +457 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/daemon-cli.js +2301 -205
- package/dist/heart/daemon/daemon-entry.js +131 -4
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon-tombstone.js +106 -0
- package/dist/heart/daemon/daemon.js +372 -3
- package/dist/heart/daemon/habit-migration.js +181 -0
- package/dist/heart/daemon/habit-parser.js +119 -0
- package/dist/heart/daemon/habit-scheduler.js +252 -0
- package/dist/heart/daemon/hatch-animation.js +10 -3
- package/dist/heart/daemon/hatch-flow.js +28 -111
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/launchd.js +159 -0
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/mcp-server.js +591 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-path-installer.js +78 -34
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/process-manager.js +75 -0
- package/dist/heart/daemon/run-hooks.js +37 -0
- package/dist/heart/daemon/runtime-logging.js +58 -15
- package/dist/heart/daemon/runtime-metadata.js +219 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/sense-manager.js +50 -2
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +202 -0
- package/dist/heart/daemon/specialist-orchestrator.js +2 -2
- package/dist/heart/daemon/specialist-prompt.js +10 -7
- package/dist/heart/daemon/specialist-tools.js +53 -4
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +507 -0
- package/dist/heart/daemon/update-checker.js +111 -0
- package/dist/heart/daemon/update-hooks.js +138 -0
- package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/identity.js +64 -21
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +197 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +159 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +195 -34
- package/dist/heart/providers/azure.js +115 -9
- package/dist/heart/providers/github-copilot.js +157 -0
- package/dist/heart/providers/minimax.js +33 -3
- package/dist/heart/providers/openai-codex.js +49 -14
- package/dist/heart/session-activity.js +173 -0
- package/dist/heart/session-recall.js +216 -0
- package/dist/heart/streaming.js +117 -33
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +122 -32
- package/dist/mind/bundle-manifest.js +14 -0
- package/dist/mind/context.js +125 -14
- package/dist/mind/{memory.js → diary.js} +70 -56
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +44 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/store-file.js +19 -0
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/obligations.js +134 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +607 -88
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/index.js +59 -2
- package/dist/nerves/runtime.js +5 -1
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/ado-semantic.js +6 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +254 -0
- package/dist/repertoire/coding/feedback.js +301 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +210 -4
- package/dist/repertoire/coding/spawner.js +58 -12
- package/dist/repertoire/coding/tools.js +176 -4
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/guardrails.js +290 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +198 -0
- package/dist/repertoire/skills.js +14 -23
- package/dist/repertoire/tasks/board.js +12 -0
- package/dist/repertoire/tasks/index.js +23 -9
- package/dist/repertoire/tasks/parser.js +0 -2
- package/dist/repertoire/tasks/transitions.js +1 -20
- package/dist/repertoire/tools-base.js +990 -273
- package/dist/repertoire/tools-bluebubbles.js +94 -0
- package/dist/repertoire/tools-github.js +1 -6
- package/dist/repertoire/tools-teams.js +65 -59
- package/dist/repertoire/tools.js +281 -91
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +97 -0
- package/dist/senses/bluebubbles-client.js +258 -5
- package/dist/senses/bluebubbles-entry.js +2 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +339 -0
- package/dist/senses/bluebubbles-model.js +12 -4
- package/dist/senses/bluebubbles-mutation-log.js +45 -5
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +981 -45
- package/dist/senses/cli-entry.js +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +374 -131
- package/dist/senses/contextual-heartbeat.js +106 -0
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/debug-activity.js +154 -0
- package/dist/senses/inner-dialog-worker.js +112 -19
- package/dist/senses/inner-dialog.js +621 -84
- package/dist/senses/pipeline.js +463 -0
- package/dist/senses/shared-turn.js +196 -0
- package/dist/senses/surface-tool.js +95 -0
- package/dist/senses/teams.js +796 -141
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +12 -4
- package/skills/configure-dev-tools.md +81 -0
- package/subagents/README.md +4 -70
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/subagents/work-doer.md +0 -233
- package/subagents/work-merger.md +0 -624
- package/subagents/work-planner.md +0 -373
|
@@ -33,19 +33,302 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.
|
|
36
|
+
exports.restTool = exports.settleTool = exports.observeTool = exports.ponderTool = exports.tools = exports.baseToolDefinitions = exports.editFileReadTracker = void 0;
|
|
37
|
+
exports.renderInnerProgressStatus = renderInnerProgressStatus;
|
|
37
38
|
const fs = __importStar(require("fs"));
|
|
39
|
+
const fg = __importStar(require("fast-glob"));
|
|
38
40
|
const child_process_1 = require("child_process");
|
|
39
41
|
const path = __importStar(require("path"));
|
|
40
42
|
const skills_1 = require("./skills");
|
|
41
43
|
const config_1 = require("../heart/config");
|
|
42
44
|
const runtime_1 = require("../nerves/runtime");
|
|
43
45
|
const identity_1 = require("../heart/identity");
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
+
const identity_2 = require("../heart/identity");
|
|
47
|
+
const socket_client_1 = require("../heart/daemon/socket-client");
|
|
48
|
+
const thoughts_1 = require("../heart/daemon/thoughts");
|
|
49
|
+
const manager_1 = require("../heart/bridges/manager");
|
|
50
|
+
const session_recall_1 = require("../heart/session-recall");
|
|
51
|
+
const session_activity_1 = require("../heart/session-activity");
|
|
52
|
+
const active_work_1 = require("../heart/active-work");
|
|
46
53
|
const tools_1 = require("./coding/tools");
|
|
47
|
-
const
|
|
48
|
-
const
|
|
54
|
+
const coding_1 = require("./coding");
|
|
55
|
+
const diary_1 = require("../mind/diary");
|
|
56
|
+
const tasks_1 = require("./tasks");
|
|
57
|
+
const pending_1 = require("../mind/pending");
|
|
58
|
+
const obligations_1 = require("../mind/obligations");
|
|
59
|
+
const progress_story_1 = require("../heart/progress-story");
|
|
60
|
+
const cross_chat_delivery_1 = require("../heart/cross-chat-delivery");
|
|
61
|
+
const obligations_2 = require("../heart/obligations");
|
|
62
|
+
// Tracks which file paths have been read via read_file in this session.
|
|
63
|
+
// edit_file requires a file to be read first (must-read-first guard).
|
|
64
|
+
exports.editFileReadTracker = new Set();
|
|
65
|
+
function buildContextDiff(lines, changeStart, changeEnd, contextSize = 3) {
|
|
66
|
+
const start = Math.max(0, changeStart - contextSize);
|
|
67
|
+
const end = Math.min(lines.length, changeEnd + contextSize);
|
|
68
|
+
const result = [];
|
|
69
|
+
for (let i = start; i < end; i++) {
|
|
70
|
+
const lineNum = i + 1;
|
|
71
|
+
const prefix = (i >= changeStart && i < changeEnd) ? ">" : " ";
|
|
72
|
+
result.push(`${prefix} ${lineNum} | ${lines[i]}`);
|
|
73
|
+
}
|
|
74
|
+
return result.join("\n");
|
|
75
|
+
}
|
|
76
|
+
function resolveLocalToolPath(targetPath) {
|
|
77
|
+
if (!path.isAbsolute(targetPath)) {
|
|
78
|
+
return path.resolve((0, identity_2.getRepoRoot)(), targetPath);
|
|
79
|
+
}
|
|
80
|
+
return targetPath;
|
|
81
|
+
}
|
|
82
|
+
const NO_SESSION_FOUND_MESSAGE = "no session found for that friend/channel/key combination.";
|
|
83
|
+
const EMPTY_SESSION_MESSAGE = "session exists but has no non-system messages.";
|
|
84
|
+
function findDelegatingBridgeId(ctx) {
|
|
85
|
+
const currentSession = ctx?.currentSession;
|
|
86
|
+
if (!currentSession)
|
|
87
|
+
return undefined;
|
|
88
|
+
return ctx?.activeBridges?.find((bridge) => bridge.lifecycle === "active"
|
|
89
|
+
&& bridge.attachedSessions.some((session) => session.friendId === currentSession.friendId
|
|
90
|
+
&& session.channel === currentSession.channel
|
|
91
|
+
&& session.key === currentSession.key))?.id;
|
|
92
|
+
}
|
|
93
|
+
async function recallSessionSafely(options) {
|
|
94
|
+
try {
|
|
95
|
+
return await (0, session_recall_1.recallSession)(options);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (options.summarize) {
|
|
99
|
+
(0, runtime_1.emitNervesEvent)({
|
|
100
|
+
component: "daemon",
|
|
101
|
+
event: "daemon.session_recall_summary_fallback",
|
|
102
|
+
message: "session recall summarization failed; using raw transcript",
|
|
103
|
+
meta: {
|
|
104
|
+
friendId: options.friendId,
|
|
105
|
+
channel: options.channel,
|
|
106
|
+
key: options.key,
|
|
107
|
+
error: error instanceof Error ? error.message : String(error),
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
try {
|
|
111
|
+
return await (0, session_recall_1.recallSession)({
|
|
112
|
+
...options,
|
|
113
|
+
summarize: undefined,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return { kind: "missing" };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { kind: "missing" };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function searchSessionSafely(options) {
|
|
124
|
+
try {
|
|
125
|
+
return await (0, session_recall_1.searchSessionTranscript)(options);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return { kind: "missing" };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function normalizeProgressOutcome(text) {
|
|
132
|
+
const trimmed = text.trim();
|
|
133
|
+
/* v8 ignore next -- defensive: normalizeProgressOutcome null branch @preserve */
|
|
134
|
+
if (!trimmed || trimmed === "nothing yet" || trimmed === "nothing recent") {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
if (trimmed.startsWith("\"") && trimmed.endsWith("\"") && trimmed.length >= 2) {
|
|
138
|
+
return trimmed.slice(1, -1);
|
|
139
|
+
}
|
|
140
|
+
return trimmed;
|
|
141
|
+
}
|
|
142
|
+
function writePendingEnvelope(queueDir, message) {
|
|
143
|
+
fs.mkdirSync(queueDir, { recursive: true });
|
|
144
|
+
const fileName = `${message.timestamp}-${Math.random().toString(36).slice(2, 10)}.json`;
|
|
145
|
+
const filePath = path.join(queueDir, fileName);
|
|
146
|
+
fs.writeFileSync(filePath, JSON.stringify(message, null, 2));
|
|
147
|
+
}
|
|
148
|
+
function renderCrossChatDeliveryStatus(target, result) {
|
|
149
|
+
const phase = result.status === "delivered_now"
|
|
150
|
+
? "completed"
|
|
151
|
+
: result.status === "queued_for_later"
|
|
152
|
+
? "queued"
|
|
153
|
+
: result.status === "blocked"
|
|
154
|
+
? "blocked"
|
|
155
|
+
: "errored";
|
|
156
|
+
const lead = result.status === "delivered_now"
|
|
157
|
+
? "delivered now"
|
|
158
|
+
: result.status === "queued_for_later"
|
|
159
|
+
? "queued for later"
|
|
160
|
+
: result.status === "blocked"
|
|
161
|
+
? "blocked"
|
|
162
|
+
: "failed";
|
|
163
|
+
return (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
164
|
+
scope: "shared-work",
|
|
165
|
+
phase,
|
|
166
|
+
objective: `message to ${target}`,
|
|
167
|
+
outcomeText: `${lead}\n${result.detail}`,
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
function emptyTaskBoard() {
|
|
171
|
+
return {
|
|
172
|
+
compact: "",
|
|
173
|
+
full: "",
|
|
174
|
+
byStatus: {
|
|
175
|
+
drafting: [],
|
|
176
|
+
processing: [],
|
|
177
|
+
validating: [],
|
|
178
|
+
collaborating: [],
|
|
179
|
+
paused: [],
|
|
180
|
+
blocked: [],
|
|
181
|
+
done: [],
|
|
182
|
+
},
|
|
183
|
+
actionRequired: [],
|
|
184
|
+
unresolvedDependencies: [],
|
|
185
|
+
activeSessions: [],
|
|
186
|
+
activeBridges: [],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function isLiveCodingSessionStatus(status) {
|
|
190
|
+
return status === "spawning"
|
|
191
|
+
|| status === "running"
|
|
192
|
+
|| status === "waiting_input"
|
|
193
|
+
|| status === "stalled";
|
|
194
|
+
}
|
|
195
|
+
function readActiveWorkInnerState() {
|
|
196
|
+
const defaultJob = {
|
|
197
|
+
status: "idle",
|
|
198
|
+
content: null,
|
|
199
|
+
origin: null,
|
|
200
|
+
mode: "reflect",
|
|
201
|
+
obligationStatus: null,
|
|
202
|
+
surfacedResult: null,
|
|
203
|
+
queuedAt: null,
|
|
204
|
+
startedAt: null,
|
|
205
|
+
surfacedAt: null,
|
|
206
|
+
};
|
|
207
|
+
try {
|
|
208
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
209
|
+
const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)());
|
|
210
|
+
const sessionPath = (0, thoughts_1.getInnerDialogSessionPath)(agentRoot);
|
|
211
|
+
const { pendingMessages, turns, runtimeState } = (0, thoughts_1.readInnerDialogRawData)(sessionPath, pendingDir);
|
|
212
|
+
const dialogStatus = (0, thoughts_1.deriveInnerDialogStatus)(pendingMessages, turns, runtimeState);
|
|
213
|
+
const job = (0, thoughts_1.deriveInnerJob)(pendingMessages, turns, runtimeState);
|
|
214
|
+
const storeObligationPending = (0, obligations_2.readPendingObligations)(agentRoot).length > 0;
|
|
215
|
+
return {
|
|
216
|
+
status: dialogStatus.processing === "started" ? "running" : "idle",
|
|
217
|
+
hasPending: dialogStatus.queue !== "clear",
|
|
218
|
+
origin: dialogStatus.origin,
|
|
219
|
+
contentSnippet: dialogStatus.contentSnippet,
|
|
220
|
+
obligationPending: dialogStatus.obligationPending || storeObligationPending,
|
|
221
|
+
job,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return {
|
|
226
|
+
status: "idle",
|
|
227
|
+
hasPending: false,
|
|
228
|
+
job: defaultJob,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function buildToolActiveWorkFrame(ctx) {
|
|
233
|
+
const currentSession = ctx?.currentSession
|
|
234
|
+
? {
|
|
235
|
+
friendId: ctx.currentSession.friendId,
|
|
236
|
+
channel: ctx.currentSession.channel,
|
|
237
|
+
key: ctx.currentSession.key,
|
|
238
|
+
sessionPath: (0, config_1.resolveSessionPath)(ctx.currentSession.friendId, ctx.currentSession.channel, ctx.currentSession.key),
|
|
239
|
+
}
|
|
240
|
+
: null;
|
|
241
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
242
|
+
const bridges = currentSession
|
|
243
|
+
? (0, manager_1.createBridgeManager)().findBridgesForSession({
|
|
244
|
+
friendId: currentSession.friendId,
|
|
245
|
+
channel: currentSession.channel,
|
|
246
|
+
key: currentSession.key,
|
|
247
|
+
})
|
|
248
|
+
: [];
|
|
249
|
+
let friendActivity = [];
|
|
250
|
+
try {
|
|
251
|
+
friendActivity = (0, session_activity_1.listSessionActivity)({
|
|
252
|
+
sessionsDir: `${agentRoot}/state/sessions`,
|
|
253
|
+
friendsDir: `${agentRoot}/friends`,
|
|
254
|
+
agentName: (0, identity_1.getAgentName)(),
|
|
255
|
+
currentSession,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
friendActivity = [];
|
|
260
|
+
}
|
|
261
|
+
const pendingObligations = (() => {
|
|
262
|
+
try {
|
|
263
|
+
return (0, obligations_2.readPendingObligations)(agentRoot);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
})();
|
|
269
|
+
let codingSessions = [];
|
|
270
|
+
let otherCodingSessions = [];
|
|
271
|
+
try {
|
|
272
|
+
const liveCodingSessions = (0, coding_1.getCodingSessionManager)()
|
|
273
|
+
.listSessions()
|
|
274
|
+
.filter((session) => isLiveCodingSessionStatus(session.status) && Boolean(session.originSession));
|
|
275
|
+
if (currentSession) {
|
|
276
|
+
codingSessions = liveCodingSessions.filter((session) => session.originSession?.friendId === currentSession.friendId
|
|
277
|
+
&& session.originSession.channel === currentSession.channel
|
|
278
|
+
&& session.originSession.key === currentSession.key);
|
|
279
|
+
otherCodingSessions = liveCodingSessions.filter((session) => !(session.originSession?.friendId === currentSession.friendId
|
|
280
|
+
&& session.originSession.channel === currentSession.channel
|
|
281
|
+
&& session.originSession.key === currentSession.key));
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
codingSessions = [];
|
|
285
|
+
otherCodingSessions = liveCodingSessions;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
codingSessions = [];
|
|
290
|
+
otherCodingSessions = [];
|
|
291
|
+
}
|
|
292
|
+
const currentObligation = currentSession
|
|
293
|
+
? pendingObligations.find((obligation) => obligation.status !== "fulfilled"
|
|
294
|
+
&& obligation.origin.friendId === currentSession.friendId
|
|
295
|
+
&& obligation.origin.channel === currentSession.channel
|
|
296
|
+
&& obligation.origin.key === currentSession.key)?.content ?? null
|
|
297
|
+
: null;
|
|
298
|
+
return (0, active_work_1.buildActiveWorkFrame)({
|
|
299
|
+
currentSession,
|
|
300
|
+
currentObligation,
|
|
301
|
+
mustResolveBeforeHandoff: false,
|
|
302
|
+
inner: readActiveWorkInnerState(),
|
|
303
|
+
bridges,
|
|
304
|
+
codingSessions,
|
|
305
|
+
otherCodingSessions,
|
|
306
|
+
pendingObligations,
|
|
307
|
+
taskBoard: (() => {
|
|
308
|
+
try {
|
|
309
|
+
return (0, tasks_1.getTaskModule)().getBoard();
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
return emptyTaskBoard();
|
|
313
|
+
}
|
|
314
|
+
})(),
|
|
315
|
+
friendActivity,
|
|
316
|
+
targetCandidates: [],
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
function renderInnerProgressStatus(status) {
|
|
320
|
+
if (status.processing === "pending") {
|
|
321
|
+
return "i've queued this thought for private attention. it'll come up when my inner dialog is free.";
|
|
322
|
+
}
|
|
323
|
+
if (status.processing === "started") {
|
|
324
|
+
return "i'm working through this privately right now.";
|
|
325
|
+
}
|
|
326
|
+
// processed / completed
|
|
327
|
+
if (status.surfaced && status.surfaced !== "nothing recent" && status.surfaced !== "no outward result") {
|
|
328
|
+
return `i thought about this privately and came to something: ${status.surfaced}`;
|
|
329
|
+
}
|
|
330
|
+
return "i thought about this privately. i'll bring it back when the time is right.";
|
|
331
|
+
}
|
|
49
332
|
exports.baseToolDefinitions = [
|
|
50
333
|
{
|
|
51
334
|
tool: {
|
|
@@ -55,12 +338,29 @@ exports.baseToolDefinitions = [
|
|
|
55
338
|
description: "read file contents",
|
|
56
339
|
parameters: {
|
|
57
340
|
type: "object",
|
|
58
|
-
properties: {
|
|
341
|
+
properties: {
|
|
342
|
+
path: { type: "string" },
|
|
343
|
+
offset: { type: "number", description: "1-based line number to start reading from" },
|
|
344
|
+
limit: { type: "number", description: "maximum number of lines to return" },
|
|
345
|
+
},
|
|
59
346
|
required: ["path"],
|
|
60
347
|
},
|
|
61
348
|
},
|
|
62
349
|
},
|
|
63
|
-
handler: (a) =>
|
|
350
|
+
handler: (a) => {
|
|
351
|
+
const resolvedPath = resolveLocalToolPath(a.path);
|
|
352
|
+
const content = fs.readFileSync(resolvedPath, "utf-8");
|
|
353
|
+
exports.editFileReadTracker.add(resolvedPath);
|
|
354
|
+
const offset = a.offset ? parseInt(a.offset, 10) : undefined;
|
|
355
|
+
const limit = a.limit ? parseInt(a.limit, 10) : undefined;
|
|
356
|
+
if (offset === undefined && limit === undefined)
|
|
357
|
+
return content;
|
|
358
|
+
const lines = content.split("\n");
|
|
359
|
+
const start = offset ? offset - 1 : 0;
|
|
360
|
+
const end = limit !== undefined ? start + limit : lines.length;
|
|
361
|
+
return lines.slice(start, end).join("\n");
|
|
362
|
+
},
|
|
363
|
+
summaryKeys: ["path"],
|
|
64
364
|
},
|
|
65
365
|
{
|
|
66
366
|
tool: {
|
|
@@ -75,103 +375,225 @@ exports.baseToolDefinitions = [
|
|
|
75
375
|
},
|
|
76
376
|
},
|
|
77
377
|
},
|
|
78
|
-
handler: (a) =>
|
|
378
|
+
handler: (a) => {
|
|
379
|
+
const resolvedPath = resolveLocalToolPath(a.path);
|
|
380
|
+
fs.mkdirSync(path.dirname(resolvedPath), { recursive: true });
|
|
381
|
+
fs.writeFileSync(resolvedPath, a.content, "utf-8");
|
|
382
|
+
return "ok";
|
|
383
|
+
},
|
|
384
|
+
summaryKeys: ["path"],
|
|
79
385
|
},
|
|
80
386
|
{
|
|
81
387
|
tool: {
|
|
82
388
|
type: "function",
|
|
83
389
|
function: {
|
|
84
|
-
name: "
|
|
85
|
-
description: "
|
|
390
|
+
name: "edit_file",
|
|
391
|
+
description: "surgically edit a file by replacing an exact string. the file must have been read via read_file first. old_string must match exactly one location in the file.",
|
|
86
392
|
parameters: {
|
|
87
393
|
type: "object",
|
|
88
|
-
properties: {
|
|
89
|
-
|
|
394
|
+
properties: {
|
|
395
|
+
path: { type: "string" },
|
|
396
|
+
old_string: { type: "string" },
|
|
397
|
+
new_string: { type: "string" },
|
|
398
|
+
},
|
|
399
|
+
required: ["path", "old_string", "new_string"],
|
|
90
400
|
},
|
|
91
401
|
},
|
|
92
402
|
},
|
|
93
|
-
handler: (a) =>
|
|
403
|
+
handler: (a) => {
|
|
404
|
+
const resolvedPath = resolveLocalToolPath(a.path);
|
|
405
|
+
if (!exports.editFileReadTracker.has(resolvedPath)) {
|
|
406
|
+
return `error: you must read the file with read_file before editing it. call read_file on ${a.path} first.`;
|
|
407
|
+
}
|
|
408
|
+
let content;
|
|
409
|
+
try {
|
|
410
|
+
content = fs.readFileSync(resolvedPath, "utf-8");
|
|
411
|
+
}
|
|
412
|
+
catch (e) {
|
|
413
|
+
return `error: could not read file: ${e instanceof Error ? e.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(e)}`;
|
|
414
|
+
}
|
|
415
|
+
// Count occurrences
|
|
416
|
+
const occurrences = [];
|
|
417
|
+
let searchFrom = 0;
|
|
418
|
+
while (true) {
|
|
419
|
+
const idx = content.indexOf(a.old_string, searchFrom);
|
|
420
|
+
if (idx === -1)
|
|
421
|
+
break;
|
|
422
|
+
occurrences.push(idx);
|
|
423
|
+
searchFrom = idx + 1;
|
|
424
|
+
}
|
|
425
|
+
if (occurrences.length === 0) {
|
|
426
|
+
return `error: old_string not found in ${a.path}`;
|
|
427
|
+
}
|
|
428
|
+
if (occurrences.length > 1) {
|
|
429
|
+
return `error: old_string is ambiguous -- found ${occurrences.length} matches in ${a.path}. provide more context to make the match unique.`;
|
|
430
|
+
}
|
|
431
|
+
// Single unique match -- replace
|
|
432
|
+
const idx = occurrences[0];
|
|
433
|
+
const updated = content.slice(0, idx) + a.new_string + content.slice(idx + a.old_string.length);
|
|
434
|
+
fs.writeFileSync(resolvedPath, updated, "utf-8");
|
|
435
|
+
// Build contextual diff
|
|
436
|
+
const lines = updated.split("\n");
|
|
437
|
+
const prefixLines = content.slice(0, idx).split("\n");
|
|
438
|
+
const changeStartLine = prefixLines.length - 1;
|
|
439
|
+
const newStringLines = a.new_string.split("\n");
|
|
440
|
+
const changeEndLine = changeStartLine + newStringLines.length;
|
|
441
|
+
return buildContextDiff(lines, changeStartLine, changeEndLine);
|
|
442
|
+
},
|
|
443
|
+
summaryKeys: ["path"],
|
|
94
444
|
},
|
|
95
445
|
{
|
|
96
446
|
tool: {
|
|
97
447
|
type: "function",
|
|
98
448
|
function: {
|
|
99
|
-
name: "
|
|
100
|
-
description: "
|
|
449
|
+
name: "glob",
|
|
450
|
+
description: "find files matching a glob pattern. returns matching paths sorted alphabetically, one per line.",
|
|
101
451
|
parameters: {
|
|
102
452
|
type: "object",
|
|
103
|
-
properties: {
|
|
104
|
-
|
|
453
|
+
properties: {
|
|
454
|
+
pattern: { type: "string", description: "glob pattern (e.g. **/*.ts)" },
|
|
455
|
+
cwd: { type: "string", description: "directory to search from (defaults to process.cwd())" },
|
|
456
|
+
},
|
|
457
|
+
required: ["pattern"],
|
|
105
458
|
},
|
|
106
459
|
},
|
|
107
460
|
},
|
|
108
|
-
handler: (a) =>
|
|
109
|
-
.
|
|
110
|
-
|
|
111
|
-
.join("\n")
|
|
461
|
+
handler: (a) => {
|
|
462
|
+
const cwd = a.cwd ? resolveLocalToolPath(a.cwd) : process.cwd();
|
|
463
|
+
const matches = fg.globSync(a.pattern, { cwd, dot: true });
|
|
464
|
+
return matches.sort().join("\n");
|
|
465
|
+
},
|
|
466
|
+
summaryKeys: ["pattern", "cwd"],
|
|
112
467
|
},
|
|
113
468
|
{
|
|
114
469
|
tool: {
|
|
115
470
|
type: "function",
|
|
116
471
|
function: {
|
|
117
|
-
name: "
|
|
118
|
-
description: "
|
|
472
|
+
name: "grep",
|
|
473
|
+
description: "search file contents for lines matching a regex pattern. searches recursively when given a directory. returns matching lines with file path and line numbers.",
|
|
119
474
|
parameters: {
|
|
120
475
|
type: "object",
|
|
121
476
|
properties: {
|
|
122
|
-
|
|
123
|
-
|
|
477
|
+
pattern: { type: "string", description: "regex pattern to search for" },
|
|
478
|
+
path: { type: "string", description: "file or directory to search" },
|
|
479
|
+
context_lines: { type: "number", description: "number of surrounding context lines (default 0)" },
|
|
480
|
+
include: { type: "string", description: "glob filter to limit searched files (e.g. *.ts)" },
|
|
124
481
|
},
|
|
125
|
-
required: ["
|
|
482
|
+
required: ["pattern", "path"],
|
|
126
483
|
},
|
|
127
484
|
},
|
|
128
485
|
},
|
|
129
486
|
handler: (a) => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
487
|
+
const targetPath = resolveLocalToolPath(a.path);
|
|
488
|
+
const regex = new RegExp(a.pattern);
|
|
489
|
+
const contextLines = parseInt(a.context_lines || "0", 10);
|
|
490
|
+
const includeGlob = a.include || undefined;
|
|
491
|
+
function searchFile(filePath) {
|
|
492
|
+
let content;
|
|
493
|
+
try {
|
|
494
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
133
495
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
496
|
+
catch {
|
|
497
|
+
return [];
|
|
498
|
+
}
|
|
499
|
+
const lines = content.split("\n");
|
|
500
|
+
const matchIndices = new Set();
|
|
501
|
+
for (let i = 0; i < lines.length; i++) {
|
|
502
|
+
if (regex.test(lines[i])) {
|
|
503
|
+
matchIndices.add(i);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (matchIndices.size === 0)
|
|
507
|
+
return [];
|
|
508
|
+
const outputIndices = new Set();
|
|
509
|
+
for (const idx of matchIndices) {
|
|
510
|
+
const start = Math.max(0, idx - contextLines);
|
|
511
|
+
const end = Math.min(lines.length - 1, idx + contextLines);
|
|
512
|
+
for (let i = start; i <= end; i++) {
|
|
513
|
+
outputIndices.add(i);
|
|
137
514
|
}
|
|
138
|
-
(0, child_process_1.execSync)(`git add ${p}`, { encoding: "utf-8" });
|
|
139
515
|
}
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
516
|
+
const sortedIndices = [...outputIndices].sort((a, b) => a - b);
|
|
517
|
+
const results = [];
|
|
518
|
+
for (const idx of sortedIndices) {
|
|
519
|
+
const lineNum = idx + 1;
|
|
520
|
+
if (matchIndices.has(idx)) {
|
|
521
|
+
results.push(`${filePath}:${lineNum}: ${lines[idx]}`);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
results.push(`-${filePath}:${lineNum}: ${lines[idx]}`);
|
|
525
|
+
}
|
|
143
526
|
}
|
|
144
|
-
|
|
145
|
-
return `${diff}\ncommitted`;
|
|
527
|
+
return results;
|
|
146
528
|
}
|
|
147
|
-
|
|
148
|
-
|
|
529
|
+
function collectFiles(dirPath) {
|
|
530
|
+
const files = [];
|
|
531
|
+
function walk(dir) {
|
|
532
|
+
let entries;
|
|
533
|
+
try {
|
|
534
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
535
|
+
}
|
|
536
|
+
catch {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
for (const entry of entries) {
|
|
540
|
+
const fullPath = path.join(dir, entry.name);
|
|
541
|
+
if (entry.isDirectory()) {
|
|
542
|
+
walk(fullPath);
|
|
543
|
+
}
|
|
544
|
+
else if (entry.isFile()) {
|
|
545
|
+
files.push(fullPath);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
walk(dirPath);
|
|
550
|
+
return files.sort();
|
|
551
|
+
}
|
|
552
|
+
function matchesGlob(filePath, glob) {
|
|
553
|
+
const escaped = glob
|
|
554
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
555
|
+
.replace(/\*/g, ".*")
|
|
556
|
+
.replace(/\?/g, ".");
|
|
557
|
+
return new RegExp(`(^|/)${escaped}$`).test(filePath);
|
|
558
|
+
}
|
|
559
|
+
const stat = fs.statSync(targetPath, { throwIfNoEntry: false });
|
|
560
|
+
if (!stat)
|
|
561
|
+
return "";
|
|
562
|
+
if (stat.isFile()) {
|
|
563
|
+
return searchFile(targetPath).join("\n");
|
|
564
|
+
}
|
|
565
|
+
let files = collectFiles(targetPath);
|
|
566
|
+
if (includeGlob) {
|
|
567
|
+
files = files.filter((f) => matchesGlob(f, includeGlob));
|
|
568
|
+
}
|
|
569
|
+
const allResults = [];
|
|
570
|
+
for (const file of files) {
|
|
571
|
+
allResults.push(...searchFile(file));
|
|
149
572
|
}
|
|
573
|
+
return allResults.join("\n");
|
|
150
574
|
},
|
|
575
|
+
summaryKeys: ["pattern", "path", "include"],
|
|
151
576
|
},
|
|
152
577
|
{
|
|
153
578
|
tool: {
|
|
154
579
|
type: "function",
|
|
155
580
|
function: {
|
|
156
|
-
name: "
|
|
157
|
-
description: "
|
|
581
|
+
name: "shell",
|
|
582
|
+
description: "run shell command",
|
|
158
583
|
parameters: {
|
|
159
584
|
type: "object",
|
|
160
|
-
properties: {
|
|
161
|
-
command: { type: "string" },
|
|
162
|
-
},
|
|
585
|
+
properties: { command: { type: "string" } },
|
|
163
586
|
required: ["command"],
|
|
164
587
|
},
|
|
165
588
|
},
|
|
166
589
|
},
|
|
167
590
|
handler: (a) => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return `error: ${e}`;
|
|
173
|
-
}
|
|
591
|
+
return (0, child_process_1.execSync)(a.command, {
|
|
592
|
+
encoding: "utf-8",
|
|
593
|
+
timeout: 30000,
|
|
594
|
+
});
|
|
174
595
|
},
|
|
596
|
+
summaryKeys: ["command"],
|
|
175
597
|
},
|
|
176
598
|
{
|
|
177
599
|
tool: {
|
|
@@ -205,20 +627,7 @@ exports.baseToolDefinitions = [
|
|
|
205
627
|
return `error: ${e}`;
|
|
206
628
|
}
|
|
207
629
|
},
|
|
208
|
-
|
|
209
|
-
{
|
|
210
|
-
tool: {
|
|
211
|
-
type: "function",
|
|
212
|
-
function: {
|
|
213
|
-
name: "get_current_time",
|
|
214
|
-
description: "get the current date and time in America/Los_Angeles (Pacific Time)",
|
|
215
|
-
parameters: { type: "object", properties: {} },
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
handler: () => new Date().toLocaleString("en-US", {
|
|
219
|
-
timeZone: "America/Los_Angeles",
|
|
220
|
-
hour12: false,
|
|
221
|
-
}),
|
|
630
|
+
summaryKeys: ["name"],
|
|
222
631
|
},
|
|
223
632
|
{
|
|
224
633
|
tool: {
|
|
@@ -235,7 +644,7 @@ exports.baseToolDefinitions = [
|
|
|
235
644
|
},
|
|
236
645
|
handler: (a) => {
|
|
237
646
|
try {
|
|
238
|
-
const result = (0, child_process_1.spawnSync)("claude", ["-p", "--dangerously-skip-permissions", "--add-dir", "."], {
|
|
647
|
+
const result = (0, child_process_1.spawnSync)("claude", ["-p", "--no-session-persistence", "--dangerously-skip-permissions", "--add-dir", "."], {
|
|
239
648
|
input: a.prompt,
|
|
240
649
|
encoding: "utf-8",
|
|
241
650
|
timeout: 60000,
|
|
@@ -250,6 +659,7 @@ exports.baseToolDefinitions = [
|
|
|
250
659
|
return `error: ${e}`;
|
|
251
660
|
}
|
|
252
661
|
},
|
|
662
|
+
summaryKeys: ["prompt"],
|
|
253
663
|
},
|
|
254
664
|
{
|
|
255
665
|
tool: {
|
|
@@ -290,13 +700,14 @@ exports.baseToolDefinitions = [
|
|
|
290
700
|
return `error: ${e}`;
|
|
291
701
|
}
|
|
292
702
|
},
|
|
703
|
+
summaryKeys: ["query"],
|
|
293
704
|
},
|
|
294
705
|
{
|
|
295
706
|
tool: {
|
|
296
707
|
type: "function",
|
|
297
708
|
function: {
|
|
298
|
-
name: "
|
|
299
|
-
description: "search
|
|
709
|
+
name: "recall",
|
|
710
|
+
description: "recall what i know — search my diary and journal for facts, thoughts, and working notes that match a query",
|
|
300
711
|
parameters: {
|
|
301
712
|
type: "object",
|
|
302
713
|
properties: { query: { type: "string" } },
|
|
@@ -309,44 +720,69 @@ exports.baseToolDefinitions = [
|
|
|
309
720
|
const query = (a.query || "").trim();
|
|
310
721
|
if (!query)
|
|
311
722
|
return "query is required";
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
.
|
|
723
|
+
const resultLines = [];
|
|
724
|
+
// Search diary entries
|
|
725
|
+
const hits = await (0, diary_1.searchDiaryEntries)(query, (0, diary_1.readDiaryEntries)());
|
|
726
|
+
for (const fact of hits) {
|
|
727
|
+
resultLines.push(`[diary] ${fact.text} (source=${fact.source}, createdAt=${fact.createdAt})`);
|
|
728
|
+
}
|
|
729
|
+
// Search journal index
|
|
730
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
731
|
+
const journalIndexPath = path.join(agentRoot, "journal", ".index.json");
|
|
732
|
+
try {
|
|
733
|
+
const raw = fs.readFileSync(journalIndexPath, "utf8");
|
|
734
|
+
const journalEntries = JSON.parse(raw);
|
|
735
|
+
if (Array.isArray(journalEntries) && journalEntries.length > 0) {
|
|
736
|
+
// Substring match on preview and filename
|
|
737
|
+
const lowerQuery = query.toLowerCase();
|
|
738
|
+
for (const entry of journalEntries) {
|
|
739
|
+
/* v8 ignore next 4 -- both sides tested (filename-only match in recall-journal.test.ts); v8 misreports || short-circuit @preserve */
|
|
740
|
+
if (entry.preview.toLowerCase().includes(lowerQuery) ||
|
|
741
|
+
entry.filename.toLowerCase().includes(lowerQuery)) {
|
|
742
|
+
resultLines.push(`[journal] ${entry.filename}: ${entry.preview}`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
catch {
|
|
748
|
+
// No journal index or malformed — skip journal search
|
|
749
|
+
}
|
|
750
|
+
return resultLines.join("\n");
|
|
317
751
|
}
|
|
318
752
|
catch (e) {
|
|
319
753
|
return `error: ${e instanceof Error ? e.message : String(e)}`;
|
|
320
754
|
}
|
|
321
755
|
},
|
|
756
|
+
summaryKeys: ["query"],
|
|
322
757
|
},
|
|
323
758
|
{
|
|
324
759
|
tool: {
|
|
325
760
|
type: "function",
|
|
326
761
|
function: {
|
|
327
|
-
name: "
|
|
328
|
-
description: "
|
|
762
|
+
name: "diary_write",
|
|
763
|
+
description: "write an entry in my diary — something i learned, noticed, or concluded that i want to recall later. optional 'about' tags the entry to a person, topic, or context.",
|
|
329
764
|
parameters: {
|
|
330
765
|
type: "object",
|
|
331
766
|
properties: {
|
|
332
|
-
|
|
767
|
+
entry: { type: "string" },
|
|
333
768
|
about: { type: "string" },
|
|
334
769
|
},
|
|
335
|
-
required: ["
|
|
770
|
+
required: ["entry"],
|
|
336
771
|
},
|
|
337
772
|
},
|
|
338
773
|
},
|
|
339
774
|
handler: async (a) => {
|
|
340
|
-
const
|
|
341
|
-
if (!
|
|
342
|
-
return "
|
|
343
|
-
const result = await (0,
|
|
344
|
-
text,
|
|
345
|
-
source: "tool:
|
|
775
|
+
const entry = (a.entry || "").trim();
|
|
776
|
+
if (!entry)
|
|
777
|
+
return "entry is required";
|
|
778
|
+
const result = await (0, diary_1.saveDiaryEntry)({
|
|
779
|
+
text: entry,
|
|
780
|
+
source: "tool:diary_write",
|
|
346
781
|
about: typeof a.about === "string" ? a.about : undefined,
|
|
347
782
|
});
|
|
348
|
-
return `saved
|
|
783
|
+
return `saved diary entry (added=${result.added}, skipped=${result.skipped})`;
|
|
349
784
|
},
|
|
785
|
+
summaryKeys: ["entry", "about"],
|
|
350
786
|
},
|
|
351
787
|
{
|
|
352
788
|
tool: {
|
|
@@ -374,151 +810,7 @@ exports.baseToolDefinitions = [
|
|
|
374
810
|
return `friend not found: ${friendId}`;
|
|
375
811
|
return JSON.stringify(friend, null, 2);
|
|
376
812
|
},
|
|
377
|
-
|
|
378
|
-
{
|
|
379
|
-
tool: {
|
|
380
|
-
type: "function",
|
|
381
|
-
function: {
|
|
382
|
-
name: "task_board",
|
|
383
|
-
description: "show the task board grouped by status",
|
|
384
|
-
parameters: { type: "object", properties: {} },
|
|
385
|
-
},
|
|
386
|
-
},
|
|
387
|
-
handler: () => {
|
|
388
|
-
const board = (0, tasks_1.getTaskModule)().getBoard();
|
|
389
|
-
return board.full || board.compact || "no tasks found";
|
|
390
|
-
},
|
|
391
|
-
},
|
|
392
|
-
{
|
|
393
|
-
tool: {
|
|
394
|
-
type: "function",
|
|
395
|
-
function: {
|
|
396
|
-
name: "task_create",
|
|
397
|
-
description: "create a new task in the bundle task system",
|
|
398
|
-
parameters: {
|
|
399
|
-
type: "object",
|
|
400
|
-
properties: {
|
|
401
|
-
title: { type: "string" },
|
|
402
|
-
type: { type: "string", enum: ["one-shot", "ongoing", "habit"] },
|
|
403
|
-
category: { type: "string" },
|
|
404
|
-
body: { type: "string" },
|
|
405
|
-
},
|
|
406
|
-
required: ["title", "type", "category", "body"],
|
|
407
|
-
},
|
|
408
|
-
},
|
|
409
|
-
},
|
|
410
|
-
handler: (a) => {
|
|
411
|
-
try {
|
|
412
|
-
const created = (0, tasks_1.getTaskModule)().createTask({
|
|
413
|
-
title: a.title,
|
|
414
|
-
type: a.type,
|
|
415
|
-
category: a.category,
|
|
416
|
-
body: a.body,
|
|
417
|
-
});
|
|
418
|
-
return `created: ${created}`;
|
|
419
|
-
}
|
|
420
|
-
catch (error) {
|
|
421
|
-
return `error: ${error instanceof Error ? error.message : String(error)}`;
|
|
422
|
-
}
|
|
423
|
-
},
|
|
424
|
-
},
|
|
425
|
-
{
|
|
426
|
-
tool: {
|
|
427
|
-
type: "function",
|
|
428
|
-
function: {
|
|
429
|
-
name: "task_update_status",
|
|
430
|
-
description: "update a task status using validated transitions",
|
|
431
|
-
parameters: {
|
|
432
|
-
type: "object",
|
|
433
|
-
properties: {
|
|
434
|
-
name: { type: "string" },
|
|
435
|
-
status: { type: "string" },
|
|
436
|
-
},
|
|
437
|
-
required: ["name", "status"],
|
|
438
|
-
},
|
|
439
|
-
},
|
|
440
|
-
},
|
|
441
|
-
handler: (a) => {
|
|
442
|
-
const result = (0, tasks_1.getTaskModule)().updateStatus(a.name, a.status);
|
|
443
|
-
if (!result.ok) {
|
|
444
|
-
return `error: ${result.reason ?? "status update failed"}`;
|
|
445
|
-
}
|
|
446
|
-
const archivedSuffix = result.archived && result.archived.length > 0
|
|
447
|
-
? ` | archived: ${result.archived.join(", ")}`
|
|
448
|
-
: "";
|
|
449
|
-
return `updated: ${a.name} -> ${result.to}${archivedSuffix}`;
|
|
450
|
-
},
|
|
451
|
-
},
|
|
452
|
-
{
|
|
453
|
-
tool: {
|
|
454
|
-
type: "function",
|
|
455
|
-
function: {
|
|
456
|
-
name: "task_board_status",
|
|
457
|
-
description: "show board detail for a specific status",
|
|
458
|
-
parameters: {
|
|
459
|
-
type: "object",
|
|
460
|
-
properties: {
|
|
461
|
-
status: { type: "string" },
|
|
462
|
-
},
|
|
463
|
-
required: ["status"],
|
|
464
|
-
},
|
|
465
|
-
},
|
|
466
|
-
},
|
|
467
|
-
handler: (a) => {
|
|
468
|
-
const lines = (0, tasks_1.getTaskModule)().boardStatus(a.status);
|
|
469
|
-
return lines.length > 0 ? lines.join("\n") : "no tasks in that status";
|
|
470
|
-
},
|
|
471
|
-
},
|
|
472
|
-
{
|
|
473
|
-
tool: {
|
|
474
|
-
type: "function",
|
|
475
|
-
function: {
|
|
476
|
-
name: "task_board_action",
|
|
477
|
-
description: "show tasks or validation issues that require action",
|
|
478
|
-
parameters: {
|
|
479
|
-
type: "object",
|
|
480
|
-
properties: {
|
|
481
|
-
scope: { type: "string" },
|
|
482
|
-
},
|
|
483
|
-
},
|
|
484
|
-
},
|
|
485
|
-
},
|
|
486
|
-
handler: (a) => {
|
|
487
|
-
const lines = (0, tasks_1.getTaskModule)().boardAction();
|
|
488
|
-
if (!a.scope) {
|
|
489
|
-
return lines.length > 0 ? lines.join("\n") : "no action required";
|
|
490
|
-
}
|
|
491
|
-
const filtered = lines.filter((line) => line.includes(a.scope));
|
|
492
|
-
return filtered.length > 0 ? filtered.join("\n") : "no matching action items";
|
|
493
|
-
},
|
|
494
|
-
},
|
|
495
|
-
{
|
|
496
|
-
tool: {
|
|
497
|
-
type: "function",
|
|
498
|
-
function: {
|
|
499
|
-
name: "task_board_deps",
|
|
500
|
-
description: "show unresolved task dependencies",
|
|
501
|
-
parameters: { type: "object", properties: {} },
|
|
502
|
-
},
|
|
503
|
-
},
|
|
504
|
-
handler: () => {
|
|
505
|
-
const lines = (0, tasks_1.getTaskModule)().boardDeps();
|
|
506
|
-
return lines.length > 0 ? lines.join("\n") : "no unresolved dependencies";
|
|
507
|
-
},
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
tool: {
|
|
511
|
-
type: "function",
|
|
512
|
-
function: {
|
|
513
|
-
name: "task_board_sessions",
|
|
514
|
-
description: "show tasks with active coding or sub-agent sessions",
|
|
515
|
-
parameters: { type: "object", properties: {} },
|
|
516
|
-
},
|
|
517
|
-
},
|
|
518
|
-
handler: () => {
|
|
519
|
-
const lines = (0, tasks_1.getTaskModule)().boardSessions();
|
|
520
|
-
return lines.length > 0 ? lines.join("\n") : "no active sessions";
|
|
521
|
-
},
|
|
813
|
+
summaryKeys: ["friendId"],
|
|
522
814
|
},
|
|
523
815
|
{
|
|
524
816
|
tool: {
|
|
@@ -602,55 +894,214 @@ exports.baseToolDefinitions = [
|
|
|
602
894
|
return `error saving note: ${err instanceof Error ? err.message : String(err)}`;
|
|
603
895
|
}
|
|
604
896
|
},
|
|
897
|
+
summaryKeys: ["type", "key", "content"],
|
|
605
898
|
},
|
|
606
899
|
// -- cross-session awareness --
|
|
900
|
+
{
|
|
901
|
+
tool: {
|
|
902
|
+
type: "function",
|
|
903
|
+
function: {
|
|
904
|
+
name: "bridge_manage",
|
|
905
|
+
description: "create and manage shared live-work bridges across already-active sessions.",
|
|
906
|
+
parameters: {
|
|
907
|
+
type: "object",
|
|
908
|
+
properties: {
|
|
909
|
+
action: {
|
|
910
|
+
type: "string",
|
|
911
|
+
enum: ["begin", "attach", "status", "promote_task", "complete", "cancel"],
|
|
912
|
+
},
|
|
913
|
+
bridgeId: { type: "string", description: "bridge id for all actions except begin" },
|
|
914
|
+
objective: { type: "string", description: "objective for begin" },
|
|
915
|
+
summary: { type: "string", description: "optional concise shared-work summary" },
|
|
916
|
+
friendId: { type: "string", description: "target friend id for attach" },
|
|
917
|
+
channel: { type: "string", description: "target channel for attach" },
|
|
918
|
+
key: { type: "string", description: "target session key for attach (defaults to 'session')" },
|
|
919
|
+
title: { type: "string", description: "task title override for promote_task" },
|
|
920
|
+
category: { type: "string", description: "task category override for promote_task" },
|
|
921
|
+
body: { type: "string", description: "task body override for promote_task" },
|
|
922
|
+
},
|
|
923
|
+
required: ["action"],
|
|
924
|
+
},
|
|
925
|
+
},
|
|
926
|
+
},
|
|
927
|
+
handler: async (args, ctx) => {
|
|
928
|
+
const manager = (0, manager_1.createBridgeManager)();
|
|
929
|
+
const action = (args.action || "").trim();
|
|
930
|
+
if (action === "begin") {
|
|
931
|
+
if (!ctx?.currentSession) {
|
|
932
|
+
return "bridge_manage begin requires an active session context.";
|
|
933
|
+
}
|
|
934
|
+
const objective = (args.objective || "").trim();
|
|
935
|
+
if (!objective)
|
|
936
|
+
return "objective is required for bridge begin.";
|
|
937
|
+
return (0, manager_1.formatBridgeStatus)(manager.beginBridge({
|
|
938
|
+
objective,
|
|
939
|
+
summary: (args.summary || objective).trim(),
|
|
940
|
+
session: ctx.currentSession,
|
|
941
|
+
}));
|
|
942
|
+
}
|
|
943
|
+
const bridgeId = (args.bridgeId || "").trim();
|
|
944
|
+
if (!bridgeId) {
|
|
945
|
+
return "bridgeId is required for this bridge action.";
|
|
946
|
+
}
|
|
947
|
+
if (action === "attach") {
|
|
948
|
+
const friendId = (args.friendId || "").trim();
|
|
949
|
+
const channel = (args.channel || "").trim();
|
|
950
|
+
const key = (args.key || "session").trim();
|
|
951
|
+
if (!friendId || !channel) {
|
|
952
|
+
return "friendId and channel are required for bridge attach.";
|
|
953
|
+
}
|
|
954
|
+
const sessionPath = (0, config_1.resolveSessionPath)(friendId, channel, key);
|
|
955
|
+
const recall = await recallSessionSafely({
|
|
956
|
+
sessionPath,
|
|
957
|
+
friendId,
|
|
958
|
+
channel,
|
|
959
|
+
key,
|
|
960
|
+
messageCount: 20,
|
|
961
|
+
trustLevel: ctx?.context?.friend?.trustLevel,
|
|
962
|
+
summarize: ctx?.summarize,
|
|
963
|
+
});
|
|
964
|
+
if (recall.kind === "missing") {
|
|
965
|
+
return NO_SESSION_FOUND_MESSAGE;
|
|
966
|
+
}
|
|
967
|
+
return (0, manager_1.formatBridgeStatus)(manager.attachSession(bridgeId, {
|
|
968
|
+
friendId,
|
|
969
|
+
channel,
|
|
970
|
+
key,
|
|
971
|
+
sessionPath,
|
|
972
|
+
snapshot: recall.kind === "ok" ? recall.snapshot : EMPTY_SESSION_MESSAGE,
|
|
973
|
+
}));
|
|
974
|
+
}
|
|
975
|
+
if (action === "status") {
|
|
976
|
+
const bridge = manager.getBridge(bridgeId);
|
|
977
|
+
if (!bridge)
|
|
978
|
+
return `bridge not found: ${bridgeId}`;
|
|
979
|
+
return (0, manager_1.formatBridgeStatus)(bridge);
|
|
980
|
+
}
|
|
981
|
+
if (action === "promote_task") {
|
|
982
|
+
return (0, manager_1.formatBridgeStatus)(manager.promoteBridgeToTask(bridgeId, {
|
|
983
|
+
title: args.title,
|
|
984
|
+
category: args.category,
|
|
985
|
+
body: args.body,
|
|
986
|
+
}));
|
|
987
|
+
}
|
|
988
|
+
if (action === "complete") {
|
|
989
|
+
return (0, manager_1.formatBridgeStatus)(manager.completeBridge(bridgeId));
|
|
990
|
+
}
|
|
991
|
+
if (action === "cancel") {
|
|
992
|
+
return (0, manager_1.formatBridgeStatus)(manager.cancelBridge(bridgeId));
|
|
993
|
+
}
|
|
994
|
+
return `unknown bridge action: ${action}`;
|
|
995
|
+
},
|
|
996
|
+
summaryKeys: ["action", "bridgeId", "objective", "friendId", "channel", "key"],
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
tool: {
|
|
1000
|
+
type: "function",
|
|
1001
|
+
function: {
|
|
1002
|
+
name: "query_active_work",
|
|
1003
|
+
description: "read the current live world-state across visible sessions, coding lanes, inner work, and return obligations. use this instead of piecing status together from separate session and coding tools.",
|
|
1004
|
+
parameters: {
|
|
1005
|
+
type: "object",
|
|
1006
|
+
properties: {},
|
|
1007
|
+
},
|
|
1008
|
+
},
|
|
1009
|
+
},
|
|
1010
|
+
handler: async (_args, ctx) => {
|
|
1011
|
+
const frame = await buildToolActiveWorkFrame(ctx);
|
|
1012
|
+
return `this is my current top-level live world-state.\nanswer whole-self status questions from this before drilling into individual sessions.\n\n${(0, active_work_1.formatActiveWorkFrame)(frame)}`;
|
|
1013
|
+
},
|
|
1014
|
+
},
|
|
607
1015
|
{
|
|
608
1016
|
tool: {
|
|
609
1017
|
type: "function",
|
|
610
1018
|
function: {
|
|
611
1019
|
name: "query_session",
|
|
612
|
-
description: "
|
|
1020
|
+
description: "inspect another session. use transcript for recent context, status for self/inner progress, or search to find older history by query.",
|
|
613
1021
|
parameters: {
|
|
614
1022
|
type: "object",
|
|
615
1023
|
properties: {
|
|
616
1024
|
friendId: { type: "string", description: "the friend UUID (or 'self')" },
|
|
617
|
-
channel: { type: "string", description: "the channel: cli, teams, or
|
|
1025
|
+
channel: { type: "string", description: "the channel: cli, teams, bluebubbles, inner, or mcp" },
|
|
618
1026
|
key: { type: "string", description: "session key (defaults to 'session')" },
|
|
619
1027
|
messageCount: { type: "string", description: "how many recent messages to return (default 20)" },
|
|
1028
|
+
mode: {
|
|
1029
|
+
type: "string",
|
|
1030
|
+
enum: ["transcript", "status", "search"],
|
|
1031
|
+
description: "transcript (default), lightweight status for self/inner checks, or search for older history",
|
|
1032
|
+
},
|
|
1033
|
+
query: { type: "string", description: "required when mode=search; search term for older session history" },
|
|
620
1034
|
},
|
|
621
1035
|
required: ["friendId", "channel"],
|
|
622
1036
|
},
|
|
623
1037
|
},
|
|
624
1038
|
},
|
|
625
1039
|
handler: async (args, ctx) => {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
const
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
if (tail.length === 0)
|
|
638
|
-
return "session exists but has no non-system messages.";
|
|
639
|
-
const transcript = tail.map((m) => `[${m.role}] ${m.content}`).join("\n");
|
|
640
|
-
// LLM summarization when summarize function is available
|
|
641
|
-
if (ctx?.summarize) {
|
|
642
|
-
const trustLevel = ctx.context?.friend?.trustLevel ?? "family";
|
|
643
|
-
const isSelfQuery = friendId === "self";
|
|
644
|
-
const instruction = isSelfQuery
|
|
645
|
-
? "summarize this session transcript fully and transparently. this is my own inner dialog — include all details, decisions, and reasoning."
|
|
646
|
-
: `summarize this session transcript. the person asking has trust level: ${trustLevel}. family=full transparency, friend=share work and general topics but protect other people's identities, acquaintance=very guarded minimal disclosure.`;
|
|
647
|
-
return await ctx.summarize(transcript, instruction);
|
|
1040
|
+
let friendId = args.friendId;
|
|
1041
|
+
const channel = args.channel;
|
|
1042
|
+
const key = args.key || "session";
|
|
1043
|
+
const count = parseInt(args.messageCount || "20", 10);
|
|
1044
|
+
const mode = args.mode || "transcript";
|
|
1045
|
+
// Resolve friend name → UUID if not already a UUID or "self"
|
|
1046
|
+
if (friendId && friendId !== "self" && !/^[0-9a-f]{8}-[0-9a-f]{4}-/.test(friendId) && ctx?.friendStore?.listAll) {
|
|
1047
|
+
const allFriends = await ctx.friendStore.listAll();
|
|
1048
|
+
const match = allFriends.find(f => f.name.toLowerCase() === friendId.toLowerCase());
|
|
1049
|
+
if (match) {
|
|
1050
|
+
friendId = match.id;
|
|
648
1051
|
}
|
|
649
|
-
return transcript;
|
|
650
1052
|
}
|
|
651
|
-
|
|
652
|
-
|
|
1053
|
+
if (mode === "status") {
|
|
1054
|
+
if (friendId !== "self" || channel !== "inner") {
|
|
1055
|
+
return "status mode is only available for self/inner dialog.";
|
|
1056
|
+
}
|
|
1057
|
+
const sessionPath = (0, thoughts_1.getInnerDialogSessionPath)((0, identity_1.getAgentRoot)());
|
|
1058
|
+
const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)());
|
|
1059
|
+
return renderInnerProgressStatus((0, thoughts_1.readInnerDialogStatus)(sessionPath, pendingDir));
|
|
1060
|
+
}
|
|
1061
|
+
if (mode === "search") {
|
|
1062
|
+
const query = (args.query || "").trim();
|
|
1063
|
+
if (!query) {
|
|
1064
|
+
return "search mode requires a non-empty query.";
|
|
1065
|
+
}
|
|
1066
|
+
const search = await searchSessionSafely({
|
|
1067
|
+
sessionPath: (0, config_1.resolveSessionPath)(friendId, channel, key),
|
|
1068
|
+
friendId,
|
|
1069
|
+
channel,
|
|
1070
|
+
key,
|
|
1071
|
+
query,
|
|
1072
|
+
});
|
|
1073
|
+
if (search.kind === "missing") {
|
|
1074
|
+
return NO_SESSION_FOUND_MESSAGE;
|
|
1075
|
+
}
|
|
1076
|
+
if (search.kind === "empty") {
|
|
1077
|
+
return EMPTY_SESSION_MESSAGE;
|
|
1078
|
+
}
|
|
1079
|
+
if (search.kind === "no_match") {
|
|
1080
|
+
return `no matches for "${search.query}" in that session.\n\n${search.snapshot}`;
|
|
1081
|
+
}
|
|
1082
|
+
return [
|
|
1083
|
+
`history search: "${search.query}"`,
|
|
1084
|
+
search.snapshot,
|
|
1085
|
+
...search.matches.map((match, index) => `match ${index + 1}\n${match}`),
|
|
1086
|
+
].join("\n\n");
|
|
1087
|
+
}
|
|
1088
|
+
const sessFile = (0, config_1.resolveSessionPath)(friendId, channel, key);
|
|
1089
|
+
const recall = await recallSessionSafely({
|
|
1090
|
+
sessionPath: sessFile,
|
|
1091
|
+
friendId,
|
|
1092
|
+
channel,
|
|
1093
|
+
key,
|
|
1094
|
+
messageCount: count,
|
|
1095
|
+
trustLevel: ctx?.context?.friend?.trustLevel,
|
|
1096
|
+
summarize: ctx?.summarize,
|
|
1097
|
+
});
|
|
1098
|
+
if (recall.kind === "missing") {
|
|
1099
|
+
return NO_SESSION_FOUND_MESSAGE;
|
|
653
1100
|
}
|
|
1101
|
+
if (recall.kind === "empty") {
|
|
1102
|
+
return EMPTY_SESSION_MESSAGE;
|
|
1103
|
+
}
|
|
1104
|
+
return recall.summary;
|
|
654
1105
|
},
|
|
655
1106
|
},
|
|
656
1107
|
{
|
|
@@ -658,12 +1109,12 @@ exports.baseToolDefinitions = [
|
|
|
658
1109
|
type: "function",
|
|
659
1110
|
function: {
|
|
660
1111
|
name: "send_message",
|
|
661
|
-
description: "send a message to a friend's session. the
|
|
1112
|
+
description: "send a message to a friend's session. when the request is explicitly authorized from a trusted live chat, the harness will try to deliver immediately; otherwise it reports truthful queued/block/failure state.",
|
|
662
1113
|
parameters: {
|
|
663
1114
|
type: "object",
|
|
664
1115
|
properties: {
|
|
665
1116
|
friendId: { type: "string", description: "the friend UUID (or 'self')" },
|
|
666
|
-
channel: { type: "string", description: "the channel: cli, teams, or
|
|
1117
|
+
channel: { type: "string", description: "the channel: cli, teams, bluebubbles, inner, or mcp" },
|
|
667
1118
|
key: { type: "string", description: "session key (defaults to 'session')" },
|
|
668
1119
|
content: { type: "string", description: "the message content to send" },
|
|
669
1120
|
},
|
|
@@ -671,44 +1122,310 @@ exports.baseToolDefinitions = [
|
|
|
671
1122
|
},
|
|
672
1123
|
},
|
|
673
1124
|
},
|
|
674
|
-
handler: async (args) => {
|
|
1125
|
+
handler: async (args, ctx) => {
|
|
675
1126
|
const friendId = args.friendId;
|
|
676
1127
|
const channel = args.channel;
|
|
677
1128
|
const key = args.key || "session";
|
|
678
1129
|
const content = args.content;
|
|
679
1130
|
const now = Date.now();
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
const
|
|
1131
|
+
const agentName = (0, identity_1.getAgentName)();
|
|
1132
|
+
// Self-routing: messages to "self" always go to inner dialog pending dir,
|
|
1133
|
+
// regardless of the channel or key the agent specified.
|
|
1134
|
+
const isSelf = friendId === "self";
|
|
1135
|
+
const pendingDir = isSelf
|
|
1136
|
+
? (0, pending_1.getInnerDialogPendingDir)(agentName)
|
|
1137
|
+
: (0, pending_1.getPendingDir)(agentName, friendId, channel, key);
|
|
1138
|
+
const delegatingBridgeId = findDelegatingBridgeId(ctx);
|
|
1139
|
+
const delegatedFrom = isSelf
|
|
1140
|
+
&& ctx?.currentSession
|
|
1141
|
+
&& !(ctx.currentSession.friendId === "self" && ctx.currentSession.channel === "inner")
|
|
1142
|
+
? {
|
|
1143
|
+
friendId: ctx.currentSession.friendId,
|
|
1144
|
+
channel: ctx.currentSession.channel,
|
|
1145
|
+
key: ctx.currentSession.key,
|
|
1146
|
+
...(delegatingBridgeId ? { bridgeId: delegatingBridgeId } : {}),
|
|
1147
|
+
}
|
|
1148
|
+
: undefined;
|
|
1149
|
+
const obligationId = delegatedFrom ? (0, obligations_1.generateObligationId)(now) : undefined;
|
|
684
1150
|
const envelope = {
|
|
685
|
-
from:
|
|
1151
|
+
from: agentName,
|
|
686
1152
|
friendId,
|
|
687
1153
|
channel,
|
|
688
1154
|
key,
|
|
689
1155
|
content,
|
|
690
1156
|
timestamp: now,
|
|
1157
|
+
...(delegatedFrom ? { delegatedFrom, obligationStatus: "pending" } : {}),
|
|
1158
|
+
...(obligationId ? { obligationId } : {}),
|
|
691
1159
|
};
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1160
|
+
if (isSelf) {
|
|
1161
|
+
writePendingEnvelope(pendingDir, envelope);
|
|
1162
|
+
if (delegatedFrom) {
|
|
1163
|
+
try {
|
|
1164
|
+
(0, obligations_2.createObligation)((0, identity_1.getAgentRoot)(), {
|
|
1165
|
+
origin: {
|
|
1166
|
+
friendId: delegatedFrom.friendId,
|
|
1167
|
+
channel: delegatedFrom.channel,
|
|
1168
|
+
key: delegatedFrom.key,
|
|
1169
|
+
},
|
|
1170
|
+
...(delegatedFrom.bridgeId ? { bridgeId: delegatedFrom.bridgeId } : {}),
|
|
1171
|
+
content,
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
catch {
|
|
1175
|
+
/* v8 ignore next -- defensive: obligation store write failure should not break send_message @preserve */
|
|
1176
|
+
}
|
|
1177
|
+
/* v8 ignore next -- obligationId always set when delegatedFrom is set (see generateObligationId above) @preserve */
|
|
1178
|
+
if (obligationId) {
|
|
1179
|
+
(0, obligations_1.createObligation)(agentName, {
|
|
1180
|
+
id: obligationId,
|
|
1181
|
+
origin: delegatedFrom,
|
|
1182
|
+
status: "queued",
|
|
1183
|
+
delegatedContent: content.length > 120 ? `${content.slice(0, 117)}...` : content,
|
|
1184
|
+
createdAt: now,
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1188
|
+
event: "repertoire.obligation_created",
|
|
1189
|
+
component: "repertoire",
|
|
1190
|
+
message: "obligation created for inner dialog delegation",
|
|
1191
|
+
meta: {
|
|
1192
|
+
friendId: delegatedFrom.friendId,
|
|
1193
|
+
channel: delegatedFrom.channel,
|
|
1194
|
+
key: delegatedFrom.key,
|
|
1195
|
+
},
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
let wakeResponse = null;
|
|
1199
|
+
try {
|
|
1200
|
+
wakeResponse = await (0, socket_client_1.requestInnerWake)(agentName);
|
|
1201
|
+
}
|
|
1202
|
+
catch {
|
|
1203
|
+
wakeResponse = null;
|
|
1204
|
+
}
|
|
1205
|
+
if (!wakeResponse?.ok) {
|
|
1206
|
+
const { runInnerDialogTurn } = await Promise.resolve().then(() => __importStar(require("../senses/inner-dialog")));
|
|
1207
|
+
if (ctx?.context?.channel.channel === "inner") {
|
|
1208
|
+
queueMicrotask(() => {
|
|
1209
|
+
void runInnerDialogTurn({ reason: "instinct" });
|
|
1210
|
+
});
|
|
1211
|
+
return renderInnerProgressStatus({
|
|
1212
|
+
queue: "queued to inner/dialog",
|
|
1213
|
+
wake: "inline scheduled",
|
|
1214
|
+
processing: "pending",
|
|
1215
|
+
surfaced: "nothing yet",
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
else {
|
|
1219
|
+
const turnResult = await runInnerDialogTurn({ reason: "instinct" });
|
|
1220
|
+
const surfacedPreview = normalizeProgressOutcome((0, thoughts_1.formatSurfacedValue)((0, thoughts_1.extractThoughtResponseFromMessages)(turnResult?.messages ?? [])));
|
|
1221
|
+
return (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
1222
|
+
scope: "inner-delegation",
|
|
1223
|
+
phase: "completed",
|
|
1224
|
+
objective: "queued to inner/dialog",
|
|
1225
|
+
outcomeText: `wake: inline fallback\n${surfacedPreview}`,
|
|
1226
|
+
}));
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return renderInnerProgressStatus({
|
|
1230
|
+
queue: "queued to inner/dialog",
|
|
1231
|
+
wake: "daemon requested",
|
|
1232
|
+
processing: "pending",
|
|
1233
|
+
surfaced: "nothing yet",
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
const deliveryResult = await (0, cross_chat_delivery_1.deliverCrossChatMessage)({
|
|
1237
|
+
friendId,
|
|
1238
|
+
channel,
|
|
1239
|
+
key,
|
|
1240
|
+
content,
|
|
1241
|
+
intent: ctx?.currentSession && ctx.currentSession.friendId !== "self"
|
|
1242
|
+
? "explicit_cross_chat"
|
|
1243
|
+
: "generic_outreach",
|
|
1244
|
+
...(ctx?.currentSession && ctx.currentSession.friendId !== "self"
|
|
1245
|
+
? {
|
|
1246
|
+
authorizingSession: {
|
|
1247
|
+
friendId: ctx.currentSession.friendId,
|
|
1248
|
+
channel: ctx.currentSession.channel,
|
|
1249
|
+
key: ctx.currentSession.key,
|
|
1250
|
+
trustLevel: ctx?.context?.friend?.trustLevel,
|
|
1251
|
+
},
|
|
1252
|
+
}
|
|
1253
|
+
: {}),
|
|
1254
|
+
}, {
|
|
1255
|
+
agentName,
|
|
1256
|
+
queuePending: (message) => writePendingEnvelope(pendingDir, message),
|
|
1257
|
+
deliverers: {
|
|
1258
|
+
bluebubbles: async (request) => {
|
|
1259
|
+
const { sendProactiveBlueBubblesMessageToSession } = await Promise.resolve().then(() => __importStar(require("../senses/bluebubbles")));
|
|
1260
|
+
const result = await sendProactiveBlueBubblesMessageToSession({
|
|
1261
|
+
friendId: request.friendId,
|
|
1262
|
+
sessionKey: request.key,
|
|
1263
|
+
text: request.content,
|
|
1264
|
+
intent: request.intent,
|
|
1265
|
+
authorizingSession: request.authorizingSession,
|
|
1266
|
+
});
|
|
1267
|
+
if (result.delivered) {
|
|
1268
|
+
return {
|
|
1269
|
+
status: "delivered_now",
|
|
1270
|
+
detail: "sent to the active bluebubbles chat now",
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
if (result.reason === "missing_target") {
|
|
1274
|
+
return {
|
|
1275
|
+
status: "blocked",
|
|
1276
|
+
detail: "bluebubbles could not resolve a routable target for that session",
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
if (result.reason === "send_error") {
|
|
1280
|
+
return {
|
|
1281
|
+
status: "failed",
|
|
1282
|
+
detail: "bluebubbles send failed",
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
return {
|
|
1286
|
+
status: "unavailable",
|
|
1287
|
+
detail: "live delivery unavailable right now; queued for the next active turn",
|
|
1288
|
+
};
|
|
1289
|
+
},
|
|
1290
|
+
teams: async (request) => {
|
|
1291
|
+
if (!ctx?.botApi) {
|
|
1292
|
+
return {
|
|
1293
|
+
status: "unavailable",
|
|
1294
|
+
detail: "live delivery unavailable right now; queued for the next active turn",
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
const { sendProactiveTeamsMessageToSession } = await Promise.resolve().then(() => __importStar(require("../senses/teams")));
|
|
1298
|
+
const result = await sendProactiveTeamsMessageToSession({
|
|
1299
|
+
friendId: request.friendId,
|
|
1300
|
+
sessionKey: request.key,
|
|
1301
|
+
text: request.content,
|
|
1302
|
+
intent: request.intent,
|
|
1303
|
+
authorizingSession: request.authorizingSession,
|
|
1304
|
+
}, {
|
|
1305
|
+
botApi: ctx.botApi,
|
|
1306
|
+
});
|
|
1307
|
+
if (result.delivered) {
|
|
1308
|
+
return {
|
|
1309
|
+
status: "delivered_now",
|
|
1310
|
+
detail: "sent to the active teams chat now",
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
if (result.reason === "missing_target") {
|
|
1314
|
+
return {
|
|
1315
|
+
status: "blocked",
|
|
1316
|
+
detail: "teams could not resolve a routable target for that session",
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
if (result.reason === "send_error") {
|
|
1320
|
+
return {
|
|
1321
|
+
status: "failed",
|
|
1322
|
+
detail: "teams send failed",
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
return {
|
|
1326
|
+
status: "unavailable",
|
|
1327
|
+
detail: "live delivery unavailable right now; queued for the next active turn",
|
|
1328
|
+
};
|
|
1329
|
+
},
|
|
1330
|
+
},
|
|
1331
|
+
});
|
|
1332
|
+
return renderCrossChatDeliveryStatus(`${friendId} on ${channel}/${key}`, deliveryResult);
|
|
695
1333
|
},
|
|
696
1334
|
},
|
|
1335
|
+
{
|
|
1336
|
+
tool: {
|
|
1337
|
+
type: "function",
|
|
1338
|
+
function: {
|
|
1339
|
+
name: "set_reasoning_effort",
|
|
1340
|
+
description: "adjust your own reasoning depth for subsequent turns. use higher effort for complex analysis, lower for simple tasks.",
|
|
1341
|
+
parameters: {
|
|
1342
|
+
type: "object",
|
|
1343
|
+
properties: {
|
|
1344
|
+
level: { type: "string", description: "the reasoning effort level to set" },
|
|
1345
|
+
},
|
|
1346
|
+
required: ["level"],
|
|
1347
|
+
},
|
|
1348
|
+
},
|
|
1349
|
+
},
|
|
1350
|
+
handler: (args, ctx) => {
|
|
1351
|
+
if (!ctx?.supportedReasoningEfforts || !ctx.setReasoningEffort) {
|
|
1352
|
+
return "reasoning effort adjustment is not available in this context.";
|
|
1353
|
+
}
|
|
1354
|
+
const level = (args.level || "").trim();
|
|
1355
|
+
if (!ctx.supportedReasoningEfforts.includes(level)) {
|
|
1356
|
+
return `invalid reasoning effort level "${level}". accepted levels: ${ctx.supportedReasoningEfforts.join(", ")}`;
|
|
1357
|
+
}
|
|
1358
|
+
ctx.setReasoningEffort(level);
|
|
1359
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1360
|
+
component: "repertoire",
|
|
1361
|
+
event: "repertoire.reasoning_effort_changed",
|
|
1362
|
+
message: `reasoning effort set to ${level}`,
|
|
1363
|
+
meta: { level },
|
|
1364
|
+
});
|
|
1365
|
+
return `reasoning effort set to "${level}".`;
|
|
1366
|
+
},
|
|
1367
|
+
requiredCapability: "reasoning-effort",
|
|
1368
|
+
summaryKeys: ["level"],
|
|
1369
|
+
},
|
|
697
1370
|
...tools_1.codingToolDefinitions,
|
|
698
1371
|
];
|
|
699
|
-
// Backward-compat: extract just the OpenAI tool schemas
|
|
700
1372
|
exports.tools = exports.baseToolDefinitions.map((d) => d.tool);
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
1373
|
+
exports.ponderTool = {
|
|
1374
|
+
type: "function",
|
|
1375
|
+
function: {
|
|
1376
|
+
name: "ponder",
|
|
1377
|
+
description: "i need to sit with this. from a conversation, takes the thread inward with a thought and a parting word. from inner dialog, keeps the wheel turning for another pass. must be the only tool call in the turn.",
|
|
1378
|
+
parameters: {
|
|
1379
|
+
type: "object",
|
|
1380
|
+
properties: {
|
|
1381
|
+
thought: {
|
|
1382
|
+
type: "string",
|
|
1383
|
+
description: "the question or thread that needs more thought — brief framing, not analysis. required from a conversation, ignored from inner dialog.",
|
|
1384
|
+
},
|
|
1385
|
+
say: {
|
|
1386
|
+
type: "string",
|
|
1387
|
+
description: "what you say before going quiet — speak to what caught your attention, not just that something did. required from a conversation, ignored from inner dialog.",
|
|
1388
|
+
},
|
|
1389
|
+
},
|
|
1390
|
+
},
|
|
1391
|
+
},
|
|
1392
|
+
};
|
|
1393
|
+
exports.observeTool = {
|
|
704
1394
|
type: "function",
|
|
705
1395
|
function: {
|
|
706
|
-
name: "
|
|
1396
|
+
name: "observe",
|
|
1397
|
+
description: "absorb what happened without responding — the moment doesn't call for words. must be the only tool call in the turn.",
|
|
1398
|
+
parameters: {
|
|
1399
|
+
type: "object",
|
|
1400
|
+
properties: {
|
|
1401
|
+
reason: { type: "string", description: "brief reason for staying silent (for logging)" },
|
|
1402
|
+
},
|
|
1403
|
+
},
|
|
1404
|
+
},
|
|
1405
|
+
};
|
|
1406
|
+
exports.settleTool = {
|
|
1407
|
+
type: "function",
|
|
1408
|
+
function: {
|
|
1409
|
+
name: "settle",
|
|
707
1410
|
description: "respond to the user with your message. call this tool when you are ready to deliver your response.",
|
|
708
1411
|
parameters: {
|
|
709
1412
|
type: "object",
|
|
710
|
-
properties: {
|
|
1413
|
+
properties: {
|
|
1414
|
+
answer: { type: "string" },
|
|
1415
|
+
intent: { type: "string", enum: ["complete", "blocked", "direct_reply"] },
|
|
1416
|
+
},
|
|
711
1417
|
required: ["answer"],
|
|
712
1418
|
},
|
|
713
1419
|
},
|
|
714
1420
|
};
|
|
1421
|
+
exports.restTool = {
|
|
1422
|
+
type: "function",
|
|
1423
|
+
function: {
|
|
1424
|
+
name: "rest",
|
|
1425
|
+
description: "put this down for now — the wheel stops until the next heartbeat. must be the only tool call in the turn.",
|
|
1426
|
+
parameters: {
|
|
1427
|
+
type: "object",
|
|
1428
|
+
properties: {},
|
|
1429
|
+
},
|
|
1430
|
+
},
|
|
1431
|
+
};
|