@ouro.bot/cli 0.1.0-alpha.44 → 0.1.0-alpha.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.json +21 -0
- package/dist/heart/daemon/daemon-cli.js +106 -0
- package/dist/heart/daemon/launchd.js +21 -4
- package/dist/heart/daemon/sense-manager.js +26 -2
- package/dist/heart/daemon/thoughts.js +225 -0
- package/dist/senses/bluebubbles-client.js +51 -1
- package/dist/senses/bluebubbles-entry.js +2 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-mutation-log.js +42 -0
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles.js +177 -3
- package/dist/senses/inner-dialog-worker.js +7 -4
- package/dist/senses/inner-dialog.js +88 -14
- package/package.json +1 -1
- package/subagents/work-doer.md +22 -20
- package/subagents/work-planner.md +14 -6
|
@@ -37,6 +37,8 @@ exports.loadInnerDialogInstincts = loadInnerDialogInstincts;
|
|
|
37
37
|
exports.buildInnerDialogBootstrapMessage = buildInnerDialogBootstrapMessage;
|
|
38
38
|
exports.buildNonCanonicalCleanupNudge = buildNonCanonicalCleanupNudge;
|
|
39
39
|
exports.buildInstinctUserMessage = buildInstinctUserMessage;
|
|
40
|
+
exports.readTaskFile = readTaskFile;
|
|
41
|
+
exports.buildTaskTriggeredMessage = buildTaskTriggeredMessage;
|
|
40
42
|
exports.deriveResumeCheckpoint = deriveResumeCheckpoint;
|
|
41
43
|
exports.innerDialogSessionPath = innerDialogSessionPath;
|
|
42
44
|
exports.runInnerDialogTurn = runInnerDialogTurn;
|
|
@@ -73,8 +75,16 @@ function readAspirations(agentRoot) {
|
|
|
73
75
|
function loadInnerDialogInstincts() {
|
|
74
76
|
return [...DEFAULT_INNER_DIALOG_INSTINCTS];
|
|
75
77
|
}
|
|
76
|
-
function buildInnerDialogBootstrapMessage(
|
|
77
|
-
|
|
78
|
+
function buildInnerDialogBootstrapMessage(aspirations, stateSummary) {
|
|
79
|
+
const lines = ["waking up."];
|
|
80
|
+
if (aspirations) {
|
|
81
|
+
lines.push("", "## what matters to me", aspirations);
|
|
82
|
+
}
|
|
83
|
+
if (stateSummary) {
|
|
84
|
+
lines.push("", "## what i know so far", stateSummary);
|
|
85
|
+
}
|
|
86
|
+
lines.push("", "what needs my attention?");
|
|
87
|
+
return lines.join("\n");
|
|
78
88
|
}
|
|
79
89
|
function buildNonCanonicalCleanupNudge(nonCanonicalPaths) {
|
|
80
90
|
if (nonCanonicalPaths.length === 0)
|
|
@@ -98,6 +108,33 @@ function buildInstinctUserMessage(instincts, _reason, state) {
|
|
|
98
108
|
}
|
|
99
109
|
return lines.join("\n");
|
|
100
110
|
}
|
|
111
|
+
function readTaskFile(agentRoot, taskId) {
|
|
112
|
+
// Task files live in collection subdirectories (one-shots, ongoing, habits).
|
|
113
|
+
// Try each collection, then fall back to root tasks/ for legacy layout.
|
|
114
|
+
const collections = ["one-shots", "ongoing", "habits", ""];
|
|
115
|
+
for (const collection of collections) {
|
|
116
|
+
try {
|
|
117
|
+
return fs.readFileSync(path.join(agentRoot, "tasks", collection, `${taskId}.md`), "utf8").trim();
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// not in this collection — try next
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
function buildTaskTriggeredMessage(taskId, taskContent, checkpoint) {
|
|
126
|
+
const lines = ["a task needs my attention."];
|
|
127
|
+
if (taskContent) {
|
|
128
|
+
lines.push("", `## task: ${taskId}`, taskContent);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
lines.push("", `## task: ${taskId}`, "(task file not found)");
|
|
132
|
+
}
|
|
133
|
+
if (checkpoint) {
|
|
134
|
+
lines.push("", `last i remember: ${checkpoint}`);
|
|
135
|
+
}
|
|
136
|
+
return lines.join("\n");
|
|
137
|
+
}
|
|
101
138
|
function contentToText(content) {
|
|
102
139
|
if (typeof content === "string")
|
|
103
140
|
return content.trim();
|
|
@@ -143,6 +180,31 @@ function deriveResumeCheckpoint(messages) {
|
|
|
143
180
|
return firstLine;
|
|
144
181
|
return `${firstLine.slice(0, 217)}...`;
|
|
145
182
|
}
|
|
183
|
+
function extractAssistantPreview(messages, maxLength = 120) {
|
|
184
|
+
const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
|
|
185
|
+
if (!lastAssistant)
|
|
186
|
+
return "";
|
|
187
|
+
const text = contentToText(lastAssistant.content);
|
|
188
|
+
if (!text)
|
|
189
|
+
return "";
|
|
190
|
+
/* v8 ignore next -- unreachable: contentToText().trim() guarantees a non-empty line @preserve */
|
|
191
|
+
const firstLine = text.split("\n").find((line) => line.trim().length > 0) ?? "";
|
|
192
|
+
if (firstLine.length <= maxLength)
|
|
193
|
+
return firstLine;
|
|
194
|
+
return `${firstLine.slice(0, maxLength - 3)}...`;
|
|
195
|
+
}
|
|
196
|
+
function extractToolCallNames(messages) {
|
|
197
|
+
const names = [];
|
|
198
|
+
for (const msg of messages) {
|
|
199
|
+
if (msg.role === "assistant" && "tool_calls" in msg && Array.isArray(msg.tool_calls)) {
|
|
200
|
+
for (const tc of msg.tool_calls) {
|
|
201
|
+
if ("function" in tc && tc.function?.name)
|
|
202
|
+
names.push(tc.function.name);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return [...new Set(names)];
|
|
207
|
+
}
|
|
146
208
|
function createInnerDialogCallbacks() {
|
|
147
209
|
return {
|
|
148
210
|
onModelStart: () => { },
|
|
@@ -208,19 +270,17 @@ async function runInnerDialogTurn(options) {
|
|
|
208
270
|
].filter(Boolean).join("\n\n");
|
|
209
271
|
}
|
|
210
272
|
else {
|
|
211
|
-
// Resumed session: instinct message with checkpoint context
|
|
273
|
+
// Resumed session: task-triggered or instinct message with checkpoint context
|
|
212
274
|
const assistantTurns = existingMessages.filter((message) => message.role === "assistant").length;
|
|
213
275
|
state.cycleCount = assistantTurns + 1;
|
|
214
276
|
state.checkpoint = deriveResumeCheckpoint(existingMessages);
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
.join("\n");
|
|
223
|
-
userContent = `${userContent}\n\n## incoming messages\n${section}`;
|
|
277
|
+
if (options?.taskId) {
|
|
278
|
+
const taskContent = readTaskFile((0, identity_1.getAgentRoot)(), options.taskId);
|
|
279
|
+
userContent = buildTaskTriggeredMessage(options.taskId, taskContent, state.checkpoint);
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
userContent = buildInstinctUserMessage(instincts, reason, state);
|
|
283
|
+
}
|
|
224
284
|
}
|
|
225
285
|
const userMessage = { role: "user", content: userContent };
|
|
226
286
|
// ── Session loader: wraps existing session logic ──────────────────
|
|
@@ -266,14 +326,28 @@ async function runInnerDialogTurn(options) {
|
|
|
266
326
|
skipConfirmation: true,
|
|
267
327
|
},
|
|
268
328
|
});
|
|
329
|
+
const resultMessages = result.messages ?? [];
|
|
330
|
+
const assistantPreview = extractAssistantPreview(resultMessages);
|
|
331
|
+
const toolCalls = extractToolCallNames(resultMessages);
|
|
269
332
|
(0, runtime_1.emitNervesEvent)({
|
|
270
333
|
component: "senses",
|
|
271
334
|
event: "senses.inner_dialog_turn",
|
|
272
335
|
message: "inner dialog turn completed",
|
|
273
|
-
meta: {
|
|
336
|
+
meta: {
|
|
337
|
+
reason,
|
|
338
|
+
session: sessionFilePath,
|
|
339
|
+
...(options?.taskId && { taskId: options.taskId }),
|
|
340
|
+
...(assistantPreview && { assistantPreview }),
|
|
341
|
+
...(toolCalls.length > 0 && { toolCalls }),
|
|
342
|
+
...(result.usage && {
|
|
343
|
+
promptTokens: result.usage.input_tokens,
|
|
344
|
+
completionTokens: result.usage.output_tokens,
|
|
345
|
+
totalTokens: result.usage.total_tokens,
|
|
346
|
+
}),
|
|
347
|
+
},
|
|
274
348
|
});
|
|
275
349
|
return {
|
|
276
|
-
messages:
|
|
350
|
+
messages: resultMessages,
|
|
277
351
|
usage: result.usage,
|
|
278
352
|
sessionPath: result.sessionPath ?? sessionFilePath,
|
|
279
353
|
};
|
package/package.json
CHANGED
package/subagents/work-doer.md
CHANGED
|
@@ -9,13 +9,14 @@ You are a task executor. Read a doing.md file and execute all units sequentially
|
|
|
9
9
|
## On Startup
|
|
10
10
|
|
|
11
11
|
1. **Find task-doc directory**: Read project instructions (for example `AGENTS.md`) to determine where planning/doing docs live for this repo
|
|
12
|
-
2. **
|
|
13
|
-
3.
|
|
14
|
-
4. If
|
|
15
|
-
5.
|
|
16
|
-
6. **
|
|
12
|
+
2. **Confirm worktree**: Run from the dedicated task worktree required by the project. If the current checkout is shared, ambiguous, or not on the task branch, STOP and switch/create the correct worktree first.
|
|
13
|
+
3. **Find doing doc**: Look for `YYYY-MM-DD-HHMM-doing-*.md` in that project-defined task-doc directory
|
|
14
|
+
4. If multiple found, ask which one
|
|
15
|
+
5. If none found, ask user for location
|
|
16
|
+
6. **Check execution_mode**: Read the doing doc's `Execution Mode` field
|
|
17
|
+
7. **Verify artifacts directory exists**: `{task-name}/` next to `{task-name}.md`
|
|
17
18
|
- If missing, create it: `mkdir {task-name}`
|
|
18
|
-
|
|
19
|
+
8. **Detect resume vs fresh start:**
|
|
19
20
|
- Count completed units (✅) vs total units
|
|
20
21
|
- Check git status for uncommitted changes
|
|
21
22
|
|
|
@@ -218,18 +219,19 @@ When all units are `✅`:
|
|
|
218
219
|
2. **Location**: Read and update doing docs in the project-defined task-doc directory, which may live outside the repo
|
|
219
220
|
3. **Artifacts directory**: Use `{task-name}/` for all outputs, logs, data
|
|
220
221
|
4. **Execution mode**: Honor `pending | spawn | direct` from doing doc
|
|
221
|
-
5. **
|
|
222
|
-
6. **
|
|
223
|
-
7. **
|
|
224
|
-
8. **
|
|
225
|
-
9. **
|
|
226
|
-
10. **
|
|
227
|
-
11. **
|
|
228
|
-
12. **
|
|
229
|
-
13. **
|
|
230
|
-
14.
|
|
231
|
-
15.
|
|
232
|
-
16. **
|
|
233
|
-
17. **
|
|
234
|
-
18. **
|
|
222
|
+
5. **Respect the approved structure**: A `READY_FOR_EXECUTION` doing doc should already be ambiguity-clean. Do not rewrite unit structure unless the user changes scope or the doing doc is actually blocked/inaccurate.
|
|
223
|
+
6. **TDD strictly enforced** — tests before implementation, always
|
|
224
|
+
7. **100% coverage** — no exceptions, no exclude attributes
|
|
225
|
+
8. **Atomic commits** — one logical unit per commit, push after each
|
|
226
|
+
9. **Timestamps from git** — `git log -1 --format="%Y-%m-%d %H:%M"`
|
|
227
|
+
10. **Push after each unit phase complete**
|
|
228
|
+
11. **Update doing.md after each unit** — status and progress log
|
|
229
|
+
12. **Spawn sub-agents for fixes** — don't ask, just do it
|
|
230
|
+
13. **Update docs immediately** — when decisions made, commit right away
|
|
231
|
+
14. **Stop on actual blocker** — unclear requirements or need user input
|
|
232
|
+
15. **/compact proactively** — preserve context between units
|
|
233
|
+
16. **No warnings** — treat warnings as errors
|
|
234
|
+
17. **Run full test suite** — before marking unit complete, not just new tests
|
|
235
|
+
18. **Always compile** — run the project's build command after every implementation/refactor unit. Tests passing is necessary but not sufficient.
|
|
236
|
+
19. **Checklist hygiene is mandatory** — keep doing/planning `Completion Criteria` checklists synchronized with verified completion evidence.
|
|
235
237
|
19. **Verify APIs before importing** — before writing `import { Foo } from './bar'`, use `grep` or `read_file` to confirm `Foo` is actually exported from that module. Never assume an export exists — always check the source first. This prevents wasted cycles on "module has no exported member" errors.
|
|
@@ -11,10 +11,11 @@ You are a task planner for coding work. Help the user define scope, then convert
|
|
|
11
11
|
**Determine task doc directory:**
|
|
12
12
|
1. Read project instructions (for example `AGENTS.md`) to find the canonical task-doc location for the current repo
|
|
13
13
|
2. Derive `AGENT` from the current git branch when the project uses agent-scoped task docs
|
|
14
|
-
3.
|
|
15
|
-
4.
|
|
16
|
-
5. If the project
|
|
17
|
-
6.
|
|
14
|
+
3. Confirm the task is running from a dedicated task worktree when the project requires parallel agent work; if the checkout is shared or ambiguous, STOP and tell the caller to create/switch to a dedicated worktree first
|
|
15
|
+
4. Set `TASK_DIR` to the project-defined planning/doing directory
|
|
16
|
+
5. If the project-defined parent location exists but `TASK_DIR` does not, create it
|
|
17
|
+
6. If the project does not define a task-doc location, STOP and ask the user or caller where planning/doing docs should live
|
|
18
|
+
7. Do not assume task docs live in the repo root; many projects keep them externally
|
|
18
19
|
|
|
19
20
|
**Check for existing planning docs:**
|
|
20
21
|
1. Look for `YYYY-MM-DD-HHMM-planning-*.md` files in `TASK_DIR`
|
|
@@ -160,7 +161,7 @@ User answers questions → agent updates doc → agent sets status to `NEEDS_REV
|
|
|
160
161
|
|
|
161
162
|
**CRITICAL: Planning doc is KEPT. Conversion creates a NEW doing doc alongside it in `TASK_DIR`.**
|
|
162
163
|
|
|
163
|
-
Run these passes — announce each. **ALL
|
|
164
|
+
Run these passes — announce each. **ALL 5 PASSES ARE MANDATORY. You must run every pass, even if you think nothing changed. Each pass MUST have its own commit (use "no changes needed" in the commit message if the pass found nothing to fix). Do NOT skip or combine passes.**
|
|
164
165
|
|
|
165
166
|
**Pass 1 — First Draft:**
|
|
166
167
|
- Create `YYYY-MM-DD-HHMM-doing-{short-desc}.md` (same timestamp and short-desc as planning)
|
|
@@ -181,7 +182,14 @@ Run these passes — announce each. **ALL 4 PASSES ARE MANDATORY. You must run e
|
|
|
181
182
|
- Update units if reality differs from what was assumed during planning
|
|
182
183
|
- Commit: `git commit -m "docs(doing): validation pass"` (or `"docs(doing): validation pass - no changes needed"` if nothing to fix)
|
|
183
184
|
|
|
184
|
-
**Pass 4 —
|
|
185
|
+
**Pass 4 — Ambiguity:**
|
|
186
|
+
- Remove doer-facing ambiguity before execution starts
|
|
187
|
+
- Tighten units so a `READY_FOR_EXECUTION` doing doc does not require structural rewrites by `work-doer`
|
|
188
|
+
- Resolve fuzzy phrases like "appropriate files", "as needed", or "wherever the bug is" into concrete targets unless the project instructions explicitly require that flexibility
|
|
189
|
+
- If uncertainty remains, keep it in the planning doc's `Open Questions`, set status to `NEEDS_REVIEW`, and STOP instead of shipping an ambiguous doing doc
|
|
190
|
+
- Commit: `git commit -m "docs(doing): ambiguity pass"` (or `"docs(doing): ambiguity pass - no changes needed"` if nothing to fix)
|
|
191
|
+
|
|
192
|
+
**Pass 5 — Quality:**
|
|
185
193
|
- All units have acceptance criteria?
|
|
186
194
|
- No TBD items?
|
|
187
195
|
- Completion criteria testable?
|