@ouro.bot/cli 0.1.0-alpha.15 → 0.1.0-alpha.151
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 +185 -191
- package/changelog.json +991 -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 +612 -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
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseHabitFile = parseHabitFile;
|
|
37
|
+
exports.renderHabitFile = renderHabitFile;
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const parser_1 = require("../../repertoire/tasks/parser");
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
function isHabitStatus(value) {
|
|
42
|
+
return value === "active" || value === "paused";
|
|
43
|
+
}
|
|
44
|
+
function extractFrontmatterAndBody(content) {
|
|
45
|
+
const lines = content.split(/\r?\n/);
|
|
46
|
+
if (lines[0]?.trim() !== "---") {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const closing = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
50
|
+
if (closing === -1) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const rawFrontmatter = lines.slice(1, closing).join("\n");
|
|
54
|
+
const body = lines.slice(closing + 1).join("\n").trim();
|
|
55
|
+
return { frontmatter: (0, parser_1.parseFrontmatter)(rawFrontmatter), body };
|
|
56
|
+
}
|
|
57
|
+
function parseHabitFile(content, filePath) {
|
|
58
|
+
(0, runtime_1.emitNervesEvent)({
|
|
59
|
+
event: "daemon.habit_parse",
|
|
60
|
+
component: "daemon",
|
|
61
|
+
message: "parsing habit file",
|
|
62
|
+
meta: { filePath },
|
|
63
|
+
});
|
|
64
|
+
const stem = path.basename(filePath, ".md");
|
|
65
|
+
const parsed = extractFrontmatterAndBody(content);
|
|
66
|
+
if (!parsed) {
|
|
67
|
+
return {
|
|
68
|
+
name: stem,
|
|
69
|
+
title: stem,
|
|
70
|
+
cadence: null,
|
|
71
|
+
status: "active",
|
|
72
|
+
lastRun: null,
|
|
73
|
+
created: null,
|
|
74
|
+
body: content.trim(),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const { frontmatter, body } = parsed;
|
|
78
|
+
const rawTitle = frontmatter.title;
|
|
79
|
+
const title = typeof rawTitle === "string" && rawTitle.length > 0 ? rawTitle : stem;
|
|
80
|
+
const rawCadence = frontmatter.cadence;
|
|
81
|
+
const cadence = typeof rawCadence === "string" && rawCadence.length > 0 ? rawCadence : null;
|
|
82
|
+
const rawStatus = frontmatter.status;
|
|
83
|
+
const status = typeof rawStatus === "string" && isHabitStatus(rawStatus) ? rawStatus : "active";
|
|
84
|
+
const rawLastRun = frontmatter.lastRun;
|
|
85
|
+
const lastRun = typeof rawLastRun === "string" && rawLastRun.length > 0 ? rawLastRun : null;
|
|
86
|
+
const rawCreated = frontmatter.created;
|
|
87
|
+
const created = typeof rawCreated === "string" && rawCreated.length > 0 ? rawCreated : null;
|
|
88
|
+
return {
|
|
89
|
+
name: stem,
|
|
90
|
+
title,
|
|
91
|
+
cadence,
|
|
92
|
+
status,
|
|
93
|
+
lastRun,
|
|
94
|
+
created,
|
|
95
|
+
body,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function formatFrontmatterValue(value) {
|
|
99
|
+
if (value === null || value === undefined)
|
|
100
|
+
return "null";
|
|
101
|
+
return String(value);
|
|
102
|
+
}
|
|
103
|
+
function renderHabitFile(frontmatter, body) {
|
|
104
|
+
(0, runtime_1.emitNervesEvent)({
|
|
105
|
+
event: "daemon.habit_render",
|
|
106
|
+
component: "daemon",
|
|
107
|
+
message: "rendering habit file",
|
|
108
|
+
meta: {},
|
|
109
|
+
});
|
|
110
|
+
const lines = ["---"];
|
|
111
|
+
for (const key of Object.keys(frontmatter)) {
|
|
112
|
+
lines.push(`${key}: ${formatFrontmatterValue(frontmatter[key])}`);
|
|
113
|
+
}
|
|
114
|
+
lines.push("---");
|
|
115
|
+
lines.push("");
|
|
116
|
+
lines.push(body.trim());
|
|
117
|
+
lines.push("");
|
|
118
|
+
return lines.join("\n");
|
|
119
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.HabitScheduler = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
39
|
+
const habit_parser_1 = require("./habit-parser");
|
|
40
|
+
const cadence_1 = require("./cadence");
|
|
41
|
+
const WATCH_DEBOUNCE_MS = 200;
|
|
42
|
+
class HabitScheduler {
|
|
43
|
+
agent;
|
|
44
|
+
habitsDir;
|
|
45
|
+
osCronManager;
|
|
46
|
+
onHabitFire;
|
|
47
|
+
deps;
|
|
48
|
+
watcher = null;
|
|
49
|
+
debounceTimer = null;
|
|
50
|
+
parseErrors = [];
|
|
51
|
+
constructor(options) {
|
|
52
|
+
this.agent = options.agent;
|
|
53
|
+
this.habitsDir = options.habitsDir;
|
|
54
|
+
this.osCronManager = options.osCronManager;
|
|
55
|
+
this.onHabitFire = options.onHabitFire;
|
|
56
|
+
this.deps = options.deps;
|
|
57
|
+
}
|
|
58
|
+
start() {
|
|
59
|
+
(0, runtime_1.emitNervesEvent)({
|
|
60
|
+
component: "daemon",
|
|
61
|
+
event: "daemon.habit_scheduler_start",
|
|
62
|
+
message: "habit scheduler starting",
|
|
63
|
+
meta: { agent: this.agent, habitsDir: this.habitsDir },
|
|
64
|
+
});
|
|
65
|
+
const habits = this.scanHabits();
|
|
66
|
+
const jobs = this.buildJobs(habits);
|
|
67
|
+
this.osCronManager.sync(jobs);
|
|
68
|
+
// Fire overdue habits immediately
|
|
69
|
+
for (const habit of habits) {
|
|
70
|
+
if (habit.status !== "active")
|
|
71
|
+
continue;
|
|
72
|
+
if (!habit.cadence)
|
|
73
|
+
continue;
|
|
74
|
+
const cadenceMs = (0, cadence_1.parseCadenceToMs)(habit.cadence);
|
|
75
|
+
if (cadenceMs === null)
|
|
76
|
+
continue;
|
|
77
|
+
const nowMs = this.deps.now();
|
|
78
|
+
if (habit.lastRun === null) {
|
|
79
|
+
(0, runtime_1.emitNervesEvent)({
|
|
80
|
+
component: "daemon",
|
|
81
|
+
event: "daemon.habit_fire",
|
|
82
|
+
message: "firing overdue habit (never run)",
|
|
83
|
+
meta: { habitName: habit.name, agent: this.agent },
|
|
84
|
+
});
|
|
85
|
+
this.onHabitFire(habit.name);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const lastRunMs = new Date(habit.lastRun).getTime();
|
|
89
|
+
const elapsed = nowMs - lastRunMs;
|
|
90
|
+
if (elapsed >= cadenceMs) {
|
|
91
|
+
(0, runtime_1.emitNervesEvent)({
|
|
92
|
+
component: "daemon",
|
|
93
|
+
event: "daemon.habit_fire",
|
|
94
|
+
message: "firing overdue habit",
|
|
95
|
+
meta: { habitName: habit.name, agent: this.agent, elapsedMs: elapsed },
|
|
96
|
+
});
|
|
97
|
+
this.onHabitFire(habit.name);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
reconcile() {
|
|
102
|
+
(0, runtime_1.emitNervesEvent)({
|
|
103
|
+
component: "daemon",
|
|
104
|
+
event: "daemon.habit_scheduler_reconcile",
|
|
105
|
+
message: "habit scheduler reconciling",
|
|
106
|
+
meta: { agent: this.agent },
|
|
107
|
+
});
|
|
108
|
+
const habits = this.scanHabits();
|
|
109
|
+
const jobs = this.buildJobs(habits);
|
|
110
|
+
this.osCronManager.sync(jobs);
|
|
111
|
+
}
|
|
112
|
+
stop() {
|
|
113
|
+
(0, runtime_1.emitNervesEvent)({
|
|
114
|
+
component: "daemon",
|
|
115
|
+
event: "daemon.habit_scheduler_stop",
|
|
116
|
+
message: "habit scheduler stopping",
|
|
117
|
+
meta: { agent: this.agent },
|
|
118
|
+
});
|
|
119
|
+
this.osCronManager.removeAll();
|
|
120
|
+
}
|
|
121
|
+
listOverdueHabits() {
|
|
122
|
+
const habits = this.scanHabits();
|
|
123
|
+
const nowMs = this.deps.now();
|
|
124
|
+
const overdue = [];
|
|
125
|
+
for (const habit of habits) {
|
|
126
|
+
if (habit.status !== "active")
|
|
127
|
+
continue;
|
|
128
|
+
if (!habit.cadence)
|
|
129
|
+
continue;
|
|
130
|
+
const cadenceMs = (0, cadence_1.parseCadenceToMs)(habit.cadence);
|
|
131
|
+
if (cadenceMs === null)
|
|
132
|
+
continue;
|
|
133
|
+
if (habit.lastRun === null) {
|
|
134
|
+
overdue.push({ name: habit.name, elapsedMs: Infinity });
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const lastRunMs = new Date(habit.lastRun).getTime();
|
|
138
|
+
const elapsed = nowMs - lastRunMs;
|
|
139
|
+
if (elapsed >= cadenceMs) {
|
|
140
|
+
overdue.push({ name: habit.name, elapsedMs: elapsed });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return overdue;
|
|
144
|
+
}
|
|
145
|
+
getParseErrors() {
|
|
146
|
+
return [...this.parseErrors];
|
|
147
|
+
}
|
|
148
|
+
getHabitFile(name) {
|
|
149
|
+
const filePath = path.join(this.habitsDir, `${name}.md`);
|
|
150
|
+
try {
|
|
151
|
+
const content = this.deps.readFile(filePath, "utf-8");
|
|
152
|
+
return (0, habit_parser_1.parseHabitFile)(content, filePath);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
watchForChanges() {
|
|
159
|
+
const watchFn = this.deps.watch;
|
|
160
|
+
if (!watchFn)
|
|
161
|
+
return;
|
|
162
|
+
// Ensure habits directory exists before watching — agents may not have one yet
|
|
163
|
+
try {
|
|
164
|
+
this.watcher = watchFn(this.habitsDir, (_event, _filename) => {
|
|
165
|
+
if (this.debounceTimer !== null) {
|
|
166
|
+
clearTimeout(this.debounceTimer);
|
|
167
|
+
}
|
|
168
|
+
this.debounceTimer = setTimeout(() => {
|
|
169
|
+
this.debounceTimer = null;
|
|
170
|
+
this.reconcile();
|
|
171
|
+
}, WATCH_DEBOUNCE_MS);
|
|
172
|
+
});
|
|
173
|
+
/* v8 ignore start — ENOENT catch requires real missing directory @preserve */
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// habits directory may not exist for all agents — skip watching silently
|
|
177
|
+
}
|
|
178
|
+
/* v8 ignore stop */
|
|
179
|
+
}
|
|
180
|
+
stopWatching() {
|
|
181
|
+
if (this.debounceTimer !== null) {
|
|
182
|
+
clearTimeout(this.debounceTimer);
|
|
183
|
+
this.debounceTimer = null;
|
|
184
|
+
}
|
|
185
|
+
if (this.watcher !== null) {
|
|
186
|
+
this.watcher.close();
|
|
187
|
+
this.watcher = null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
scanHabits() {
|
|
191
|
+
let files;
|
|
192
|
+
try {
|
|
193
|
+
files = this.deps.readdir(this.habitsDir);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
this.parseErrors = [];
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
const habits = [];
|
|
200
|
+
const errors = [];
|
|
201
|
+
for (const file of files) {
|
|
202
|
+
if (!file.endsWith(".md"))
|
|
203
|
+
continue;
|
|
204
|
+
const filePath = path.join(this.habitsDir, file);
|
|
205
|
+
try {
|
|
206
|
+
const content = this.deps.readFile(filePath, "utf-8");
|
|
207
|
+
const habit = (0, habit_parser_1.parseHabitFile)(content, filePath);
|
|
208
|
+
habits.push(habit);
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
212
|
+
errors.push({ file, error: errorMessage });
|
|
213
|
+
(0, runtime_1.emitNervesEvent)({
|
|
214
|
+
level: "error",
|
|
215
|
+
component: "daemon",
|
|
216
|
+
event: "daemon.habit_parse_error",
|
|
217
|
+
message: "failed to parse habit file",
|
|
218
|
+
meta: {
|
|
219
|
+
file,
|
|
220
|
+
error: errorMessage,
|
|
221
|
+
agent: this.agent,
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
this.parseErrors = errors;
|
|
227
|
+
return habits;
|
|
228
|
+
}
|
|
229
|
+
buildJobs(habits) {
|
|
230
|
+
const jobs = [];
|
|
231
|
+
for (const habit of habits) {
|
|
232
|
+
if (habit.status !== "active")
|
|
233
|
+
continue;
|
|
234
|
+
if (!habit.cadence)
|
|
235
|
+
continue;
|
|
236
|
+
const cronSchedule = (0, cadence_1.parseCadenceToCron)(habit.cadence);
|
|
237
|
+
if (cronSchedule === null)
|
|
238
|
+
continue;
|
|
239
|
+
jobs.push({
|
|
240
|
+
id: `${this.agent}:${habit.name}:cadence`,
|
|
241
|
+
agent: this.agent,
|
|
242
|
+
taskId: habit.name,
|
|
243
|
+
schedule: cronSchedule,
|
|
244
|
+
lastRun: habit.lastRun,
|
|
245
|
+
command: `${this.deps.ouroPath} poke ${this.agent} --habit ${habit.name}`,
|
|
246
|
+
taskPath: path.join(this.habitsDir, `${habit.name}.md`),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return jobs;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
exports.HabitScheduler = HabitScheduler;
|
|
@@ -20,9 +20,16 @@ async function playHatchAnimation(hatchlingName, writer) {
|
|
|
20
20
|
meta: { hatchlingName },
|
|
21
21
|
});
|
|
22
22
|
const write = writer ?? ((text) => process.stderr.write(text));
|
|
23
|
+
// Total animation time randomized between 3–5 seconds
|
|
24
|
+
const totalMs = 3000 + Math.floor(Math.random() * 2000);
|
|
25
|
+
const eggPhase = Math.floor(totalMs * 0.4);
|
|
26
|
+
const dotsPhase = Math.floor(totalMs * 0.4);
|
|
27
|
+
const revealPause = totalMs - eggPhase - dotsPhase;
|
|
23
28
|
write(`\n ${EGG}`);
|
|
24
|
-
await wait(
|
|
29
|
+
await wait(eggPhase);
|
|
25
30
|
write(DOTS);
|
|
26
|
-
await wait(
|
|
27
|
-
write(`${SNAKE} \x1b[1m${hatchlingName}\x1b[0m
|
|
31
|
+
await wait(dotsPhase);
|
|
32
|
+
write(`${SNAKE} \x1b[1m${hatchlingName}\x1b[0m`);
|
|
33
|
+
await wait(revealPause);
|
|
34
|
+
write("\n\n");
|
|
28
35
|
}
|
|
@@ -39,13 +39,19 @@ const fs = __importStar(require("fs"));
|
|
|
39
39
|
const os = __importStar(require("os"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const identity_1 = require("../identity");
|
|
42
|
+
const config_1 = require("../config");
|
|
42
43
|
const runtime_1 = require("../../nerves/runtime");
|
|
44
|
+
const auth_flow_1 = require("./auth-flow");
|
|
45
|
+
const habit_parser_1 = require("./habit-parser");
|
|
43
46
|
const hatch_specialist_1 = require("./hatch-specialist");
|
|
44
47
|
function requiredCredentialKeys(provider) {
|
|
45
48
|
if (provider === "anthropic")
|
|
46
49
|
return ["setupToken"];
|
|
47
50
|
if (provider === "openai-codex")
|
|
48
51
|
return ["oauthAccessToken"];
|
|
52
|
+
/* v8 ignore next -- branch tested via requiredCredentialKeys unit test @preserve */
|
|
53
|
+
if (provider === "github-copilot")
|
|
54
|
+
return ["githubToken"];
|
|
49
55
|
if (provider === "minimax")
|
|
50
56
|
return ["apiKey"];
|
|
51
57
|
return ["apiKey", "endpoint", "deployment"];
|
|
@@ -66,70 +72,8 @@ function validateCredentials(provider, credentials) {
|
|
|
66
72
|
throw new Error(`Missing required credentials for ${provider}: ${missing.join(", ")}`);
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
|
-
function buildSecretsTemplate() {
|
|
70
|
-
return {
|
|
71
|
-
providers: {
|
|
72
|
-
azure: {
|
|
73
|
-
modelName: "gpt-4o-mini",
|
|
74
|
-
apiKey: "",
|
|
75
|
-
endpoint: "",
|
|
76
|
-
deployment: "",
|
|
77
|
-
apiVersion: "2025-04-01-preview",
|
|
78
|
-
},
|
|
79
|
-
minimax: {
|
|
80
|
-
model: "minimax-text-01",
|
|
81
|
-
apiKey: "",
|
|
82
|
-
},
|
|
83
|
-
anthropic: {
|
|
84
|
-
model: "claude-opus-4-6",
|
|
85
|
-
setupToken: "",
|
|
86
|
-
},
|
|
87
|
-
"openai-codex": {
|
|
88
|
-
model: "gpt-5.2",
|
|
89
|
-
oauthAccessToken: "",
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
teams: {
|
|
93
|
-
clientId: "",
|
|
94
|
-
clientSecret: "",
|
|
95
|
-
tenantId: "",
|
|
96
|
-
},
|
|
97
|
-
oauth: {
|
|
98
|
-
graphConnectionName: "graph",
|
|
99
|
-
adoConnectionName: "ado",
|
|
100
|
-
githubConnectionName: "",
|
|
101
|
-
},
|
|
102
|
-
teamsChannel: {
|
|
103
|
-
skipConfirmation: true,
|
|
104
|
-
port: 3978,
|
|
105
|
-
},
|
|
106
|
-
integrations: {
|
|
107
|
-
perplexityApiKey: "",
|
|
108
|
-
openaiEmbeddingsApiKey: "",
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
75
|
function writeSecretsFile(agentName, provider, credentials, secretsRoot) {
|
|
113
|
-
|
|
114
|
-
if (provider === "anthropic") {
|
|
115
|
-
secrets.providers.anthropic.setupToken = credentials.setupToken.trim();
|
|
116
|
-
}
|
|
117
|
-
else if (provider === "openai-codex") {
|
|
118
|
-
secrets.providers["openai-codex"].oauthAccessToken = credentials.oauthAccessToken.trim();
|
|
119
|
-
}
|
|
120
|
-
else if (provider === "minimax") {
|
|
121
|
-
secrets.providers.minimax.apiKey = credentials.apiKey.trim();
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
secrets.providers.azure.apiKey = credentials.apiKey.trim();
|
|
125
|
-
secrets.providers.azure.endpoint = credentials.endpoint.trim();
|
|
126
|
-
secrets.providers.azure.deployment = credentials.deployment.trim();
|
|
127
|
-
}
|
|
128
|
-
const secretsDir = path.join(secretsRoot, agentName);
|
|
129
|
-
fs.mkdirSync(secretsDir, { recursive: true });
|
|
130
|
-
const secretsPath = path.join(secretsDir, "secrets.json");
|
|
131
|
-
fs.writeFileSync(secretsPath, `${JSON.stringify(secrets, null, 2)}\n`, "utf-8");
|
|
132
|
-
return secretsPath;
|
|
76
|
+
return (0, auth_flow_1.writeProviderCredentials)(agentName, provider, credentials, { secretsRoot }).secretsPath;
|
|
133
77
|
}
|
|
134
78
|
function writeReadme(dir, purpose) {
|
|
135
79
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -138,51 +82,24 @@ function writeReadme(dir, purpose) {
|
|
|
138
82
|
fs.writeFileSync(readmePath, `# ${path.basename(dir)}\n\n${purpose}\n`, "utf-8");
|
|
139
83
|
}
|
|
140
84
|
}
|
|
141
|
-
function
|
|
142
|
-
const
|
|
143
|
-
const slug = trimmed
|
|
144
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
145
|
-
.replace(/^-+/, "")
|
|
146
|
-
.replace(/-+$/, "");
|
|
147
|
-
return slug || "friend";
|
|
148
|
-
}
|
|
149
|
-
function pad(value) {
|
|
150
|
-
return String(value).padStart(2, "0");
|
|
151
|
-
}
|
|
152
|
-
function formatTaskStem(now) {
|
|
153
|
-
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}`;
|
|
154
|
-
}
|
|
155
|
-
function writeHeartbeatTask(bundleRoot, now) {
|
|
156
|
-
const habitsDir = path.join(bundleRoot, "tasks", "habits");
|
|
85
|
+
function writeHeartbeatHabit(bundleRoot, now) {
|
|
86
|
+
const habitsDir = path.join(bundleRoot, "habits");
|
|
157
87
|
fs.mkdirSync(habitsDir, { recursive: true });
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
"
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
"status: processing",
|
|
167
|
-
`created: ${iso}`,
|
|
168
|
-
`updated: ${iso}`,
|
|
169
|
-
"requester: system",
|
|
170
|
-
"validator: null",
|
|
171
|
-
"cadence: \"30m\"",
|
|
172
|
-
"scheduledAt: null",
|
|
173
|
-
"lastRun: null",
|
|
174
|
-
"---",
|
|
175
|
-
"",
|
|
176
|
-
"Run a lightweight heartbeat cycle. Review task board and inbox.",
|
|
177
|
-
"",
|
|
178
|
-
].join("\n");
|
|
88
|
+
const filePath = path.join(habitsDir, "heartbeat.md");
|
|
89
|
+
const content = (0, habit_parser_1.renderHabitFile)({
|
|
90
|
+
title: "Heartbeat check-in",
|
|
91
|
+
cadence: "30m",
|
|
92
|
+
status: "active",
|
|
93
|
+
lastRun: "null",
|
|
94
|
+
created: now.toISOString(),
|
|
95
|
+
}, "Run a lightweight heartbeat cycle. Review task board and inbox.\nCheck on pending obligations. Journal anything important.");
|
|
179
96
|
fs.writeFileSync(filePath, content, "utf-8");
|
|
180
97
|
}
|
|
181
98
|
function writeFriendImprint(bundleRoot, humanName, now) {
|
|
182
99
|
const friendsDir = path.join(bundleRoot, "friends");
|
|
183
100
|
fs.mkdirSync(friendsDir, { recursive: true });
|
|
184
101
|
const nowIso = now.toISOString();
|
|
185
|
-
const id = `friend-${slugify(humanName)}`;
|
|
102
|
+
const id = `friend-${(0, config_1.slugify)(humanName) || "friend"}`;
|
|
186
103
|
const localExternalId = `${os.userInfo().username}@${os.hostname()}`;
|
|
187
104
|
const record = {
|
|
188
105
|
id,
|
|
@@ -207,12 +124,12 @@ function writeFriendImprint(bundleRoot, humanName, now) {
|
|
|
207
124
|
};
|
|
208
125
|
fs.writeFileSync(path.join(friendsDir, `${id}.json`), `${JSON.stringify(record, null, 2)}\n`, "utf-8");
|
|
209
126
|
}
|
|
210
|
-
function
|
|
211
|
-
const
|
|
212
|
-
fs.mkdirSync(path.join(
|
|
213
|
-
fs.mkdirSync(path.join(
|
|
214
|
-
fs.writeFileSync(path.join(
|
|
215
|
-
fs.writeFileSync(path.join(
|
|
127
|
+
function writeDiaryScaffold(bundleRoot) {
|
|
128
|
+
const diaryRoot = path.join(bundleRoot, "diary");
|
|
129
|
+
fs.mkdirSync(path.join(diaryRoot, "daily"), { recursive: true });
|
|
130
|
+
fs.mkdirSync(path.join(diaryRoot, "archive"), { recursive: true });
|
|
131
|
+
fs.writeFileSync(path.join(diaryRoot, "facts.jsonl"), "", "utf-8");
|
|
132
|
+
fs.writeFileSync(path.join(diaryRoot, "entities.json"), "{}\n", "utf-8");
|
|
216
133
|
}
|
|
217
134
|
function writeHatchlingAgentConfig(bundleRoot, input) {
|
|
218
135
|
const template = (0, identity_1.buildDefaultAgentTemplate)(input.agentName);
|
|
@@ -248,19 +165,19 @@ async function runHatchFlow(input, deps = {}) {
|
|
|
248
165
|
fs.mkdirSync(bundleRoot, { recursive: true });
|
|
249
166
|
writeReadme(bundleRoot, "Root of this agent bundle.");
|
|
250
167
|
writeReadme(path.join(bundleRoot, "psyche"), "Identity and behavior files.");
|
|
251
|
-
writeReadme(path.join(bundleRoot, "
|
|
168
|
+
writeReadme(path.join(bundleRoot, "diary"), "Persistent diary — things I've learned and remember.");
|
|
252
169
|
writeReadme(path.join(bundleRoot, "friends"), "Known friend records.");
|
|
253
170
|
writeReadme(path.join(bundleRoot, "tasks"), "Task files.");
|
|
254
|
-
writeReadme(path.join(bundleRoot, "tasks", "habits"), "Recurring tasks.");
|
|
255
171
|
writeReadme(path.join(bundleRoot, "tasks", "one-shots"), "One-shot tasks.");
|
|
256
172
|
writeReadme(path.join(bundleRoot, "tasks", "ongoing"), "Ongoing tasks.");
|
|
173
|
+
writeReadme(path.join(bundleRoot, "habits"), "Recurring habits and autonomous rhythms.");
|
|
257
174
|
writeReadme(path.join(bundleRoot, "skills"), "Local skill files.");
|
|
258
175
|
writeReadme(path.join(bundleRoot, "senses"), "Sense-specific config.");
|
|
259
176
|
writeReadme(path.join(bundleRoot, "senses", "teams"), "Teams sense config.");
|
|
260
177
|
writeHatchlingAgentConfig(bundleRoot, input);
|
|
261
|
-
|
|
178
|
+
writeDiaryScaffold(bundleRoot);
|
|
262
179
|
writeFriendImprint(bundleRoot, input.humanName, now);
|
|
263
|
-
|
|
180
|
+
writeHeartbeatHabit(bundleRoot, now);
|
|
264
181
|
(0, runtime_1.emitNervesEvent)({
|
|
265
182
|
component: "daemon",
|
|
266
183
|
event: "daemon.hatch_flow_end",
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.bundleMetaHook = bundleMetaHook;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const runtime_1 = require("../../../nerves/runtime");
|
|
40
|
+
async function bundleMetaHook(ctx) {
|
|
41
|
+
(0, runtime_1.emitNervesEvent)({
|
|
42
|
+
component: "daemon",
|
|
43
|
+
event: "daemon.bundle_meta_hook_start",
|
|
44
|
+
message: "running bundle-meta update hook",
|
|
45
|
+
meta: { agentRoot: ctx.agentRoot, currentVersion: ctx.currentVersion },
|
|
46
|
+
});
|
|
47
|
+
const metaPath = path.join(ctx.agentRoot, "bundle-meta.json");
|
|
48
|
+
let existing;
|
|
49
|
+
try {
|
|
50
|
+
if (fs.existsSync(metaPath)) {
|
|
51
|
+
const raw = fs.readFileSync(metaPath, "utf-8");
|
|
52
|
+
existing = JSON.parse(raw);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Malformed JSON -- treat as missing, will overwrite with fresh
|
|
57
|
+
existing = undefined;
|
|
58
|
+
}
|
|
59
|
+
const updated = {
|
|
60
|
+
runtimeVersion: ctx.currentVersion,
|
|
61
|
+
bundleSchemaVersion: existing?.bundleSchemaVersion ?? 1,
|
|
62
|
+
lastUpdated: new Date().toISOString(),
|
|
63
|
+
};
|
|
64
|
+
// Save old runtimeVersion as previousRuntimeVersion (if there was one)
|
|
65
|
+
if (existing?.runtimeVersion) {
|
|
66
|
+
updated.previousRuntimeVersion = existing.runtimeVersion;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
fs.writeFileSync(metaPath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
|
|
73
|
+
(0, runtime_1.emitNervesEvent)({
|
|
74
|
+
component: "daemon",
|
|
75
|
+
event: "daemon.bundle_meta_hook_error",
|
|
76
|
+
message: "bundle-meta hook write failed",
|
|
77
|
+
meta: { agentRoot: ctx.agentRoot, error: errorMessage },
|
|
78
|
+
});
|
|
79
|
+
return { ok: false, error: errorMessage };
|
|
80
|
+
}
|
|
81
|
+
(0, runtime_1.emitNervesEvent)({
|
|
82
|
+
component: "daemon",
|
|
83
|
+
event: "daemon.bundle_meta_hook_end",
|
|
84
|
+
message: "bundle-meta updated",
|
|
85
|
+
meta: {
|
|
86
|
+
agentRoot: ctx.agentRoot,
|
|
87
|
+
runtimeVersion: updated.runtimeVersion,
|
|
88
|
+
previousRuntimeVersion: updated.previousRuntimeVersion,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
return { ok: true };
|
|
92
|
+
}
|