@ouro.bot/cli 0.0.1-alpha.0 → 0.1.0-alpha.1
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 +20 -0
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +22 -0
- package/AdoptionSpecialist.ouro/psyche/identities/basilisk.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/jafar.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/jormungandr.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/kaa.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/medusa.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/nagini.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/ouroboros.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/python.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/quetzalcoatl.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/sir-hiss.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/the-serpent.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/the-snake.md +31 -0
- package/README.md +224 -6
- package/dist/heart/agent-entry.js +17 -0
- package/dist/heart/api-error.js +34 -0
- package/dist/heart/config.js +296 -0
- package/dist/heart/core.js +485 -0
- package/dist/heart/daemon/daemon-cli.js +626 -0
- package/dist/heart/daemon/daemon-entry.js +74 -0
- package/dist/heart/daemon/daemon.js +310 -0
- package/dist/heart/daemon/hatch-flow.js +284 -0
- package/dist/heart/daemon/hatch-specialist.js +107 -0
- package/dist/heart/daemon/health-monitor.js +79 -0
- package/dist/heart/daemon/message-router.js +98 -0
- package/dist/heart/daemon/ouro-bot-entry.js +23 -0
- package/dist/heart/daemon/ouro-bot-wrapper.js +90 -0
- package/dist/heart/daemon/ouro-entry.js +23 -0
- package/dist/heart/daemon/ouro-uti.js +212 -0
- package/dist/heart/daemon/process-manager.js +220 -0
- package/dist/heart/daemon/runtime-logging.js +98 -0
- package/dist/heart/daemon/subagent-installer.js +125 -0
- package/dist/heart/daemon/task-scheduler.js +237 -0
- package/dist/heart/harness.js +26 -0
- package/dist/heart/identity.js +270 -0
- package/dist/heart/kicks.js +144 -0
- package/dist/heart/primitives.js +4 -0
- package/dist/heart/providers/anthropic.js +329 -0
- package/dist/heart/providers/azure.js +66 -0
- package/dist/heart/providers/minimax.js +53 -0
- package/dist/heart/providers/openai-codex.js +162 -0
- package/dist/heart/streaming.js +412 -0
- package/dist/heart/turn-coordinator.js +62 -0
- package/dist/inner-worker-entry.js +4 -0
- package/dist/mind/associative-recall.js +176 -0
- package/dist/mind/bundle-manifest.js +118 -0
- package/dist/mind/context.js +218 -0
- package/dist/mind/first-impressions.js +43 -0
- package/dist/mind/format.js +56 -0
- package/dist/mind/friends/channel.js +41 -0
- package/dist/mind/friends/resolver.js +84 -0
- package/dist/mind/friends/store-file.js +171 -0
- package/dist/mind/friends/store.js +4 -0
- package/dist/mind/friends/tokens.js +26 -0
- package/dist/mind/friends/types.js +21 -0
- package/dist/mind/memory.js +326 -0
- package/dist/mind/phrases.js +43 -0
- package/dist/mind/prompt.js +254 -0
- package/dist/mind/token-estimate.js +119 -0
- package/dist/nerves/cli-logging.js +31 -0
- package/dist/nerves/coverage/audit-rules.js +81 -0
- package/dist/nerves/coverage/audit.js +200 -0
- package/dist/nerves/coverage/cli-main.js +5 -0
- package/dist/nerves/coverage/cli.js +51 -0
- package/dist/nerves/coverage/contract.js +23 -0
- package/dist/nerves/coverage/file-completeness.js +46 -0
- package/dist/nerves/coverage/run-artifacts.js +77 -0
- package/dist/nerves/coverage/source-scanner.js +34 -0
- package/dist/nerves/index.js +152 -0
- package/dist/nerves/runtime.js +38 -0
- package/dist/repertoire/ado-client.js +211 -0
- package/dist/repertoire/ado-context.js +73 -0
- package/dist/repertoire/ado-semantic.js +841 -0
- package/dist/repertoire/ado-templates.js +146 -0
- package/dist/repertoire/coding/index.js +36 -0
- package/dist/repertoire/coding/manager.js +489 -0
- package/dist/repertoire/coding/monitor.js +60 -0
- package/dist/repertoire/coding/reporter.js +45 -0
- package/dist/repertoire/coding/spawner.js +102 -0
- package/dist/repertoire/coding/tools.js +167 -0
- package/dist/repertoire/coding/types.js +2 -0
- package/dist/repertoire/data/ado-endpoints.json +122 -0
- package/dist/repertoire/data/graph-endpoints.json +212 -0
- package/dist/repertoire/github-client.js +64 -0
- package/dist/repertoire/graph-client.js +118 -0
- package/dist/repertoire/skills.js +156 -0
- package/dist/repertoire/tasks/board.js +122 -0
- package/dist/repertoire/tasks/index.js +210 -0
- package/dist/repertoire/tasks/lifecycle.js +80 -0
- package/dist/repertoire/tasks/middleware.js +65 -0
- package/dist/repertoire/tasks/parser.js +173 -0
- package/dist/repertoire/tasks/scanner.js +132 -0
- package/dist/repertoire/tasks/transitions.js +145 -0
- package/dist/repertoire/tasks/types.js +2 -0
- package/dist/repertoire/tools-base.js +622 -0
- package/dist/repertoire/tools-github.js +53 -0
- package/dist/repertoire/tools-teams.js +308 -0
- package/dist/repertoire/tools.js +199 -0
- package/dist/senses/cli-entry.js +15 -0
- package/dist/senses/cli.js +523 -0
- package/dist/senses/commands.js +98 -0
- package/dist/senses/inner-dialog-worker.js +61 -0
- package/dist/senses/inner-dialog.js +216 -0
- package/dist/senses/teams-entry.js +15 -0
- package/dist/senses/teams.js +695 -0
- package/dist/senses/trust-gate.js +150 -0
- package/package.json +34 -11
- package/subagents/README.md +71 -0
- package/subagents/work-doer.md +233 -0
- package/subagents/work-merger.md +593 -0
- package/subagents/work-planner.md +373 -0
- package/bin/ouro.js +0 -6
|
@@ -0,0 +1,220 @@
|
|
|
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.DaemonProcessManager = void 0;
|
|
37
|
+
const child_process_1 = require("child_process");
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const identity_1 = require("../identity");
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
function startOfHour(ms) {
|
|
42
|
+
return ms - 60 * 60 * 1000;
|
|
43
|
+
}
|
|
44
|
+
class DaemonProcessManager {
|
|
45
|
+
agents = new Map();
|
|
46
|
+
maxRestartsPerHour;
|
|
47
|
+
stabilityThresholdMs;
|
|
48
|
+
initialBackoffMs;
|
|
49
|
+
maxBackoffMs;
|
|
50
|
+
spawnFn;
|
|
51
|
+
now;
|
|
52
|
+
setTimeoutFn;
|
|
53
|
+
clearTimeoutFn;
|
|
54
|
+
constructor(options) {
|
|
55
|
+
this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
|
|
56
|
+
this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
|
|
57
|
+
this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
|
|
58
|
+
this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
|
|
59
|
+
this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
|
|
60
|
+
this.now = options.now ?? (() => Date.now());
|
|
61
|
+
this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
|
|
62
|
+
this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
|
|
63
|
+
for (const agent of options.agents) {
|
|
64
|
+
this.agents.set(agent.name, {
|
|
65
|
+
config: agent,
|
|
66
|
+
process: null,
|
|
67
|
+
restartTimer: null,
|
|
68
|
+
crashTimestamps: [],
|
|
69
|
+
stopRequested: false,
|
|
70
|
+
snapshot: {
|
|
71
|
+
name: agent.name,
|
|
72
|
+
channel: agent.channel,
|
|
73
|
+
status: "stopped",
|
|
74
|
+
pid: null,
|
|
75
|
+
restartCount: 0,
|
|
76
|
+
startedAt: null,
|
|
77
|
+
lastCrashAt: null,
|
|
78
|
+
backoffMs: this.initialBackoffMs,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async startAutoStartAgents() {
|
|
84
|
+
for (const state of this.agents.values()) {
|
|
85
|
+
if (state.config.autoStart) {
|
|
86
|
+
await this.startAgent(state.config.name);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async startAgent(agent) {
|
|
91
|
+
const state = this.requireAgent(agent);
|
|
92
|
+
if (state.process)
|
|
93
|
+
return;
|
|
94
|
+
this.clearRestartTimer(state);
|
|
95
|
+
state.stopRequested = false;
|
|
96
|
+
state.snapshot.status = "starting";
|
|
97
|
+
const runCwd = (0, identity_1.getRepoRoot)();
|
|
98
|
+
const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
|
|
99
|
+
const args = [entryScript, "--agent", agent, ...(state.config.args ?? [])];
|
|
100
|
+
const child = this.spawnFn("node", args, {
|
|
101
|
+
cwd: runCwd,
|
|
102
|
+
env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
|
|
103
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
104
|
+
});
|
|
105
|
+
state.process = child;
|
|
106
|
+
state.snapshot.status = "running";
|
|
107
|
+
state.snapshot.pid = child.pid ?? null;
|
|
108
|
+
state.snapshot.startedAt = new Date(this.now()).toISOString();
|
|
109
|
+
(0, runtime_1.emitNervesEvent)({
|
|
110
|
+
component: "daemon",
|
|
111
|
+
event: "daemon.agent_started",
|
|
112
|
+
message: "daemon started managed agent process",
|
|
113
|
+
meta: { agent, pid: child.pid ?? null, cwd: runCwd },
|
|
114
|
+
});
|
|
115
|
+
child.once("exit", (code, signal) => {
|
|
116
|
+
this.onExit(state, code, signal);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async stopAgent(agent) {
|
|
120
|
+
const state = this.requireAgent(agent);
|
|
121
|
+
this.clearRestartTimer(state);
|
|
122
|
+
state.stopRequested = true;
|
|
123
|
+
if (!state.process) {
|
|
124
|
+
state.snapshot.status = "stopped";
|
|
125
|
+
state.snapshot.pid = null;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const child = state.process;
|
|
129
|
+
state.process = null;
|
|
130
|
+
state.snapshot.status = "stopped";
|
|
131
|
+
state.snapshot.pid = null;
|
|
132
|
+
try {
|
|
133
|
+
child.kill("SIGTERM");
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
(0, runtime_1.emitNervesEvent)({
|
|
137
|
+
level: "warn",
|
|
138
|
+
component: "daemon",
|
|
139
|
+
event: "daemon.agent_stop_error",
|
|
140
|
+
message: "failed to send SIGTERM to managed agent",
|
|
141
|
+
meta: { agent },
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async restartAgent(agent) {
|
|
146
|
+
await this.stopAgent(agent);
|
|
147
|
+
await this.startAgent(agent);
|
|
148
|
+
}
|
|
149
|
+
async stopAll() {
|
|
150
|
+
for (const state of this.agents.values()) {
|
|
151
|
+
await this.stopAgent(state.config.name);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
getAgentSnapshot(agent) {
|
|
155
|
+
return this.agents.get(agent)?.snapshot;
|
|
156
|
+
}
|
|
157
|
+
listAgentSnapshots() {
|
|
158
|
+
return [...this.agents.values()].map((state) => state.snapshot);
|
|
159
|
+
}
|
|
160
|
+
onExit(state, code, signal) {
|
|
161
|
+
if (!state.process)
|
|
162
|
+
return;
|
|
163
|
+
state.process = null;
|
|
164
|
+
state.snapshot.pid = null;
|
|
165
|
+
const crashed = !state.stopRequested && code !== 0;
|
|
166
|
+
const now = this.now();
|
|
167
|
+
const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
|
|
168
|
+
const runDuration = Math.max(0, now - startedAt);
|
|
169
|
+
(0, runtime_1.emitNervesEvent)({
|
|
170
|
+
level: crashed ? "warn" : "info",
|
|
171
|
+
component: "daemon",
|
|
172
|
+
event: "daemon.agent_exit",
|
|
173
|
+
message: "managed agent process exited",
|
|
174
|
+
meta: { agent: state.config.name, code, signal, crashed, runDurationMs: runDuration },
|
|
175
|
+
});
|
|
176
|
+
if (!crashed) {
|
|
177
|
+
state.snapshot.status = "stopped";
|
|
178
|
+
if (runDuration >= this.stabilityThresholdMs) {
|
|
179
|
+
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
180
|
+
}
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
state.snapshot.lastCrashAt = new Date(now).toISOString();
|
|
184
|
+
state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
|
|
185
|
+
state.crashTimestamps.push(now);
|
|
186
|
+
if (state.crashTimestamps.length > this.maxRestartsPerHour) {
|
|
187
|
+
state.snapshot.status = "crashed";
|
|
188
|
+
(0, runtime_1.emitNervesEvent)({
|
|
189
|
+
level: "error",
|
|
190
|
+
component: "daemon",
|
|
191
|
+
event: "daemon.agent_restart_exhausted",
|
|
192
|
+
message: "managed agent exceeded restart limit and is marked crashed",
|
|
193
|
+
meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
state.snapshot.status = "starting";
|
|
198
|
+
state.snapshot.restartCount += 1;
|
|
199
|
+
const waitMs = state.snapshot.backoffMs;
|
|
200
|
+
state.snapshot.backoffMs = Math.min(state.snapshot.backoffMs * 2, this.maxBackoffMs);
|
|
201
|
+
this.clearRestartTimer(state);
|
|
202
|
+
state.restartTimer = this.setTimeoutFn(() => {
|
|
203
|
+
void this.startAgent(state.config.name);
|
|
204
|
+
}, waitMs);
|
|
205
|
+
}
|
|
206
|
+
clearRestartTimer(state) {
|
|
207
|
+
if (state.restartTimer === null)
|
|
208
|
+
return;
|
|
209
|
+
this.clearTimeoutFn(state.restartTimer);
|
|
210
|
+
state.restartTimer = null;
|
|
211
|
+
}
|
|
212
|
+
requireAgent(agent) {
|
|
213
|
+
const state = this.agents.get(agent);
|
|
214
|
+
if (!state) {
|
|
215
|
+
throw new Error(`Unknown managed agent '${agent}'.`);
|
|
216
|
+
}
|
|
217
|
+
return state;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
exports.DaemonProcessManager = DaemonProcessManager;
|
|
@@ -0,0 +1,98 @@
|
|
|
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.configureDaemonRuntimeLogger = configureDaemonRuntimeLogger;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const nerves_1 = require("../../nerves");
|
|
41
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
42
|
+
const DEFAULT_RUNTIME_LOGGING = {
|
|
43
|
+
level: "info",
|
|
44
|
+
sinks: ["terminal", "ndjson"],
|
|
45
|
+
};
|
|
46
|
+
function isLogLevel(value) {
|
|
47
|
+
return value === "debug" || value === "info" || value === "warn" || value === "error";
|
|
48
|
+
}
|
|
49
|
+
function resolveRuntimeLoggingConfig(configPath) {
|
|
50
|
+
let parsed = null;
|
|
51
|
+
try {
|
|
52
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
53
|
+
parsed = JSON.parse(raw);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return { ...DEFAULT_RUNTIME_LOGGING };
|
|
57
|
+
}
|
|
58
|
+
if (!parsed || typeof parsed !== "object") {
|
|
59
|
+
return { ...DEFAULT_RUNTIME_LOGGING };
|
|
60
|
+
}
|
|
61
|
+
const candidate = parsed;
|
|
62
|
+
const level = isLogLevel(candidate.level) ? candidate.level : DEFAULT_RUNTIME_LOGGING.level;
|
|
63
|
+
const sinks = Array.isArray(candidate.sinks)
|
|
64
|
+
? candidate.sinks.filter((entry) => entry === "terminal" || entry === "ndjson")
|
|
65
|
+
: DEFAULT_RUNTIME_LOGGING.sinks;
|
|
66
|
+
return {
|
|
67
|
+
level,
|
|
68
|
+
sinks: sinks.length > 0 ? [...new Set(sinks)] : [...DEFAULT_RUNTIME_LOGGING.sinks],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function configureDaemonRuntimeLogger(processName, options = {}) {
|
|
72
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
73
|
+
const configPath = options.configPath ?? path.join(homeDir, ".agentstate", "daemon", "logging.json");
|
|
74
|
+
const config = resolveRuntimeLoggingConfig(configPath);
|
|
75
|
+
const sinks = config.sinks.map((sinkName) => {
|
|
76
|
+
if (sinkName === "terminal") {
|
|
77
|
+
return (0, nerves_1.createTerminalSink)();
|
|
78
|
+
}
|
|
79
|
+
const ndjsonPath = path.join(homeDir, ".agentstate", "daemon", "logs", `${processName}.ndjson`);
|
|
80
|
+
return (0, nerves_1.createNdjsonFileSink)(ndjsonPath);
|
|
81
|
+
});
|
|
82
|
+
const logger = (0, nerves_1.createLogger)({
|
|
83
|
+
level: config.level,
|
|
84
|
+
sinks,
|
|
85
|
+
});
|
|
86
|
+
(0, runtime_1.setRuntimeLogger)(logger);
|
|
87
|
+
(0, runtime_1.emitNervesEvent)({
|
|
88
|
+
component: "daemon",
|
|
89
|
+
event: "daemon.runtime_logger_configured",
|
|
90
|
+
message: "configured daemon runtime logger",
|
|
91
|
+
meta: {
|
|
92
|
+
processName,
|
|
93
|
+
level: config.level,
|
|
94
|
+
sinks: config.sinks,
|
|
95
|
+
configPath,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
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.installSubagentsForAvailableCli = installSubagentsForAvailableCli;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
42
|
+
function detectCliBinary(binary) {
|
|
43
|
+
const result = (0, child_process_1.spawnSync)("which", [binary], { encoding: "utf-8" });
|
|
44
|
+
if (result.status !== 0)
|
|
45
|
+
return null;
|
|
46
|
+
const resolved = result.stdout.trim();
|
|
47
|
+
return resolved.length > 0 ? resolved : null;
|
|
48
|
+
}
|
|
49
|
+
function listSubagentSources(subagentsDir) {
|
|
50
|
+
if (!fs.existsSync(subagentsDir))
|
|
51
|
+
return [];
|
|
52
|
+
return fs.readdirSync(subagentsDir)
|
|
53
|
+
.filter((name) => name.endsWith(".md"))
|
|
54
|
+
.filter((name) => name !== "README.md")
|
|
55
|
+
.map((name) => path.join(subagentsDir, name))
|
|
56
|
+
.sort((a, b) => a.localeCompare(b));
|
|
57
|
+
}
|
|
58
|
+
function ensureSymlink(source, target) {
|
|
59
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
60
|
+
if (fs.existsSync(target)) {
|
|
61
|
+
const stats = fs.lstatSync(target);
|
|
62
|
+
if (stats.isSymbolicLink()) {
|
|
63
|
+
const linkedPath = fs.readlinkSync(target);
|
|
64
|
+
if (linkedPath === source)
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
fs.unlinkSync(target);
|
|
68
|
+
}
|
|
69
|
+
fs.symlinkSync(source, target);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
async function installSubagentsForAvailableCli(options = {}) {
|
|
73
|
+
const repoRoot = options.repoRoot ?? path.resolve(__dirname, "..", "..", "..");
|
|
74
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
75
|
+
const which = options.which ?? detectCliBinary;
|
|
76
|
+
const subagentsDir = path.join(repoRoot, "subagents");
|
|
77
|
+
const sources = listSubagentSources(subagentsDir);
|
|
78
|
+
const notes = [];
|
|
79
|
+
(0, runtime_1.emitNervesEvent)({
|
|
80
|
+
component: "daemon",
|
|
81
|
+
event: "daemon.subagent_install_start",
|
|
82
|
+
message: "starting subagent auto-install",
|
|
83
|
+
meta: { sources: sources.length },
|
|
84
|
+
});
|
|
85
|
+
if (sources.length === 0) {
|
|
86
|
+
notes.push(`no subagent files found at ${subagentsDir}`);
|
|
87
|
+
return { claudeInstalled: 0, codexInstalled: 0, notes };
|
|
88
|
+
}
|
|
89
|
+
let claudeInstalled = 0;
|
|
90
|
+
let codexInstalled = 0;
|
|
91
|
+
const claudePath = which("claude");
|
|
92
|
+
if (!claudePath) {
|
|
93
|
+
notes.push("claude CLI not found; skipping subagent install");
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const claudeAgentsDir = path.join(homeDir, ".claude", "agents");
|
|
97
|
+
for (const source of sources) {
|
|
98
|
+
const target = path.join(claudeAgentsDir, path.basename(source));
|
|
99
|
+
if (ensureSymlink(source, target)) {
|
|
100
|
+
claudeInstalled += 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const codexPath = which("codex");
|
|
105
|
+
if (!codexPath) {
|
|
106
|
+
notes.push("codex CLI not found; skipping subagent install");
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
const codexSkillsDir = path.join(homeDir, ".codex", "skills");
|
|
110
|
+
for (const source of sources) {
|
|
111
|
+
const skillName = path.basename(source, ".md");
|
|
112
|
+
const target = path.join(codexSkillsDir, skillName, "SKILL.md");
|
|
113
|
+
if (ensureSymlink(source, target)) {
|
|
114
|
+
codexInstalled += 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
(0, runtime_1.emitNervesEvent)({
|
|
119
|
+
component: "daemon",
|
|
120
|
+
event: "daemon.subagent_install_end",
|
|
121
|
+
message: "completed subagent auto-install",
|
|
122
|
+
meta: { claudeInstalled, codexInstalled, notes: notes.length },
|
|
123
|
+
});
|
|
124
|
+
return { claudeInstalled, codexInstalled, notes };
|
|
125
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
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.TaskDrivenScheduler = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const identity_1 = require("../identity");
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
const parser_1 = require("../../repertoire/tasks/parser");
|
|
42
|
+
function walkMarkdownFiles(root, readdirSync, existsSync, files) {
|
|
43
|
+
if (!existsSync(root))
|
|
44
|
+
return;
|
|
45
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
46
|
+
const fullPath = path.join(root, entry.name);
|
|
47
|
+
if (entry.isDirectory()) {
|
|
48
|
+
walkMarkdownFiles(fullPath, readdirSync, existsSync, files);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (entry.name.endsWith(".md")) {
|
|
52
|
+
files.push(fullPath);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function parseCadence(raw) {
|
|
57
|
+
if (typeof raw !== "string")
|
|
58
|
+
return null;
|
|
59
|
+
const value = raw.trim();
|
|
60
|
+
if (!value)
|
|
61
|
+
return null;
|
|
62
|
+
// Cron format (minute hour day month weekday)
|
|
63
|
+
if (/^\S+\s+\S+\s+\S+\s+\S+\s+\S+$/.test(value)) {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
const cadenceMatch = /^(\d+)(m|h|d)$/.exec(value);
|
|
67
|
+
if (!cadenceMatch)
|
|
68
|
+
return null;
|
|
69
|
+
const interval = Number.parseInt(cadenceMatch[1], 10);
|
|
70
|
+
if (!Number.isFinite(interval) || interval <= 0)
|
|
71
|
+
return null;
|
|
72
|
+
const unit = cadenceMatch[2];
|
|
73
|
+
if (unit === "m")
|
|
74
|
+
return `*/${interval} * * * *`;
|
|
75
|
+
if (unit === "h")
|
|
76
|
+
return `0 */${interval} * * *`;
|
|
77
|
+
return `0 0 */${interval} * *`;
|
|
78
|
+
}
|
|
79
|
+
function parseScheduledAt(raw) {
|
|
80
|
+
if (typeof raw !== "string")
|
|
81
|
+
return null;
|
|
82
|
+
const value = raw.trim();
|
|
83
|
+
if (!value)
|
|
84
|
+
return null;
|
|
85
|
+
const date = new Date(value);
|
|
86
|
+
if (Number.isNaN(date.getTime()))
|
|
87
|
+
return null;
|
|
88
|
+
const minute = date.getUTCMinutes();
|
|
89
|
+
const hour = date.getUTCHours();
|
|
90
|
+
const day = date.getUTCDate();
|
|
91
|
+
const month = date.getUTCMonth() + 1;
|
|
92
|
+
return `${minute} ${hour} ${day} ${month} *`;
|
|
93
|
+
}
|
|
94
|
+
function removeRuntimeFrontmatter(frontmatter) {
|
|
95
|
+
const { _isCanonicalFilename, ...clean } = frontmatter;
|
|
96
|
+
return clean;
|
|
97
|
+
}
|
|
98
|
+
class TaskDrivenScheduler {
|
|
99
|
+
agents;
|
|
100
|
+
bundlesRoot;
|
|
101
|
+
nowIso;
|
|
102
|
+
existsSync;
|
|
103
|
+
readFileSync;
|
|
104
|
+
writeFileSync;
|
|
105
|
+
readdirSync;
|
|
106
|
+
jobs = new Map();
|
|
107
|
+
taskPathByKey = new Map();
|
|
108
|
+
constructor(options) {
|
|
109
|
+
this.agents = [...options.agents];
|
|
110
|
+
this.bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
111
|
+
this.nowIso = options.nowIso ?? (() => new Date().toISOString());
|
|
112
|
+
this.existsSync = options.existsSync ?? fs.existsSync;
|
|
113
|
+
this.readFileSync = options.readFileSync ?? fs.readFileSync;
|
|
114
|
+
this.writeFileSync = options.writeFileSync ?? fs.writeFileSync;
|
|
115
|
+
this.readdirSync = options.readdirSync ?? fs.readdirSync;
|
|
116
|
+
}
|
|
117
|
+
start() {
|
|
118
|
+
void this.reconcile();
|
|
119
|
+
}
|
|
120
|
+
stop() {
|
|
121
|
+
// no long-lived resources; reconciliation is stateless across ticks
|
|
122
|
+
}
|
|
123
|
+
listJobs() {
|
|
124
|
+
return [...this.jobs.values()]
|
|
125
|
+
.map((job) => ({ id: job.id, schedule: job.schedule, lastRun: job.lastRun }))
|
|
126
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
127
|
+
}
|
|
128
|
+
async triggerJob(jobId) {
|
|
129
|
+
const job = this.jobs.get(jobId);
|
|
130
|
+
if (!job) {
|
|
131
|
+
return { ok: false, message: `unknown scheduled job: ${jobId}` };
|
|
132
|
+
}
|
|
133
|
+
await this.recordTaskRun(job.agent, job.taskId);
|
|
134
|
+
return { ok: true, message: `triggered ${jobId}` };
|
|
135
|
+
}
|
|
136
|
+
async reconcile() {
|
|
137
|
+
const nextJobs = new Map();
|
|
138
|
+
const nextTaskPaths = new Map();
|
|
139
|
+
for (const agent of this.agents) {
|
|
140
|
+
const taskRoot = path.join(this.bundlesRoot, `${agent}.ouro`, "tasks");
|
|
141
|
+
const collections = ["one-shots", "ongoing", "habits"];
|
|
142
|
+
const files = [];
|
|
143
|
+
for (const collection of collections) {
|
|
144
|
+
walkMarkdownFiles(path.join(taskRoot, collection), this.readdirSync, this.existsSync, files);
|
|
145
|
+
}
|
|
146
|
+
for (const filePath of files) {
|
|
147
|
+
let task;
|
|
148
|
+
try {
|
|
149
|
+
task = (0, parser_1.parseTaskFile)(this.readFileSync(filePath, "utf-8"), filePath);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const taskId = task.stem;
|
|
155
|
+
nextTaskPaths.set(`${agent}:${taskId}`, filePath);
|
|
156
|
+
if (task.status === "done")
|
|
157
|
+
continue;
|
|
158
|
+
const cadence = parseCadence(task.frontmatter.cadence);
|
|
159
|
+
if (cadence) {
|
|
160
|
+
const id = `${agent}:${taskId}:cadence`;
|
|
161
|
+
nextJobs.set(id, {
|
|
162
|
+
id,
|
|
163
|
+
agent,
|
|
164
|
+
taskId,
|
|
165
|
+
schedule: cadence,
|
|
166
|
+
lastRun: typeof task.frontmatter.lastRun === "string" ? task.frontmatter.lastRun : null,
|
|
167
|
+
command: `ouro poke ${agent} --task ${taskId}`,
|
|
168
|
+
taskPath: filePath,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const scheduledAt = parseScheduledAt(task.frontmatter.scheduledAt);
|
|
172
|
+
if (scheduledAt) {
|
|
173
|
+
const id = `${agent}:${taskId}:scheduledAt`;
|
|
174
|
+
nextJobs.set(id, {
|
|
175
|
+
id,
|
|
176
|
+
agent,
|
|
177
|
+
taskId,
|
|
178
|
+
schedule: scheduledAt,
|
|
179
|
+
lastRun: typeof task.frontmatter.lastRun === "string" ? task.frontmatter.lastRun : null,
|
|
180
|
+
command: `ouro poke ${agent} --task ${taskId}`,
|
|
181
|
+
taskPath: filePath,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
this.jobs.clear();
|
|
187
|
+
for (const [id, job] of nextJobs.entries()) {
|
|
188
|
+
this.jobs.set(id, job);
|
|
189
|
+
}
|
|
190
|
+
this.taskPathByKey.clear();
|
|
191
|
+
for (const [key, filePath] of nextTaskPaths.entries()) {
|
|
192
|
+
this.taskPathByKey.set(key, filePath);
|
|
193
|
+
}
|
|
194
|
+
(0, runtime_1.emitNervesEvent)({
|
|
195
|
+
component: "daemon",
|
|
196
|
+
event: "daemon.scheduler_reconciled",
|
|
197
|
+
message: "reconciled task-driven schedule jobs",
|
|
198
|
+
meta: { jobCount: this.jobs.size, agents: this.agents.length },
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
async recordTaskRun(agent, taskId) {
|
|
202
|
+
const key = `${agent}:${taskId}`;
|
|
203
|
+
let taskPath = this.taskPathByKey.get(key);
|
|
204
|
+
if (!taskPath) {
|
|
205
|
+
await this.reconcile();
|
|
206
|
+
taskPath = this.taskPathByKey.get(key);
|
|
207
|
+
if (!taskPath)
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
let parsed;
|
|
211
|
+
try {
|
|
212
|
+
parsed = (0, parser_1.parseTaskFile)(this.readFileSync(taskPath, "utf-8"), taskPath);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const now = this.nowIso();
|
|
218
|
+
const frontmatter = removeRuntimeFrontmatter(parsed.frontmatter);
|
|
219
|
+
frontmatter.lastRun = now;
|
|
220
|
+
if (typeof frontmatter.updated === "string") {
|
|
221
|
+
frontmatter.updated = now.slice(0, 10);
|
|
222
|
+
}
|
|
223
|
+
this.writeFileSync(taskPath, (0, parser_1.renderTaskFile)(frontmatter, parsed.body), "utf-8");
|
|
224
|
+
for (const job of this.jobs.values()) {
|
|
225
|
+
if (job.agent === agent && job.taskId === taskId) {
|
|
226
|
+
job.lastRun = now;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
(0, runtime_1.emitNervesEvent)({
|
|
230
|
+
component: "daemon",
|
|
231
|
+
event: "daemon.scheduler_task_run_recorded",
|
|
232
|
+
message: "recorded scheduled task run",
|
|
233
|
+
meta: { agent, taskId, at: now },
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
exports.TaskDrivenScheduler = TaskDrivenScheduler;
|