@ouro.bot/cli 0.1.0-alpha.4 → 0.1.0-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AdoptionSpecialist.ouro/agent.json +70 -9
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +117 -188
- package/assets/ouroboros.png +0 -0
- package/changelog.json +170 -0
- package/dist/heart/config.js +81 -8
- package/dist/heart/core.js +78 -45
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/daemon-cli.js +987 -77
- package/dist/heart/daemon/daemon-entry.js +14 -5
- package/dist/heart/daemon/daemon-runtime-sync.js +90 -0
- package/dist/heart/daemon/daemon.js +177 -9
- package/dist/heart/daemon/hatch-animation.js +35 -0
- package/dist/heart/daemon/hatch-flow.js +4 -20
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/launchd.js +134 -0
- package/dist/heart/daemon/message-router.js +15 -6
- package/dist/heart/daemon/ouro-bot-entry.js +0 -0
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-entry.js +0 -0
- package/dist/heart/daemon/ouro-path-installer.js +178 -0
- package/dist/heart/daemon/ouro-uti.js +11 -2
- package/dist/heart/daemon/process-manager.js +1 -1
- package/dist/heart/daemon/run-hooks.js +37 -0
- package/dist/heart/daemon/runtime-metadata.js +118 -0
- package/dist/heart/daemon/sense-manager.js +266 -0
- package/dist/heart/daemon/specialist-orchestrator.js +129 -0
- package/dist/heart/daemon/specialist-prompt.js +99 -0
- package/dist/heart/daemon/specialist-tools.js +283 -0
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/subagent-installer.js +10 -1
- 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/identity.js +96 -4
- package/dist/heart/kicks.js +1 -19
- package/dist/heart/providers/anthropic.js +16 -2
- package/dist/heart/sense-truth.js +61 -0
- package/dist/heart/streaming.js +96 -21
- package/dist/mind/bundle-manifest.js +70 -0
- package/dist/mind/context.js +7 -7
- package/dist/mind/first-impressions.js +2 -1
- package/dist/mind/friends/channel.js +43 -0
- package/dist/mind/friends/store-file.js +19 -0
- package/dist/mind/friends/types.js +9 -1
- package/dist/mind/memory.js +10 -3
- package/dist/mind/pending.js +10 -2
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +222 -7
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/coding/feedback.js +134 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +62 -4
- package/dist/repertoire/coding/spawner.js +3 -3
- package/dist/repertoire/coding/tools.js +41 -2
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/tasks/index.js +2 -9
- package/dist/repertoire/tasks/transitions.js +1 -2
- package/dist/repertoire/tools-base.js +202 -219
- package/dist/repertoire/tools-bluebubbles.js +93 -0
- package/dist/repertoire/tools-teams.js +58 -25
- package/dist/repertoire/tools.js +55 -35
- package/dist/senses/bluebubbles-client.js +434 -0
- package/dist/senses/bluebubbles-entry.js +11 -0
- package/dist/senses/bluebubbles-media.js +338 -0
- package/dist/senses/bluebubbles-model.js +261 -0
- package/dist/senses/bluebubbles-mutation-log.js +74 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +832 -0
- package/dist/senses/cli.js +327 -138
- package/dist/senses/debug-activity.js +127 -0
- package/dist/senses/inner-dialog.js +103 -55
- package/dist/senses/pipeline.js +124 -0
- package/dist/senses/teams.js +427 -112
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +14 -3
- package/subagents/README.md +40 -53
- package/subagents/work-doer.md +26 -24
- package/subagents/work-merger.md +24 -30
- package/subagents/work-planner.md +34 -25
- package/dist/inner-worker-entry.js +0 -4
|
@@ -33,19 +33,32 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.finalAnswerTool = exports.
|
|
36
|
+
exports.finalAnswerTool = exports.tools = exports.baseToolDefinitions = exports.editFileReadTracker = void 0;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
|
+
const fg = __importStar(require("fast-glob"));
|
|
38
39
|
const child_process_1 = require("child_process");
|
|
39
40
|
const path = __importStar(require("path"));
|
|
40
41
|
const skills_1 = require("./skills");
|
|
41
42
|
const config_1 = require("../heart/config");
|
|
42
43
|
const runtime_1 = require("../nerves/runtime");
|
|
43
44
|
const identity_1 = require("../heart/identity");
|
|
44
|
-
const os = __importStar(require("os"));
|
|
45
|
-
const tasks_1 = require("./tasks");
|
|
46
45
|
const tools_1 = require("./coding/tools");
|
|
47
46
|
const memory_1 = require("../mind/memory");
|
|
48
|
-
const
|
|
47
|
+
const pending_1 = require("../mind/pending");
|
|
48
|
+
// Tracks which file paths have been read via read_file in this session.
|
|
49
|
+
// edit_file requires a file to be read first (must-read-first guard).
|
|
50
|
+
exports.editFileReadTracker = new Set();
|
|
51
|
+
function buildContextDiff(lines, changeStart, changeEnd, contextSize = 3) {
|
|
52
|
+
const start = Math.max(0, changeStart - contextSize);
|
|
53
|
+
const end = Math.min(lines.length, changeEnd + contextSize);
|
|
54
|
+
const result = [];
|
|
55
|
+
for (let i = start; i < end; i++) {
|
|
56
|
+
const lineNum = i + 1;
|
|
57
|
+
const prefix = (i >= changeStart && i < changeEnd) ? ">" : " ";
|
|
58
|
+
result.push(`${prefix} ${lineNum} | ${lines[i]}`);
|
|
59
|
+
}
|
|
60
|
+
return result.join("\n");
|
|
61
|
+
}
|
|
49
62
|
exports.baseToolDefinitions = [
|
|
50
63
|
{
|
|
51
64
|
tool: {
|
|
@@ -55,12 +68,27 @@ exports.baseToolDefinitions = [
|
|
|
55
68
|
description: "read file contents",
|
|
56
69
|
parameters: {
|
|
57
70
|
type: "object",
|
|
58
|
-
properties: {
|
|
71
|
+
properties: {
|
|
72
|
+
path: { type: "string" },
|
|
73
|
+
offset: { type: "number", description: "1-based line number to start reading from" },
|
|
74
|
+
limit: { type: "number", description: "maximum number of lines to return" },
|
|
75
|
+
},
|
|
59
76
|
required: ["path"],
|
|
60
77
|
},
|
|
61
78
|
},
|
|
62
79
|
},
|
|
63
|
-
handler: (a) =>
|
|
80
|
+
handler: (a) => {
|
|
81
|
+
const content = fs.readFileSync(a.path, "utf-8");
|
|
82
|
+
exports.editFileReadTracker.add(a.path);
|
|
83
|
+
const offset = a.offset ? parseInt(a.offset, 10) : undefined;
|
|
84
|
+
const limit = a.limit ? parseInt(a.limit, 10) : undefined;
|
|
85
|
+
if (offset === undefined && limit === undefined)
|
|
86
|
+
return content;
|
|
87
|
+
const lines = content.split("\n");
|
|
88
|
+
const start = offset ? offset - 1 : 0;
|
|
89
|
+
const end = limit !== undefined ? start + limit : lines.length;
|
|
90
|
+
return lines.slice(start, end).join("\n");
|
|
91
|
+
},
|
|
64
92
|
},
|
|
65
93
|
{
|
|
66
94
|
tool: {
|
|
@@ -75,103 +103,213 @@ exports.baseToolDefinitions = [
|
|
|
75
103
|
},
|
|
76
104
|
},
|
|
77
105
|
},
|
|
78
|
-
handler: (a) =>
|
|
106
|
+
handler: (a) => {
|
|
107
|
+
fs.mkdirSync(path.dirname(a.path), { recursive: true });
|
|
108
|
+
fs.writeFileSync(a.path, a.content, "utf-8");
|
|
109
|
+
return "ok";
|
|
110
|
+
},
|
|
79
111
|
},
|
|
80
112
|
{
|
|
81
113
|
tool: {
|
|
82
114
|
type: "function",
|
|
83
115
|
function: {
|
|
84
|
-
name: "
|
|
85
|
-
description: "
|
|
116
|
+
name: "edit_file",
|
|
117
|
+
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
118
|
parameters: {
|
|
87
119
|
type: "object",
|
|
88
|
-
properties: {
|
|
89
|
-
|
|
120
|
+
properties: {
|
|
121
|
+
path: { type: "string" },
|
|
122
|
+
old_string: { type: "string" },
|
|
123
|
+
new_string: { type: "string" },
|
|
124
|
+
},
|
|
125
|
+
required: ["path", "old_string", "new_string"],
|
|
90
126
|
},
|
|
91
127
|
},
|
|
92
128
|
},
|
|
93
|
-
handler: (a) =>
|
|
129
|
+
handler: (a) => {
|
|
130
|
+
if (!exports.editFileReadTracker.has(a.path)) {
|
|
131
|
+
return `error: you must read the file with read_file before editing it. call read_file on ${a.path} first.`;
|
|
132
|
+
}
|
|
133
|
+
let content;
|
|
134
|
+
try {
|
|
135
|
+
content = fs.readFileSync(a.path, "utf-8");
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
return `error: could not read file: ${e instanceof Error ? e.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(e)}`;
|
|
139
|
+
}
|
|
140
|
+
// Count occurrences
|
|
141
|
+
const occurrences = [];
|
|
142
|
+
let searchFrom = 0;
|
|
143
|
+
while (true) {
|
|
144
|
+
const idx = content.indexOf(a.old_string, searchFrom);
|
|
145
|
+
if (idx === -1)
|
|
146
|
+
break;
|
|
147
|
+
occurrences.push(idx);
|
|
148
|
+
searchFrom = idx + 1;
|
|
149
|
+
}
|
|
150
|
+
if (occurrences.length === 0) {
|
|
151
|
+
return `error: old_string not found in ${a.path}`;
|
|
152
|
+
}
|
|
153
|
+
if (occurrences.length > 1) {
|
|
154
|
+
return `error: old_string is ambiguous -- found ${occurrences.length} matches in ${a.path}. provide more context to make the match unique.`;
|
|
155
|
+
}
|
|
156
|
+
// Single unique match -- replace
|
|
157
|
+
const idx = occurrences[0];
|
|
158
|
+
const updated = content.slice(0, idx) + a.new_string + content.slice(idx + a.old_string.length);
|
|
159
|
+
fs.writeFileSync(a.path, updated, "utf-8");
|
|
160
|
+
// Build contextual diff
|
|
161
|
+
const lines = updated.split("\n");
|
|
162
|
+
const prefixLines = content.slice(0, idx).split("\n");
|
|
163
|
+
const changeStartLine = prefixLines.length - 1;
|
|
164
|
+
const newStringLines = a.new_string.split("\n");
|
|
165
|
+
const changeEndLine = changeStartLine + newStringLines.length;
|
|
166
|
+
return buildContextDiff(lines, changeStartLine, changeEndLine);
|
|
167
|
+
},
|
|
94
168
|
},
|
|
95
169
|
{
|
|
96
170
|
tool: {
|
|
97
171
|
type: "function",
|
|
98
172
|
function: {
|
|
99
|
-
name: "
|
|
100
|
-
description: "
|
|
173
|
+
name: "glob",
|
|
174
|
+
description: "find files matching a glob pattern. returns matching paths sorted alphabetically, one per line.",
|
|
101
175
|
parameters: {
|
|
102
176
|
type: "object",
|
|
103
|
-
properties: {
|
|
104
|
-
|
|
177
|
+
properties: {
|
|
178
|
+
pattern: { type: "string", description: "glob pattern (e.g. **/*.ts)" },
|
|
179
|
+
cwd: { type: "string", description: "directory to search from (defaults to process.cwd())" },
|
|
180
|
+
},
|
|
181
|
+
required: ["pattern"],
|
|
105
182
|
},
|
|
106
183
|
},
|
|
107
184
|
},
|
|
108
|
-
handler: (a) =>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
.join("\n")
|
|
185
|
+
handler: (a) => {
|
|
186
|
+
const cwd = a.cwd || process.cwd();
|
|
187
|
+
const matches = fg.globSync(a.pattern, { cwd, dot: true });
|
|
188
|
+
return matches.sort().join("\n");
|
|
189
|
+
},
|
|
112
190
|
},
|
|
113
191
|
{
|
|
114
192
|
tool: {
|
|
115
193
|
type: "function",
|
|
116
194
|
function: {
|
|
117
|
-
name: "
|
|
118
|
-
description: "
|
|
195
|
+
name: "grep",
|
|
196
|
+
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
197
|
parameters: {
|
|
120
198
|
type: "object",
|
|
121
199
|
properties: {
|
|
122
|
-
|
|
123
|
-
|
|
200
|
+
pattern: { type: "string", description: "regex pattern to search for" },
|
|
201
|
+
path: { type: "string", description: "file or directory to search" },
|
|
202
|
+
context_lines: { type: "number", description: "number of surrounding context lines (default 0)" },
|
|
203
|
+
include: { type: "string", description: "glob filter to limit searched files (e.g. *.ts)" },
|
|
124
204
|
},
|
|
125
|
-
required: ["
|
|
205
|
+
required: ["pattern", "path"],
|
|
126
206
|
},
|
|
127
207
|
},
|
|
128
208
|
},
|
|
129
209
|
handler: (a) => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
210
|
+
const targetPath = a.path;
|
|
211
|
+
const regex = new RegExp(a.pattern);
|
|
212
|
+
const contextLines = parseInt(a.context_lines || "0", 10);
|
|
213
|
+
const includeGlob = a.include || undefined;
|
|
214
|
+
function searchFile(filePath) {
|
|
215
|
+
let content;
|
|
216
|
+
try {
|
|
217
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
133
218
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
219
|
+
catch {
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
const lines = content.split("\n");
|
|
223
|
+
const matchIndices = new Set();
|
|
224
|
+
for (let i = 0; i < lines.length; i++) {
|
|
225
|
+
if (regex.test(lines[i])) {
|
|
226
|
+
matchIndices.add(i);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (matchIndices.size === 0)
|
|
230
|
+
return [];
|
|
231
|
+
const outputIndices = new Set();
|
|
232
|
+
for (const idx of matchIndices) {
|
|
233
|
+
const start = Math.max(0, idx - contextLines);
|
|
234
|
+
const end = Math.min(lines.length - 1, idx + contextLines);
|
|
235
|
+
for (let i = start; i <= end; i++) {
|
|
236
|
+
outputIndices.add(i);
|
|
137
237
|
}
|
|
138
|
-
(0, child_process_1.execSync)(`git add ${p}`, { encoding: "utf-8" });
|
|
139
238
|
}
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
239
|
+
const sortedIndices = [...outputIndices].sort((a, b) => a - b);
|
|
240
|
+
const results = [];
|
|
241
|
+
for (const idx of sortedIndices) {
|
|
242
|
+
const lineNum = idx + 1;
|
|
243
|
+
if (matchIndices.has(idx)) {
|
|
244
|
+
results.push(`${filePath}:${lineNum}: ${lines[idx]}`);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
results.push(`-${filePath}:${lineNum}: ${lines[idx]}`);
|
|
248
|
+
}
|
|
143
249
|
}
|
|
144
|
-
|
|
145
|
-
return `${diff}\ncommitted`;
|
|
250
|
+
return results;
|
|
146
251
|
}
|
|
147
|
-
|
|
148
|
-
|
|
252
|
+
function collectFiles(dirPath) {
|
|
253
|
+
const files = [];
|
|
254
|
+
function walk(dir) {
|
|
255
|
+
let entries;
|
|
256
|
+
try {
|
|
257
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
for (const entry of entries) {
|
|
263
|
+
const fullPath = path.join(dir, entry.name);
|
|
264
|
+
if (entry.isDirectory()) {
|
|
265
|
+
walk(fullPath);
|
|
266
|
+
}
|
|
267
|
+
else if (entry.isFile()) {
|
|
268
|
+
files.push(fullPath);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
walk(dirPath);
|
|
273
|
+
return files.sort();
|
|
274
|
+
}
|
|
275
|
+
function matchesGlob(filePath, glob) {
|
|
276
|
+
const escaped = glob
|
|
277
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
278
|
+
.replace(/\*/g, ".*")
|
|
279
|
+
.replace(/\?/g, ".");
|
|
280
|
+
return new RegExp(`(^|/)${escaped}$`).test(filePath);
|
|
281
|
+
}
|
|
282
|
+
const stat = fs.statSync(targetPath, { throwIfNoEntry: false });
|
|
283
|
+
if (!stat)
|
|
284
|
+
return "";
|
|
285
|
+
if (stat.isFile()) {
|
|
286
|
+
return searchFile(targetPath).join("\n");
|
|
287
|
+
}
|
|
288
|
+
let files = collectFiles(targetPath);
|
|
289
|
+
if (includeGlob) {
|
|
290
|
+
files = files.filter((f) => matchesGlob(f, includeGlob));
|
|
291
|
+
}
|
|
292
|
+
const allResults = [];
|
|
293
|
+
for (const file of files) {
|
|
294
|
+
allResults.push(...searchFile(file));
|
|
149
295
|
}
|
|
296
|
+
return allResults.join("\n");
|
|
150
297
|
},
|
|
151
298
|
},
|
|
152
299
|
{
|
|
153
300
|
tool: {
|
|
154
301
|
type: "function",
|
|
155
302
|
function: {
|
|
156
|
-
name: "
|
|
157
|
-
description: "
|
|
303
|
+
name: "shell",
|
|
304
|
+
description: "run shell command",
|
|
158
305
|
parameters: {
|
|
159
306
|
type: "object",
|
|
160
|
-
properties: {
|
|
161
|
-
command: { type: "string" },
|
|
162
|
-
},
|
|
307
|
+
properties: { command: { type: "string" } },
|
|
163
308
|
required: ["command"],
|
|
164
309
|
},
|
|
165
310
|
},
|
|
166
311
|
},
|
|
167
|
-
handler: (a) => {
|
|
168
|
-
try {
|
|
169
|
-
return (0, child_process_1.execSync)(`gh ${a.command}`, { encoding: "utf-8", timeout: 60000 });
|
|
170
|
-
}
|
|
171
|
-
catch (e) {
|
|
172
|
-
return `error: ${e}`;
|
|
173
|
-
}
|
|
174
|
-
},
|
|
312
|
+
handler: (a) => (0, child_process_1.execSync)(a.command, { encoding: "utf-8", timeout: 30000 }),
|
|
175
313
|
},
|
|
176
314
|
{
|
|
177
315
|
tool: {
|
|
@@ -206,20 +344,6 @@ exports.baseToolDefinitions = [
|
|
|
206
344
|
}
|
|
207
345
|
},
|
|
208
346
|
},
|
|
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
|
-
}),
|
|
222
|
-
},
|
|
223
347
|
{
|
|
224
348
|
tool: {
|
|
225
349
|
type: "function",
|
|
@@ -235,7 +359,7 @@ exports.baseToolDefinitions = [
|
|
|
235
359
|
},
|
|
236
360
|
handler: (a) => {
|
|
237
361
|
try {
|
|
238
|
-
const result = (0, child_process_1.spawnSync)("claude", ["-p", "--dangerously-skip-permissions", "--add-dir", "."], {
|
|
362
|
+
const result = (0, child_process_1.spawnSync)("claude", ["-p", "--no-session-persistence", "--dangerously-skip-permissions", "--add-dir", "."], {
|
|
239
363
|
input: a.prompt,
|
|
240
364
|
encoding: "utf-8",
|
|
241
365
|
timeout: 60000,
|
|
@@ -375,151 +499,6 @@ exports.baseToolDefinitions = [
|
|
|
375
499
|
return JSON.stringify(friend, null, 2);
|
|
376
500
|
},
|
|
377
501
|
},
|
|
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
|
-
},
|
|
522
|
-
},
|
|
523
502
|
{
|
|
524
503
|
tool: {
|
|
525
504
|
type: "function",
|
|
@@ -628,7 +607,7 @@ exports.baseToolDefinitions = [
|
|
|
628
607
|
const channel = args.channel;
|
|
629
608
|
const key = args.key || "session";
|
|
630
609
|
const count = parseInt(args.messageCount || "20", 10);
|
|
631
|
-
const sessFile =
|
|
610
|
+
const sessFile = (0, config_1.resolveSessionPath)(friendId, channel, key);
|
|
632
611
|
const raw = fs.readFileSync(sessFile, "utf-8");
|
|
633
612
|
const data = JSON.parse(raw);
|
|
634
613
|
const messages = (data.messages || [])
|
|
@@ -677,12 +656,18 @@ exports.baseToolDefinitions = [
|
|
|
677
656
|
const key = args.key || "session";
|
|
678
657
|
const content = args.content;
|
|
679
658
|
const now = Date.now();
|
|
680
|
-
const
|
|
659
|
+
const agentName = (0, identity_1.getAgentName)();
|
|
660
|
+
// Self-routing: messages to "self" always go to inner dialog pending dir,
|
|
661
|
+
// regardless of the channel or key the agent specified.
|
|
662
|
+
const isSelf = friendId === "self";
|
|
663
|
+
const pendingDir = isSelf
|
|
664
|
+
? (0, pending_1.getInnerDialogPendingDir)(agentName)
|
|
665
|
+
: (0, pending_1.getPendingDir)(agentName, friendId, channel, key);
|
|
681
666
|
fs.mkdirSync(pendingDir, { recursive: true });
|
|
682
667
|
const fileName = `${now}-${Math.random().toString(36).slice(2, 10)}.json`;
|
|
683
668
|
const filePath = path.join(pendingDir, fileName);
|
|
684
669
|
const envelope = {
|
|
685
|
-
from:
|
|
670
|
+
from: agentName,
|
|
686
671
|
friendId,
|
|
687
672
|
channel,
|
|
688
673
|
key,
|
|
@@ -691,15 +676,13 @@ exports.baseToolDefinitions = [
|
|
|
691
676
|
};
|
|
692
677
|
fs.writeFileSync(filePath, JSON.stringify(envelope, null, 2));
|
|
693
678
|
const preview = content.length > 80 ? content.slice(0, 80) + "…" : content;
|
|
694
|
-
|
|
679
|
+
const target = isSelf ? "inner/dialog" : `${channel}/${key}`;
|
|
680
|
+
return `message queued for delivery to ${friendId} on ${target}. preview: "${preview}". it will be delivered when their session is next active.`;
|
|
695
681
|
},
|
|
696
682
|
},
|
|
697
683
|
...tools_1.codingToolDefinitions,
|
|
698
684
|
];
|
|
699
|
-
// Backward-compat: extract just the OpenAI tool schemas
|
|
700
685
|
exports.tools = exports.baseToolDefinitions.map((d) => d.tool);
|
|
701
|
-
// Backward-compat: extract just the handlers by name
|
|
702
|
-
exports.baseToolHandlers = Object.fromEntries(exports.baseToolDefinitions.map((d) => [d.tool.function.name, d.handler]));
|
|
703
686
|
exports.finalAnswerTool = {
|
|
704
687
|
type: "function",
|
|
705
688
|
function: {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bluebubblesToolDefinitions = void 0;
|
|
4
|
+
const runtime_1 = require("../nerves/runtime");
|
|
5
|
+
exports.bluebubblesToolDefinitions = [
|
|
6
|
+
{
|
|
7
|
+
tool: {
|
|
8
|
+
type: "function",
|
|
9
|
+
function: {
|
|
10
|
+
name: "bluebubbles_set_reply_target",
|
|
11
|
+
description: "choose where the current iMessage turn should land. use this when you want to widen back to top-level or route your update into a specific active thread in the same chat.",
|
|
12
|
+
parameters: {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {
|
|
15
|
+
target: {
|
|
16
|
+
type: "string",
|
|
17
|
+
enum: ["current_lane", "top_level", "thread"],
|
|
18
|
+
description: "current_lane mirrors the current inbound lane, top_level answers in the main chat, and thread targets a specific active thread.",
|
|
19
|
+
},
|
|
20
|
+
threadOriginatorGuid: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "required when target=thread; use one of the thread ids surfaced in the inbound iMessage context.",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ["target"],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
handler: (args, ctx) => {
|
|
30
|
+
const target = typeof args.target === "string" ? args.target.trim() : "";
|
|
31
|
+
const controller = ctx?.bluebubblesReplyTarget;
|
|
32
|
+
if (!controller) {
|
|
33
|
+
(0, runtime_1.emitNervesEvent)({
|
|
34
|
+
level: "warn",
|
|
35
|
+
component: "tools",
|
|
36
|
+
event: "tool.error",
|
|
37
|
+
message: "bluebubbles reply target missing controller",
|
|
38
|
+
meta: { target },
|
|
39
|
+
});
|
|
40
|
+
return "bluebubbles reply targeting is not available in this context.";
|
|
41
|
+
}
|
|
42
|
+
if (target === "current_lane") {
|
|
43
|
+
const result = controller.setSelection({ target: "current_lane" });
|
|
44
|
+
(0, runtime_1.emitNervesEvent)({
|
|
45
|
+
component: "tools",
|
|
46
|
+
event: "tool.end",
|
|
47
|
+
message: "bluebubbles reply target updated",
|
|
48
|
+
meta: { target: "current_lane", success: true },
|
|
49
|
+
});
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
if (target === "top_level") {
|
|
53
|
+
const result = controller.setSelection({ target: "top_level" });
|
|
54
|
+
(0, runtime_1.emitNervesEvent)({
|
|
55
|
+
component: "tools",
|
|
56
|
+
event: "tool.end",
|
|
57
|
+
message: "bluebubbles reply target updated",
|
|
58
|
+
meta: { target: "top_level", success: true },
|
|
59
|
+
});
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
if (target === "thread") {
|
|
63
|
+
const threadOriginatorGuid = typeof args.threadOriginatorGuid === "string" ? args.threadOriginatorGuid.trim() : "";
|
|
64
|
+
if (!threadOriginatorGuid) {
|
|
65
|
+
(0, runtime_1.emitNervesEvent)({
|
|
66
|
+
level: "warn",
|
|
67
|
+
component: "tools",
|
|
68
|
+
event: "tool.error",
|
|
69
|
+
message: "bluebubbles reply target missing thread id",
|
|
70
|
+
meta: { target: "thread" },
|
|
71
|
+
});
|
|
72
|
+
return "threadOriginatorGuid is required when target=thread.";
|
|
73
|
+
}
|
|
74
|
+
const result = controller.setSelection({ target: "thread", threadOriginatorGuid });
|
|
75
|
+
(0, runtime_1.emitNervesEvent)({
|
|
76
|
+
component: "tools",
|
|
77
|
+
event: "tool.end",
|
|
78
|
+
message: "bluebubbles reply target updated",
|
|
79
|
+
meta: { target: "thread", success: true },
|
|
80
|
+
});
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
level: "warn",
|
|
85
|
+
component: "tools",
|
|
86
|
+
event: "tool.error",
|
|
87
|
+
message: "bluebubbles reply target invalid target",
|
|
88
|
+
meta: { target },
|
|
89
|
+
});
|
|
90
|
+
return "target must be one of: current_lane, top_level, thread.";
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
];
|