@ouro.bot/cli 0.1.0-alpha.724 → 0.1.0-alpha.725
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.json +12 -0
- package/dist/heart/active-work.js +10 -3
- package/dist/heart/core.js +343 -77
- package/dist/heart/daemon/cli-exec.js +72 -14
- package/dist/heart/daemon/cli-help.js +13 -2
- package/dist/heart/daemon/cli-parse.js +82 -1
- package/dist/heart/daemon/daemon.js +286 -33
- package/dist/heart/daemon/doctor.js +21 -6
- package/dist/heart/daemon/habit-private-wake.js +6 -1
- package/dist/heart/habits/habit-cancel.js +810 -0
- package/dist/heart/habits/habit-lifecycle.js +1327 -0
- package/dist/heart/habits/habit-migration.js +28 -23
- package/dist/heart/habits/habit-parser.js +72 -8
- package/dist/heart/habits/habit-runtime-state.js +3 -24
- package/dist/heart/habits/habit-scheduler.js +268 -50
- package/dist/heart/habits/habit-session.js +16 -0
- package/dist/heart/hatch/hatch-flow.js +6 -4
- package/dist/heart/mailbox/readers/runtime-readers.js +26 -27
- package/dist/heart/orientation-frame.js +58 -1
- package/dist/heart/provider-attempt.js +4 -1
- package/dist/heart/providers/anthropic.js +23 -2
- package/dist/heart/start-of-turn-packet.js +14 -11
- package/dist/heart/streaming.js +565 -185
- package/dist/mind/obligation-steering.js +15 -9
- package/dist/mind/prompt.js +58 -26
- package/dist/nerves/coverage/run-artifacts.js +55 -35
- package/dist/nerves/review/cli.js +2 -2
- package/dist/nerves/review/core.js +14 -7
- package/dist/repertoire/tools-base.js +2 -0
- package/dist/repertoire/tools-habits.js +103 -0
- package/dist/repertoire/tools.js +18 -3
- package/dist/rsvp/cli.js +126 -20
- package/dist/rsvp/diagnostics.js +12 -2
- package/dist/rsvp/habit-policy.js +7 -0
- package/dist/rsvp/habit-stage.js +19 -4
- package/dist/rsvp/native-habit-runner.js +151 -11
- package/dist/rsvp/outbound-state.js +360 -0
- package/dist/senses/bluebubbles/client.js +90 -17
- package/dist/senses/bluebubbles/inbound-log.js +1 -0
- package/dist/senses/bluebubbles/index.js +662 -425
- package/dist/senses/bluebubbles/model.js +57 -13
- package/dist/senses/bluebubbles/mutation-log.js +21 -10
- package/dist/senses/bluebubbles/reaction-policy.js +55 -0
- package/dist/senses/bluebubbles/semantic-receipts.js +1818 -0
- package/dist/senses/cli/tui-store.js +1 -0
- package/dist/senses/cli.js +1 -0
- package/dist/senses/habit-lifecycle-guard.js +24 -0
- package/dist/senses/ingress-evidence.js +2 -0
- package/dist/senses/pipeline.js +90 -24
- package/dist/senses/private-runtime-worker.js +21 -20
- package/dist/senses/private-runtime.js +79 -29
- package/dist/senses/shared-turn.js +1 -0
- package/dist/senses/teams.js +1 -0
- package/package.json +6 -1
|
@@ -39,6 +39,7 @@ const path = __importStar(require("path"));
|
|
|
39
39
|
const runtime_1 = require("../../nerves/runtime");
|
|
40
40
|
const habit_parser_1 = require("./habit-parser");
|
|
41
41
|
const habit_runtime_state_1 = require("./habit-runtime-state");
|
|
42
|
+
const habit_lifecycle_1 = require("./habit-lifecycle");
|
|
42
43
|
const frontmatter_1 = require("../../util/frontmatter");
|
|
43
44
|
/** Fields that belong to the task system and should be stripped from migrated habits. */
|
|
44
45
|
const TASK_ONLY_FIELDS = new Set([
|
|
@@ -54,15 +55,13 @@ const TASK_ONLY_FIELDS = new Set([
|
|
|
54
55
|
]);
|
|
55
56
|
/** Regex matching the YYYY-MM-DD-HHMM- timestamp prefix in task filenames. */
|
|
56
57
|
const TIMESTAMP_PREFIX = /^\d{4}-\d{2}-\d{2}-\d{4}-/;
|
|
57
|
-
/** Map old task statuses
|
|
58
|
-
function mapStatus(taskStatus) {
|
|
59
|
-
if (taskStatus === "processing")
|
|
60
|
-
return "active";
|
|
61
|
-
if (taskStatus === "paused")
|
|
62
|
-
return "paused";
|
|
58
|
+
/** Map known old task statuses while preserving explicit unknown values for fail-closed parsing. */
|
|
59
|
+
function mapStatus(taskStatus, wasExplicit) {
|
|
60
|
+
if (!wasExplicit || taskStatus === "processing")
|
|
61
|
+
return { skip: false, status: "active" };
|
|
63
62
|
if (taskStatus === "done")
|
|
64
|
-
return
|
|
65
|
-
return
|
|
63
|
+
return { skip: true, status: taskStatus };
|
|
64
|
+
return { skip: false, status: taskStatus };
|
|
66
65
|
}
|
|
67
66
|
/** Strip timestamp prefix from filename to get the slug name. */
|
|
68
67
|
function stripTimestampPrefix(filename) {
|
|
@@ -99,16 +98,6 @@ function migrateHabitsFromTaskSystem(bundleRoot) {
|
|
|
99
98
|
for (const file of mdFiles) {
|
|
100
99
|
const slugName = stripTimestampPrefix(file);
|
|
101
100
|
const targetPath = path.join(newHabitsDir, slugName);
|
|
102
|
-
// Skip if already migrated
|
|
103
|
-
if (fs.existsSync(targetPath)) {
|
|
104
|
-
(0, runtime_1.emitNervesEvent)({
|
|
105
|
-
component: "daemon",
|
|
106
|
-
event: "daemon.habit_migration_skip",
|
|
107
|
-
message: "habit already exists at target, skipping",
|
|
108
|
-
meta: { file, targetPath },
|
|
109
|
-
});
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
101
|
const sourcePath = path.join(oldHabitsDir, file);
|
|
113
102
|
let content;
|
|
114
103
|
try {
|
|
@@ -126,11 +115,14 @@ function migrateHabitsFromTaskSystem(bundleRoot) {
|
|
|
126
115
|
continue;
|
|
127
116
|
const rawFrontmatter = lines.slice(1, closing).join("\n");
|
|
128
117
|
const body = lines.slice(closing + 1).join("\n").trim();
|
|
118
|
+
if (!(0, habit_parser_1.isHabitFrontmatterSyntaxValid)(rawFrontmatter))
|
|
119
|
+
continue;
|
|
129
120
|
const frontmatter = (0, frontmatter_1.parseFrontmatter)(rawFrontmatter);
|
|
130
121
|
// Check status — skip done habits
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
|
|
122
|
+
const hasExplicitStatus = Object.prototype.hasOwnProperty.call(frontmatter, "status");
|
|
123
|
+
const rawStatus = hasExplicitStatus ? frontmatter.status : undefined;
|
|
124
|
+
const mappedStatus = mapStatus(rawStatus, hasExplicitStatus);
|
|
125
|
+
if (mappedStatus.skip) {
|
|
134
126
|
(0, runtime_1.emitNervesEvent)({
|
|
135
127
|
component: "daemon",
|
|
136
128
|
event: "daemon.habit_migration_skip",
|
|
@@ -150,7 +142,7 @@ function migrateHabitsFromTaskSystem(bundleRoot) {
|
|
|
150
142
|
newFrontmatter.title = frontmatter.title;
|
|
151
143
|
if (typeof frontmatter.cadence === "string")
|
|
152
144
|
newFrontmatter.cadence = frontmatter.cadence;
|
|
153
|
-
newFrontmatter.status =
|
|
145
|
+
newFrontmatter.status = mappedStatus.status;
|
|
154
146
|
newFrontmatter.created = typeof frontmatter.created === "string" ? frontmatter.created : "null";
|
|
155
147
|
// Add any other non-task fields from original
|
|
156
148
|
for (const [key, value] of Object.entries(frontmatter)) {
|
|
@@ -166,7 +158,20 @@ function migrateHabitsFromTaskSystem(bundleRoot) {
|
|
|
166
158
|
newFrontmatter[key] = value;
|
|
167
159
|
}
|
|
168
160
|
const rendered = (0, habit_parser_1.renderHabitFile)(newFrontmatter, body);
|
|
169
|
-
|
|
161
|
+
const publication = (0, habit_lifecycle_1.publishNewHabitDefinition)({
|
|
162
|
+
agentRoot: bundleRoot,
|
|
163
|
+
habitId: path.basename(slugName, ".md"),
|
|
164
|
+
bytes: rendered,
|
|
165
|
+
});
|
|
166
|
+
if (publication === "exists") {
|
|
167
|
+
(0, runtime_1.emitNervesEvent)({
|
|
168
|
+
component: "daemon",
|
|
169
|
+
event: "daemon.habit_migration_skip",
|
|
170
|
+
message: "habit already exists at target, skipping",
|
|
171
|
+
meta: { file, targetPath },
|
|
172
|
+
});
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
170
175
|
if (legacyLastRun) {
|
|
171
176
|
(0, habit_runtime_state_1.writeHabitLastRun)(bundleRoot, path.basename(slugName, ".md"), legacyLastRun);
|
|
172
177
|
}
|
|
@@ -33,6 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.isHabitFrontmatterSyntaxValid = isHabitFrontmatterSyntaxValid;
|
|
37
|
+
exports.createDegradedHabitFile = createDegradedHabitFile;
|
|
36
38
|
exports.parseHabitFile = parseHabitFile;
|
|
37
39
|
exports.renderHabitFile = renderHabitFile;
|
|
38
40
|
const path = __importStar(require("path"));
|
|
@@ -40,7 +42,7 @@ const frontmatter_1 = require("../../util/frontmatter");
|
|
|
40
42
|
const runtime_1 = require("../../nerves/runtime");
|
|
41
43
|
const habit_policy_1 = require("../../rsvp/habit-policy");
|
|
42
44
|
function isHabitStatus(value) {
|
|
43
|
-
return value === "active" || value === "paused";
|
|
45
|
+
return value === "active" || value === "paused" || value === "cancelled";
|
|
44
46
|
}
|
|
45
47
|
function parseToolsField(raw) {
|
|
46
48
|
if (raw === undefined || raw === null)
|
|
@@ -110,18 +112,63 @@ function parseContinuity(raw) {
|
|
|
110
112
|
const mode = record ? record.mode : null;
|
|
111
113
|
return { mode: mode === "stateful" ? "stateful" : "fresh" };
|
|
112
114
|
}
|
|
115
|
+
function isHabitFrontmatterSyntaxValid(rawFrontmatter) {
|
|
116
|
+
const lines = rawFrontmatter.split(/\r?\n/);
|
|
117
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
118
|
+
const line = lines[index];
|
|
119
|
+
if (!line.trim())
|
|
120
|
+
continue;
|
|
121
|
+
const match = /^([A-Za-z0-9_:-]+):\s*(.*)$/.exec(line);
|
|
122
|
+
if (!match)
|
|
123
|
+
return false;
|
|
124
|
+
if (match[2].length > 0)
|
|
125
|
+
continue;
|
|
126
|
+
let cursor = index + 1;
|
|
127
|
+
while (cursor < lines.length && /^\s*-\s+/.test(lines[cursor]))
|
|
128
|
+
cursor += 1;
|
|
129
|
+
if (cursor > index + 1) {
|
|
130
|
+
index = cursor - 1;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
while (cursor < lines.length && /^\s+[A-Za-z0-9_:-]+:\s*/.test(lines[cursor]))
|
|
134
|
+
cursor += 1;
|
|
135
|
+
index = cursor - 1;
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
113
139
|
function extractFrontmatterAndBody(content) {
|
|
114
140
|
const lines = content.split(/\r?\n/);
|
|
115
141
|
if (lines[0]?.trim() !== "---") {
|
|
116
|
-
return
|
|
142
|
+
return { kind: "legacy_body", body: content.trim() };
|
|
117
143
|
}
|
|
118
144
|
const closing = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
119
145
|
if (closing === -1) {
|
|
120
|
-
return
|
|
146
|
+
return { kind: "degraded", reason: "unterminated_frontmatter", body: content.trim() };
|
|
121
147
|
}
|
|
122
148
|
const rawFrontmatter = lines.slice(1, closing).join("\n");
|
|
123
149
|
const body = lines.slice(closing + 1).join("\n").trim();
|
|
124
|
-
|
|
150
|
+
if (!isHabitFrontmatterSyntaxValid(rawFrontmatter)) {
|
|
151
|
+
return { kind: "degraded", reason: "malformed_frontmatter", body };
|
|
152
|
+
}
|
|
153
|
+
return { kind: "frontmatter", frontmatter: (0, frontmatter_1.parseFrontmatter)(rawFrontmatter), body };
|
|
154
|
+
}
|
|
155
|
+
function createDegradedHabitFile(filePath, degradedReason, body = "", degradedDetail = null) {
|
|
156
|
+
const stem = path.basename(filePath, ".md");
|
|
157
|
+
return {
|
|
158
|
+
name: stem,
|
|
159
|
+
title: stem,
|
|
160
|
+
cadence: null,
|
|
161
|
+
status: "degraded",
|
|
162
|
+
degradedReason,
|
|
163
|
+
degradedDetail,
|
|
164
|
+
lastRun: null,
|
|
165
|
+
created: null,
|
|
166
|
+
tools: undefined,
|
|
167
|
+
origin: null,
|
|
168
|
+
surface: { family: false, originator: false, extra: [] },
|
|
169
|
+
continuity: { mode: "fresh" },
|
|
170
|
+
body: body.trim(),
|
|
171
|
+
};
|
|
125
172
|
}
|
|
126
173
|
function parseHabitFile(content, filePath) {
|
|
127
174
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -132,7 +179,7 @@ function parseHabitFile(content, filePath) {
|
|
|
132
179
|
});
|
|
133
180
|
const stem = path.basename(filePath, ".md");
|
|
134
181
|
const parsed = extractFrontmatterAndBody(content);
|
|
135
|
-
if (
|
|
182
|
+
if (parsed.kind === "legacy_body") {
|
|
136
183
|
return {
|
|
137
184
|
name: stem,
|
|
138
185
|
title: stem,
|
|
@@ -144,21 +191,38 @@ function parseHabitFile(content, filePath) {
|
|
|
144
191
|
origin: null,
|
|
145
192
|
surface: { family: true, originator: true, extra: [] },
|
|
146
193
|
continuity: { mode: "fresh" },
|
|
147
|
-
body:
|
|
194
|
+
body: parsed.body,
|
|
148
195
|
};
|
|
149
196
|
}
|
|
197
|
+
if (parsed.kind === "degraded") {
|
|
198
|
+
return createDegradedHabitFile(filePath, parsed.reason, parsed.body);
|
|
199
|
+
}
|
|
150
200
|
const { frontmatter, body } = parsed;
|
|
151
201
|
const rawTitle = frontmatter.title;
|
|
152
202
|
const title = typeof rawTitle === "string" && rawTitle.length > 0 ? rawTitle : stem;
|
|
153
203
|
const rawCadence = frontmatter.cadence;
|
|
154
204
|
const cadence = typeof rawCadence === "string" && rawCadence.length > 0 ? rawCadence : null;
|
|
155
205
|
const rawStatus = frontmatter.status;
|
|
156
|
-
const status =
|
|
206
|
+
const status = rawStatus === undefined
|
|
207
|
+
? "active"
|
|
208
|
+
: typeof rawStatus === "string" && isHabitStatus(rawStatus)
|
|
209
|
+
? rawStatus
|
|
210
|
+
: null;
|
|
211
|
+
if (status === null) {
|
|
212
|
+
return createDegradedHabitFile(filePath, "invalid_status", body);
|
|
213
|
+
}
|
|
157
214
|
const rawLastRun = frontmatter.lastRun ?? frontmatter.last_run;
|
|
158
215
|
const lastRun = typeof rawLastRun === "string" && rawLastRun.length > 0 ? rawLastRun : null;
|
|
159
216
|
const rawCreated = frontmatter.created;
|
|
160
217
|
const created = typeof rawCreated === "string" && rawCreated.length > 0 ? rawCreated : null;
|
|
161
|
-
|
|
218
|
+
let rsvp;
|
|
219
|
+
try {
|
|
220
|
+
rsvp = (0, habit_policy_1.parseRsvpHabitMetadata)(frontmatter.rsvp);
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
const degradedDetail = (0, habit_policy_1.rsvpHabitMetadataErrorDetail)(error);
|
|
224
|
+
return createDegradedHabitFile(filePath, "invalid_metadata", body, degradedDetail);
|
|
225
|
+
}
|
|
162
226
|
const tools = rsvp ? [...habit_policy_1.RSVP_HABIT_ALLOWED_TOOLS] : parseToolsField(frontmatter.tools);
|
|
163
227
|
const origin = parseOrigin(frontmatter.origin);
|
|
164
228
|
const surface = parseSurface(frontmatter.surface);
|
|
@@ -37,7 +37,6 @@ exports.readHabitLastRun = readHabitLastRun;
|
|
|
37
37
|
exports.applyHabitRuntimeState = applyHabitRuntimeState;
|
|
38
38
|
exports.writeHabitLastRun = writeHabitLastRun;
|
|
39
39
|
exports.recordHabitRun = recordHabitRun;
|
|
40
|
-
const fs = __importStar(require("fs"));
|
|
41
40
|
const path = __importStar(require("path"));
|
|
42
41
|
const json_store_1 = require("../../arc/json-store");
|
|
43
42
|
const runtime_1 = require("../../nerves/runtime");
|
|
@@ -50,21 +49,6 @@ function isNonEmptyString(value) {
|
|
|
50
49
|
function nullableCursor(value) {
|
|
51
50
|
return isNonEmptyString(value) ? value.trim() : null;
|
|
52
51
|
}
|
|
53
|
-
function stripLegacyLastRunFromDefinition(definitionPath) {
|
|
54
|
-
const content = fs.readFileSync(definitionPath, "utf-8");
|
|
55
|
-
const lines = content.split(/\r?\n/);
|
|
56
|
-
if (lines[0]?.trim() !== "---")
|
|
57
|
-
return;
|
|
58
|
-
const closing = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
59
|
-
if (closing === -1)
|
|
60
|
-
return;
|
|
61
|
-
const frontmatterLines = lines.slice(1, closing);
|
|
62
|
-
const filtered = frontmatterLines.filter((line) => !/^\s*lastRun\s*:/.test(line) && !/^\s*last_run\s*:/.test(line));
|
|
63
|
-
if (filtered.length === frontmatterLines.length)
|
|
64
|
-
return;
|
|
65
|
-
const nextContent = ["---", ...filtered, "---", ...lines.slice(closing + 1)].join("\n");
|
|
66
|
-
fs.writeFileSync(definitionPath, nextContent, "utf-8");
|
|
67
|
-
}
|
|
68
52
|
function readHabitLastRun(agentRoot, habitName) {
|
|
69
53
|
const record = (0, json_store_1.readJsonFile)(habitRuntimeStateDir(agentRoot), habitName);
|
|
70
54
|
return isNonEmptyString(record?.lastRun) ? record.lastRun : null;
|
|
@@ -103,12 +87,7 @@ function writeHabitLastRun(agentRoot, habitName, lastRun, updatedAt = lastRun, o
|
|
|
103
87
|
}
|
|
104
88
|
function recordHabitRun(agentRoot, habitName, lastRun, options = {}) {
|
|
105
89
|
writeHabitLastRun(agentRoot, habitName, lastRun, lastRun, options);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
stripLegacyLastRunFromDefinition(options.definitionPath);
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
// Missing/deleted habit files should never block runtime-state recording.
|
|
113
|
-
}
|
|
90
|
+
// Runtime cursor state is the sole authority for lastRun. Definition files
|
|
91
|
+
// are lifecycle-owned and must never be opportunistically rewritten from a
|
|
92
|
+
// run-completion path, even for legacy-field cleanup.
|
|
114
93
|
}
|