nemoris 0.1.0 → 0.1.2
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/.env.example +49 -49
- package/LICENSE +21 -21
- package/README.md +209 -209
- package/SECURITY.md +59 -119
- package/bin/nemoris +46 -46
- package/config/agents/agent.toml.example +28 -28
- package/config/agents/content.toml +23 -0
- package/config/agents/default.toml +22 -22
- package/config/agents/heartbeat.toml +35 -0
- package/config/agents/iris.toml +23 -0
- package/config/agents/lab.toml +23 -0
- package/config/agents/main.toml +45 -0
- package/config/agents/nemo.toml +21 -0
- package/config/agents/ops.toml +38 -0
- package/config/agents/orchestrator.toml +18 -18
- package/config/agents/revenue.toml +23 -0
- package/config/agents/testyboo.toml +19 -0
- package/config/delivery.toml +73 -73
- package/config/embeddings.toml +5 -5
- package/config/identity/content-purpose.md +11 -0
- package/config/identity/content-soul.md +45 -0
- package/config/identity/default-purpose.md +1 -1
- package/config/identity/default-soul.md +3 -3
- package/config/identity/heartbeat-purpose.md +9 -0
- package/config/identity/heartbeat-soul.md +16 -0
- package/config/identity/iris-purpose.md +17 -0
- package/config/identity/iris-soul.md +68 -0
- package/config/identity/lab-purpose.md +10 -0
- package/config/identity/lab-soul.md +38 -0
- package/config/identity/main-purpose.md +17 -0
- package/config/identity/main-soul.md +66 -0
- package/config/identity/main-user.md +22 -0
- package/config/identity/ops-purpose.md +9 -0
- package/config/identity/ops-soul.md +16 -0
- package/config/identity/orchestrator-purpose.md +1 -1
- package/config/identity/orchestrator-soul.md +1 -1
- package/config/identity/revenue-purpose.md +9 -0
- package/config/identity/revenue-soul.md +41 -0
- package/config/identity/testyboo-purpose.md +13 -0
- package/config/identity/testyboo-soul.md +20 -0
- package/config/improvement-targets.toml +15 -15
- package/config/jobs/heartbeat-check.toml +30 -30
- package/config/jobs/memory-rollup.toml +46 -46
- package/config/jobs/workspace-health.toml +63 -63
- package/config/mcp.toml +16 -16
- package/config/output-contracts.toml +17 -17
- package/config/peers.toml +32 -32
- package/config/peers.toml.example +32 -32
- package/config/policies/memory-default.toml +10 -10
- package/config/policies/memory-heartbeat.toml +5 -5
- package/config/policies/memory-ops.toml +10 -10
- package/config/policies/tools-heartbeat-minimal.toml +8 -8
- package/config/policies/tools-interactive-safe.toml +8 -8
- package/config/policies/tools-ops-bounded.toml +8 -8
- package/config/policies/tools-orchestrator.toml +7 -7
- package/config/providers/anthropic.toml +15 -15
- package/config/providers/ollama.toml +5 -5
- package/config/providers/openai-codex.toml +9 -9
- package/config/providers/openrouter.toml +5 -5
- package/config/router.toml +22 -22
- package/config/runtime.toml +114 -114
- package/config/skills/self-improvement.toml +15 -15
- package/config/skills/telegram-onboarding-spec.md +240 -240
- package/config/skills/workspace-monitor.toml +15 -15
- package/config/task-router.toml +42 -42
- package/install.sh +50 -50
- package/package.json +91 -90
- package/src/auth/auth-profiles.js +169 -169
- package/src/auth/openai-codex-oauth.js +285 -285
- package/src/battle.js +449 -449
- package/src/cli/help.js +265 -265
- package/src/cli/output-filter.js +49 -49
- package/src/cli/runtime-control.js +704 -704
- package/src/cli-main.js +2763 -2763
- package/src/cli.js +78 -78
- package/src/config/loader.js +332 -332
- package/src/config/schema-validator.js +214 -214
- package/src/config/toml-lite.js +8 -8
- package/src/daemon/action-handlers.js +71 -71
- package/src/daemon/healing-tick.js +87 -87
- package/src/daemon/health-probes.js +90 -90
- package/src/daemon/notifier.js +57 -57
- package/src/daemon/nurse.js +218 -218
- package/src/daemon/repair-log.js +106 -106
- package/src/daemon/rule-staging.js +90 -90
- package/src/daemon/rules.js +29 -29
- package/src/daemon/telegram-commands.js +54 -54
- package/src/daemon/updater.js +85 -85
- package/src/jobs/job-runner.js +78 -78
- package/src/mcp/consumer.js +129 -129
- package/src/memory/active-recall.js +171 -171
- package/src/memory/backend-manager.js +97 -97
- package/src/memory/backends/file-backend.js +38 -38
- package/src/memory/backends/qmd-backend.js +219 -219
- package/src/memory/embedding-guards.js +24 -24
- package/src/memory/embedding-index.js +118 -118
- package/src/memory/embedding-service.js +179 -179
- package/src/memory/file-index.js +177 -177
- package/src/memory/memory-signature.js +5 -5
- package/src/memory/memory-store.js +648 -648
- package/src/memory/retrieval-planner.js +66 -66
- package/src/memory/scoring.js +145 -145
- package/src/memory/simhash.js +78 -78
- package/src/memory/sqlite-active-store.js +824 -824
- package/src/memory/write-policy.js +36 -36
- package/src/onboarding/aliases.js +33 -33
- package/src/onboarding/auth/api-key.js +224 -224
- package/src/onboarding/auth/ollama-detect.js +42 -42
- package/src/onboarding/clack-prompter.js +77 -77
- package/src/onboarding/doctor.js +530 -530
- package/src/onboarding/lock.js +42 -42
- package/src/onboarding/model-catalog.js +344 -344
- package/src/onboarding/phases/auth.js +576 -589
- package/src/onboarding/phases/build.js +130 -130
- package/src/onboarding/phases/choose.js +82 -82
- package/src/onboarding/phases/detect.js +98 -98
- package/src/onboarding/phases/hatch.js +216 -216
- package/src/onboarding/phases/identity.js +79 -79
- package/src/onboarding/phases/ollama.js +345 -345
- package/src/onboarding/phases/scaffold.js +99 -99
- package/src/onboarding/phases/telegram.js +377 -377
- package/src/onboarding/phases/validate.js +204 -204
- package/src/onboarding/phases/verify.js +206 -206
- package/src/onboarding/platform.js +482 -482
- package/src/onboarding/status-bar.js +95 -95
- package/src/onboarding/templates.js +794 -794
- package/src/onboarding/toml-writer.js +38 -38
- package/src/onboarding/tui.js +250 -250
- package/src/onboarding/uninstall.js +153 -153
- package/src/onboarding/wizard.js +516 -499
- package/src/providers/anthropic.js +168 -168
- package/src/providers/base.js +247 -247
- package/src/providers/circuit-breaker.js +136 -136
- package/src/providers/ollama.js +163 -163
- package/src/providers/openai-codex.js +149 -149
- package/src/providers/openrouter.js +136 -136
- package/src/providers/registry.js +36 -36
- package/src/providers/router.js +16 -16
- package/src/runtime/bootstrap-cache.js +47 -47
- package/src/runtime/capabilities-prompt.js +25 -25
- package/src/runtime/completion-ping.js +99 -99
- package/src/runtime/config-validator.js +121 -121
- package/src/runtime/context-ledger.js +360 -360
- package/src/runtime/cutover-readiness.js +42 -42
- package/src/runtime/daemon.js +729 -729
- package/src/runtime/delivery-ack.js +195 -195
- package/src/runtime/delivery-adapters/local-file.js +41 -41
- package/src/runtime/delivery-adapters/openclaw-cli.js +94 -94
- package/src/runtime/delivery-adapters/openclaw-peer.js +98 -98
- package/src/runtime/delivery-adapters/shadow.js +13 -13
- package/src/runtime/delivery-adapters/standalone-http.js +98 -98
- package/src/runtime/delivery-adapters/telegram.js +104 -104
- package/src/runtime/delivery-adapters/tui.js +128 -128
- package/src/runtime/delivery-manager.js +807 -807
- package/src/runtime/delivery-store.js +168 -168
- package/src/runtime/dependency-health.js +118 -118
- package/src/runtime/envelope.js +114 -114
- package/src/runtime/evaluation.js +1089 -1089
- package/src/runtime/exec-approvals.js +216 -216
- package/src/runtime/executor.js +500 -500
- package/src/runtime/failure-ping.js +67 -67
- package/src/runtime/flows.js +83 -83
- package/src/runtime/guards.js +45 -45
- package/src/runtime/handoff.js +51 -51
- package/src/runtime/identity-cache.js +28 -28
- package/src/runtime/improvement-engine.js +109 -109
- package/src/runtime/improvement-harness.js +581 -581
- package/src/runtime/input-sanitiser.js +72 -72
- package/src/runtime/interaction-contract.js +347 -347
- package/src/runtime/lane-readiness.js +226 -226
- package/src/runtime/migration.js +323 -323
- package/src/runtime/model-resolution.js +78 -78
- package/src/runtime/network.js +64 -64
- package/src/runtime/notification-store.js +97 -97
- package/src/runtime/notifier.js +256 -256
- package/src/runtime/orchestrator.js +53 -53
- package/src/runtime/orphan-reaper.js +41 -41
- package/src/runtime/output-contract-schema.js +139 -139
- package/src/runtime/output-contract-validator.js +439 -439
- package/src/runtime/peer-readiness.js +69 -69
- package/src/runtime/peer-registry.js +133 -133
- package/src/runtime/pilot-status.js +108 -108
- package/src/runtime/prompt-builder.js +261 -261
- package/src/runtime/provider-attempt.js +582 -582
- package/src/runtime/report-fallback.js +71 -71
- package/src/runtime/result-normalizer.js +183 -183
- package/src/runtime/retention.js +74 -74
- package/src/runtime/review.js +244 -244
- package/src/runtime/route-job.js +15 -15
- package/src/runtime/run-store.js +38 -38
- package/src/runtime/schedule.js +88 -88
- package/src/runtime/scheduler-state.js +434 -434
- package/src/runtime/scheduler.js +656 -656
- package/src/runtime/session-compactor.js +182 -182
- package/src/runtime/session-search.js +155 -155
- package/src/runtime/slack-inbound.js +249 -249
- package/src/runtime/ssrf.js +102 -102
- package/src/runtime/status-aggregator.js +330 -330
- package/src/runtime/task-contract.js +140 -140
- package/src/runtime/task-packet.js +107 -107
- package/src/runtime/task-router.js +140 -140
- package/src/runtime/telegram-inbound.js +1565 -1565
- package/src/runtime/token-counter.js +134 -134
- package/src/runtime/token-estimator.js +59 -59
- package/src/runtime/tool-loop.js +200 -200
- package/src/runtime/transport-server.js +311 -311
- package/src/runtime/tui-server.js +411 -411
- package/src/runtime/ulid.js +44 -44
- package/src/security/ssrf-check.js +197 -197
- package/src/setup.js +369 -369
- package/src/shadow/bridge.js +303 -303
- package/src/skills/loader.js +84 -84
- package/src/tools/catalog.json +49 -49
- package/src/tools/cli-delegate.js +44 -44
- package/src/tools/mcp-client.js +106 -106
- package/src/tools/micro/cancel-task.js +6 -6
- package/src/tools/micro/complete-task.js +6 -6
- package/src/tools/micro/fail-task.js +6 -6
- package/src/tools/micro/http-fetch.js +74 -74
- package/src/tools/micro/index.js +36 -36
- package/src/tools/micro/lcm-recall.js +60 -60
- package/src/tools/micro/list-dir.js +17 -17
- package/src/tools/micro/list-skills.js +46 -46
- package/src/tools/micro/load-skill.js +38 -38
- package/src/tools/micro/memory-search.js +45 -45
- package/src/tools/micro/read-file.js +11 -11
- package/src/tools/micro/session-search.js +54 -54
- package/src/tools/micro/shell-exec.js +43 -43
- package/src/tools/micro/trigger-job.js +79 -79
- package/src/tools/micro/web-search.js +58 -58
- package/src/tools/micro/workspace-paths.js +39 -39
- package/src/tools/micro/write-file.js +14 -14
- package/src/tools/micro/write-memory.js +41 -41
- package/src/tools/registry.js +348 -348
- package/src/tools/tool-result-contract.js +36 -36
- package/src/tui/chat.js +835 -835
- package/src/tui/renderer.js +175 -175
- package/src/tui/socket-client.js +217 -217
- package/src/utils/canonical-json.js +29 -29
- package/src/utils/compaction.js +30 -30
- package/src/utils/env-loader.js +5 -5
- package/src/utils/errors.js +80 -80
- package/src/utils/fs.js +101 -101
- package/src/utils/ids.js +5 -5
- package/src/utils/model-context-limits.js +30 -30
- package/src/utils/token-budget.js +74 -74
- package/src/utils/usage-cost.js +25 -25
- package/src/utils/usage-metrics.js +14 -14
|
@@ -1,140 +1,140 @@
|
|
|
1
|
-
import { ulid } from "./ulid.js";
|
|
2
|
-
|
|
3
|
-
const VALID_TRANSITIONS = {
|
|
4
|
-
created: ["accepted", "rejected"],
|
|
5
|
-
accepted: ["running", "rejected"],
|
|
6
|
-
running: ["completed", "failed", "cancelled", "timed_out"],
|
|
7
|
-
timed_out: ["escalated"],
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export class TaskContract {
|
|
11
|
-
constructor(db, envelopeStore) {
|
|
12
|
-
this.db = db;
|
|
13
|
-
this.envelopes = envelopeStore;
|
|
14
|
-
this._ensureSchema();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
_ensureSchema() {
|
|
18
|
-
this.db.exec(`
|
|
19
|
-
CREATE TABLE IF NOT EXISTS tasks (
|
|
20
|
-
task_id TEXT PRIMARY KEY,
|
|
21
|
-
envelope_id TEXT NOT NULL,
|
|
22
|
-
owner_agent TEXT NOT NULL,
|
|
23
|
-
assigned_agent TEXT NOT NULL,
|
|
24
|
-
status TEXT NOT NULL DEFAULT 'created',
|
|
25
|
-
objective TEXT NOT NULL,
|
|
26
|
-
deadline_at TEXT NOT NULL,
|
|
27
|
-
result_slot TEXT,
|
|
28
|
-
error_slot TEXT,
|
|
29
|
-
escalation_chain TEXT NOT NULL,
|
|
30
|
-
escalation_index INTEGER NOT NULL DEFAULT 0,
|
|
31
|
-
created_at TEXT NOT NULL,
|
|
32
|
-
updated_at TEXT NOT NULL,
|
|
33
|
-
FOREIGN KEY (envelope_id) REFERENCES envelopes(id)
|
|
34
|
-
);
|
|
35
|
-
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
|
|
36
|
-
CREATE INDEX IF NOT EXISTS idx_tasks_assigned ON tasks(assigned_agent);
|
|
37
|
-
CREATE INDEX IF NOT EXISTS idx_tasks_deadline ON tasks(deadline_at);
|
|
38
|
-
`);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
createTask({ ownerAgent, assignedAgent, objective, deadlineMinutes, escalationChain = ["assigned", "owner", "operator"] }) {
|
|
42
|
-
const taskId = ulid();
|
|
43
|
-
const now = new Date();
|
|
44
|
-
const deadlineAt = new Date(now.getTime() + deadlineMinutes * 60 * 1000).toISOString();
|
|
45
|
-
const envelope = this.envelopes.create({
|
|
46
|
-
sourceAgent: ownerAgent,
|
|
47
|
-
criticality: "result",
|
|
48
|
-
payloadType: "task",
|
|
49
|
-
payload: { objective, assignedAgent, deadlineMinutes },
|
|
50
|
-
});
|
|
51
|
-
this.db.prepare(`
|
|
52
|
-
INSERT INTO tasks (task_id, envelope_id, owner_agent, assigned_agent, status, objective, deadline_at, escalation_chain, escalation_index, created_at, updated_at)
|
|
53
|
-
VALUES (?, ?, ?, ?, 'created', ?, ?, ?, 0, ?, ?)
|
|
54
|
-
`).run(taskId, envelope.id, ownerAgent, assignedAgent, objective.slice(0, 500), deadlineAt, JSON.stringify(escalationChain), now.toISOString(), now.toISOString());
|
|
55
|
-
return this.get(taskId);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
get(taskId) {
|
|
59
|
-
return this.db.prepare("SELECT * FROM tasks WHERE task_id = ?").get(taskId) || null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
transition(taskId, newStatus, callerAgent, { result, error } = {}) {
|
|
63
|
-
const task = this.get(taskId);
|
|
64
|
-
if (!task) throw new Error(`Task ${taskId} not found`);
|
|
65
|
-
const allowed = VALID_TRANSITIONS[task.status];
|
|
66
|
-
if (!allowed || !allowed.includes(newStatus)) {
|
|
67
|
-
throw new Error(`Invalid transition: ${task.status} → ${newStatus}`);
|
|
68
|
-
}
|
|
69
|
-
// Permission checks
|
|
70
|
-
if (newStatus === "cancelled" && callerAgent !== task.owner_agent) {
|
|
71
|
-
throw new Error("Only owner can cancel a task");
|
|
72
|
-
}
|
|
73
|
-
const assignedOnly = ["accepted", "running", "completed", "failed", "rejected"];
|
|
74
|
-
if (assignedOnly.includes(newStatus) && callerAgent !== task.assigned_agent) {
|
|
75
|
-
throw new Error(`Only assigned agent can transition to ${newStatus}`);
|
|
76
|
-
}
|
|
77
|
-
const now = new Date().toISOString();
|
|
78
|
-
const updates = { status: newStatus, updated_at: now };
|
|
79
|
-
if (result !== undefined) {
|
|
80
|
-
updates.result_slot = typeof result === "string" ? result : JSON.stringify(result);
|
|
81
|
-
if (updates.result_slot.length > 4000) {
|
|
82
|
-
updates.result_slot = JSON.stringify({
|
|
83
|
-
ledger_ref: `overflow-${taskId}`,
|
|
84
|
-
summary: updates.result_slot.slice(0, 200),
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if (error !== undefined) {
|
|
89
|
-
updates.error_slot = typeof error === "string" ? error : JSON.stringify(error);
|
|
90
|
-
}
|
|
91
|
-
const setClauses = Object.keys(updates).map((k) => `${k} = ?`).join(", ");
|
|
92
|
-
const values = [...Object.values(updates), taskId];
|
|
93
|
-
this.db.prepare(`UPDATE tasks SET ${setClauses} WHERE task_id = ?`).run(...values);
|
|
94
|
-
return this.get(taskId);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
listByStatus(status) {
|
|
98
|
-
return this.db.prepare("SELECT * FROM tasks WHERE status = ? ORDER BY created_at DESC").all(status);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
listTimedOut() {
|
|
102
|
-
const now = new Date().toISOString();
|
|
103
|
-
return this.db.prepare("SELECT * FROM tasks WHERE status = 'running' AND deadline_at < ?").all(now);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
sweepTimedOutTasks() {
|
|
107
|
-
const now = new Date().toISOString();
|
|
108
|
-
const overdue = this.db.prepare("SELECT * FROM tasks WHERE status = 'running' AND deadline_at < ?").all(now);
|
|
109
|
-
for (const task of overdue) {
|
|
110
|
-
this.db.prepare("UPDATE tasks SET status = 'timed_out', updated_at = ? WHERE task_id = ?").run(now, task.task_id);
|
|
111
|
-
}
|
|
112
|
-
return overdue;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
advanceEscalation(taskId) {
|
|
116
|
-
const task = this.get(taskId);
|
|
117
|
-
if (!task) throw new Error(`Task ${taskId} not found`);
|
|
118
|
-
if (!["timed_out", "escalated"].includes(task.status)) {
|
|
119
|
-
throw new Error(`Cannot escalate task in status: ${task.status}`);
|
|
120
|
-
}
|
|
121
|
-
const chain = JSON.parse(task.escalation_chain);
|
|
122
|
-
const newIndex = task.escalation_index + 1;
|
|
123
|
-
this.db.prepare("UPDATE tasks SET escalation_index = ?, updated_at = ? WHERE task_id = ?")
|
|
124
|
-
.run(newIndex, new Date().toISOString(), taskId);
|
|
125
|
-
if (newIndex >= chain.length) {
|
|
126
|
-
this.db.prepare("UPDATE tasks SET status = 'escalated', updated_at = ? WHERE task_id = ?")
|
|
127
|
-
.run(new Date().toISOString(), taskId);
|
|
128
|
-
}
|
|
129
|
-
return { escalationIndex: newIndex, target: chain[newIndex] || null, exhausted: newIndex >= chain.length };
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
getInbox() {
|
|
133
|
-
return this.db.prepare(`
|
|
134
|
-
SELECT * FROM tasks
|
|
135
|
-
WHERE status IN ('timed_out', 'escalated')
|
|
136
|
-
AND escalation_index >= json_array_length(escalation_chain)
|
|
137
|
-
ORDER BY created_at DESC
|
|
138
|
-
`).all();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
1
|
+
import { ulid } from "./ulid.js";
|
|
2
|
+
|
|
3
|
+
const VALID_TRANSITIONS = {
|
|
4
|
+
created: ["accepted", "rejected"],
|
|
5
|
+
accepted: ["running", "rejected"],
|
|
6
|
+
running: ["completed", "failed", "cancelled", "timed_out"],
|
|
7
|
+
timed_out: ["escalated"],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export class TaskContract {
|
|
11
|
+
constructor(db, envelopeStore) {
|
|
12
|
+
this.db = db;
|
|
13
|
+
this.envelopes = envelopeStore;
|
|
14
|
+
this._ensureSchema();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_ensureSchema() {
|
|
18
|
+
this.db.exec(`
|
|
19
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
20
|
+
task_id TEXT PRIMARY KEY,
|
|
21
|
+
envelope_id TEXT NOT NULL,
|
|
22
|
+
owner_agent TEXT NOT NULL,
|
|
23
|
+
assigned_agent TEXT NOT NULL,
|
|
24
|
+
status TEXT NOT NULL DEFAULT 'created',
|
|
25
|
+
objective TEXT NOT NULL,
|
|
26
|
+
deadline_at TEXT NOT NULL,
|
|
27
|
+
result_slot TEXT,
|
|
28
|
+
error_slot TEXT,
|
|
29
|
+
escalation_chain TEXT NOT NULL,
|
|
30
|
+
escalation_index INTEGER NOT NULL DEFAULT 0,
|
|
31
|
+
created_at TEXT NOT NULL,
|
|
32
|
+
updated_at TEXT NOT NULL,
|
|
33
|
+
FOREIGN KEY (envelope_id) REFERENCES envelopes(id)
|
|
34
|
+
);
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_assigned ON tasks(assigned_agent);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_deadline ON tasks(deadline_at);
|
|
38
|
+
`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
createTask({ ownerAgent, assignedAgent, objective, deadlineMinutes, escalationChain = ["assigned", "owner", "operator"] }) {
|
|
42
|
+
const taskId = ulid();
|
|
43
|
+
const now = new Date();
|
|
44
|
+
const deadlineAt = new Date(now.getTime() + deadlineMinutes * 60 * 1000).toISOString();
|
|
45
|
+
const envelope = this.envelopes.create({
|
|
46
|
+
sourceAgent: ownerAgent,
|
|
47
|
+
criticality: "result",
|
|
48
|
+
payloadType: "task",
|
|
49
|
+
payload: { objective, assignedAgent, deadlineMinutes },
|
|
50
|
+
});
|
|
51
|
+
this.db.prepare(`
|
|
52
|
+
INSERT INTO tasks (task_id, envelope_id, owner_agent, assigned_agent, status, objective, deadline_at, escalation_chain, escalation_index, created_at, updated_at)
|
|
53
|
+
VALUES (?, ?, ?, ?, 'created', ?, ?, ?, 0, ?, ?)
|
|
54
|
+
`).run(taskId, envelope.id, ownerAgent, assignedAgent, objective.slice(0, 500), deadlineAt, JSON.stringify(escalationChain), now.toISOString(), now.toISOString());
|
|
55
|
+
return this.get(taskId);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get(taskId) {
|
|
59
|
+
return this.db.prepare("SELECT * FROM tasks WHERE task_id = ?").get(taskId) || null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
transition(taskId, newStatus, callerAgent, { result, error } = {}) {
|
|
63
|
+
const task = this.get(taskId);
|
|
64
|
+
if (!task) throw new Error(`Task ${taskId} not found`);
|
|
65
|
+
const allowed = VALID_TRANSITIONS[task.status];
|
|
66
|
+
if (!allowed || !allowed.includes(newStatus)) {
|
|
67
|
+
throw new Error(`Invalid transition: ${task.status} → ${newStatus}`);
|
|
68
|
+
}
|
|
69
|
+
// Permission checks
|
|
70
|
+
if (newStatus === "cancelled" && callerAgent !== task.owner_agent) {
|
|
71
|
+
throw new Error("Only owner can cancel a task");
|
|
72
|
+
}
|
|
73
|
+
const assignedOnly = ["accepted", "running", "completed", "failed", "rejected"];
|
|
74
|
+
if (assignedOnly.includes(newStatus) && callerAgent !== task.assigned_agent) {
|
|
75
|
+
throw new Error(`Only assigned agent can transition to ${newStatus}`);
|
|
76
|
+
}
|
|
77
|
+
const now = new Date().toISOString();
|
|
78
|
+
const updates = { status: newStatus, updated_at: now };
|
|
79
|
+
if (result !== undefined) {
|
|
80
|
+
updates.result_slot = typeof result === "string" ? result : JSON.stringify(result);
|
|
81
|
+
if (updates.result_slot.length > 4000) {
|
|
82
|
+
updates.result_slot = JSON.stringify({
|
|
83
|
+
ledger_ref: `overflow-${taskId}`,
|
|
84
|
+
summary: updates.result_slot.slice(0, 200),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (error !== undefined) {
|
|
89
|
+
updates.error_slot = typeof error === "string" ? error : JSON.stringify(error);
|
|
90
|
+
}
|
|
91
|
+
const setClauses = Object.keys(updates).map((k) => `${k} = ?`).join(", ");
|
|
92
|
+
const values = [...Object.values(updates), taskId];
|
|
93
|
+
this.db.prepare(`UPDATE tasks SET ${setClauses} WHERE task_id = ?`).run(...values);
|
|
94
|
+
return this.get(taskId);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
listByStatus(status) {
|
|
98
|
+
return this.db.prepare("SELECT * FROM tasks WHERE status = ? ORDER BY created_at DESC").all(status);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
listTimedOut() {
|
|
102
|
+
const now = new Date().toISOString();
|
|
103
|
+
return this.db.prepare("SELECT * FROM tasks WHERE status = 'running' AND deadline_at < ?").all(now);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
sweepTimedOutTasks() {
|
|
107
|
+
const now = new Date().toISOString();
|
|
108
|
+
const overdue = this.db.prepare("SELECT * FROM tasks WHERE status = 'running' AND deadline_at < ?").all(now);
|
|
109
|
+
for (const task of overdue) {
|
|
110
|
+
this.db.prepare("UPDATE tasks SET status = 'timed_out', updated_at = ? WHERE task_id = ?").run(now, task.task_id);
|
|
111
|
+
}
|
|
112
|
+
return overdue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
advanceEscalation(taskId) {
|
|
116
|
+
const task = this.get(taskId);
|
|
117
|
+
if (!task) throw new Error(`Task ${taskId} not found`);
|
|
118
|
+
if (!["timed_out", "escalated"].includes(task.status)) {
|
|
119
|
+
throw new Error(`Cannot escalate task in status: ${task.status}`);
|
|
120
|
+
}
|
|
121
|
+
const chain = JSON.parse(task.escalation_chain);
|
|
122
|
+
const newIndex = task.escalation_index + 1;
|
|
123
|
+
this.db.prepare("UPDATE tasks SET escalation_index = ?, updated_at = ? WHERE task_id = ?")
|
|
124
|
+
.run(newIndex, new Date().toISOString(), taskId);
|
|
125
|
+
if (newIndex >= chain.length) {
|
|
126
|
+
this.db.prepare("UPDATE tasks SET status = 'escalated', updated_at = ? WHERE task_id = ?")
|
|
127
|
+
.run(new Date().toISOString(), taskId);
|
|
128
|
+
}
|
|
129
|
+
return { escalationIndex: newIndex, target: chain[newIndex] || null, exhausted: newIndex >= chain.length };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getInbox() {
|
|
133
|
+
return this.db.prepare(`
|
|
134
|
+
SELECT * FROM tasks
|
|
135
|
+
WHERE status IN ('timed_out', 'escalated')
|
|
136
|
+
AND escalation_index >= json_array_length(escalation_chain)
|
|
137
|
+
ORDER BY created_at DESC
|
|
138
|
+
`).all();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -1,107 +1,107 @@
|
|
|
1
|
-
import { activeRecall } from "../memory/active-recall.js";
|
|
2
|
-
|
|
3
|
-
function compactCheckpoint(checkpoint) {
|
|
4
|
-
if (!checkpoint) return null;
|
|
5
|
-
return {
|
|
6
|
-
objective: checkpoint.objective,
|
|
7
|
-
status: checkpoint.status,
|
|
8
|
-
nextActions: checkpoint.nextActions || [],
|
|
9
|
-
blockers: checkpoint.blockers || []
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export async function buildTaskPacket({
|
|
14
|
-
agentId,
|
|
15
|
-
objective,
|
|
16
|
-
memoryStore,
|
|
17
|
-
backendManager = null,
|
|
18
|
-
workspaceRoot = null,
|
|
19
|
-
backendOrder = null,
|
|
20
|
-
qmdSupplementLimit = 2,
|
|
21
|
-
retrievalQueries = null,
|
|
22
|
-
identity = null,
|
|
23
|
-
interactionContract = null,
|
|
24
|
-
outputContract = null,
|
|
25
|
-
reportGuidance = null,
|
|
26
|
-
allowedTools = [],
|
|
27
|
-
artifactRefs = [],
|
|
28
|
-
budget = {},
|
|
29
|
-
checkpointId = "latest",
|
|
30
|
-
memoryLimit = 8,
|
|
31
|
-
retrievalBlend = null,
|
|
32
|
-
conversationContext = undefined
|
|
33
|
-
}) {
|
|
34
|
-
const checkpoint = compactCheckpoint(await memoryStore.loadCheckpoint(agentId, checkpointId));
|
|
35
|
-
let memory;
|
|
36
|
-
const useActiveRecall = memoryStore?.queryRetrievalCandidates;
|
|
37
|
-
if (useActiveRecall) {
|
|
38
|
-
const tokenBudget = budget.maxTokens ? Math.min(2000, Math.floor(budget.maxTokens * 0.08)) : 2000;
|
|
39
|
-
const recallResult = await activeRecall({
|
|
40
|
-
inboundMessage: objective,
|
|
41
|
-
memoryStore,
|
|
42
|
-
tokenBudget,
|
|
43
|
-
topicRelevanceFn: null,
|
|
44
|
-
});
|
|
45
|
-
const items = [
|
|
46
|
-
...recallResult.facts.map((c) => ({ ...c, tier: "fact" })),
|
|
47
|
-
...recallResult.asides.map((c) => ({ ...c, tier: "aside" })),
|
|
48
|
-
];
|
|
49
|
-
memory = { items, backends: null, retrieval: null };
|
|
50
|
-
} else {
|
|
51
|
-
memory = backendManager
|
|
52
|
-
? await backendManager.queryCombined(agentId, objective, {
|
|
53
|
-
workspaceRoot,
|
|
54
|
-
backendOrder,
|
|
55
|
-
qmdSupplementLimit,
|
|
56
|
-
fileQuery: retrievalQueries?.fileQuery || objective,
|
|
57
|
-
qmdQuery: retrievalQueries?.qmdQuery || objective,
|
|
58
|
-
limit: memoryLimit,
|
|
59
|
-
retrievalBlend
|
|
60
|
-
})
|
|
61
|
-
: await memoryStore.query(agentId, objective, { limit: memoryLimit, retrievalBlend });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
agentId,
|
|
66
|
-
objective,
|
|
67
|
-
conversationContext: conversationContext || null,
|
|
68
|
-
budget: {
|
|
69
|
-
maxTokens: budget.maxTokens ?? 8000,
|
|
70
|
-
maxRuntimeSeconds: budget.maxRuntimeSeconds ?? 60
|
|
71
|
-
},
|
|
72
|
-
layers: {
|
|
73
|
-
systemRules: [
|
|
74
|
-
"Transcript is not the system of record.",
|
|
75
|
-
"Use checkpoint and memory before requesting more history.",
|
|
76
|
-
"Do not write durable memory without policy approval."
|
|
77
|
-
],
|
|
78
|
-
identity,
|
|
79
|
-
interactionContract,
|
|
80
|
-
outputContract,
|
|
81
|
-
reportGuidance,
|
|
82
|
-
checkpoint,
|
|
83
|
-
retrieval: memory.backends || null,
|
|
84
|
-
retrievalMeta: memory.retrieval || null,
|
|
85
|
-
retrievalQueries,
|
|
86
|
-
memory: memory.items.map((item) => ({
|
|
87
|
-
entryId: item.entryId || null,
|
|
88
|
-
type: item.type || "memory",
|
|
89
|
-
category: item.category || "general",
|
|
90
|
-
title: item.title || "",
|
|
91
|
-
content: item.content || "",
|
|
92
|
-
summary: item.summary || "",
|
|
93
|
-
reason: item.reason || "",
|
|
94
|
-
score: Number((item.score ?? 0).toFixed(4)),
|
|
95
|
-
tier: item.tier || null,
|
|
96
|
-
sourceBackend: item.sourceBackend || "file",
|
|
97
|
-
candidateSource: item.candidateSource || "indexed",
|
|
98
|
-
lexicalScore: Number((item.lexicalScore ?? 0).toFixed(4)),
|
|
99
|
-
embeddingSimilarity: Number((item.embeddingSimilarity ?? 0).toFixed(4)),
|
|
100
|
-
embeddingFreshness: item.embeddingFreshness || "missing",
|
|
101
|
-
retrievalSources: item.retrievalSources || ["lexical"]
|
|
102
|
-
})),
|
|
103
|
-
tools: allowedTools,
|
|
104
|
-
artifacts: artifactRefs
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
}
|
|
1
|
+
import { activeRecall } from "../memory/active-recall.js";
|
|
2
|
+
|
|
3
|
+
function compactCheckpoint(checkpoint) {
|
|
4
|
+
if (!checkpoint) return null;
|
|
5
|
+
return {
|
|
6
|
+
objective: checkpoint.objective,
|
|
7
|
+
status: checkpoint.status,
|
|
8
|
+
nextActions: checkpoint.nextActions || [],
|
|
9
|
+
blockers: checkpoint.blockers || []
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function buildTaskPacket({
|
|
14
|
+
agentId,
|
|
15
|
+
objective,
|
|
16
|
+
memoryStore,
|
|
17
|
+
backendManager = null,
|
|
18
|
+
workspaceRoot = null,
|
|
19
|
+
backendOrder = null,
|
|
20
|
+
qmdSupplementLimit = 2,
|
|
21
|
+
retrievalQueries = null,
|
|
22
|
+
identity = null,
|
|
23
|
+
interactionContract = null,
|
|
24
|
+
outputContract = null,
|
|
25
|
+
reportGuidance = null,
|
|
26
|
+
allowedTools = [],
|
|
27
|
+
artifactRefs = [],
|
|
28
|
+
budget = {},
|
|
29
|
+
checkpointId = "latest",
|
|
30
|
+
memoryLimit = 8,
|
|
31
|
+
retrievalBlend = null,
|
|
32
|
+
conversationContext = undefined
|
|
33
|
+
}) {
|
|
34
|
+
const checkpoint = compactCheckpoint(await memoryStore.loadCheckpoint(agentId, checkpointId));
|
|
35
|
+
let memory;
|
|
36
|
+
const useActiveRecall = memoryStore?.queryRetrievalCandidates;
|
|
37
|
+
if (useActiveRecall) {
|
|
38
|
+
const tokenBudget = budget.maxTokens ? Math.min(2000, Math.floor(budget.maxTokens * 0.08)) : 2000;
|
|
39
|
+
const recallResult = await activeRecall({
|
|
40
|
+
inboundMessage: objective,
|
|
41
|
+
memoryStore,
|
|
42
|
+
tokenBudget,
|
|
43
|
+
topicRelevanceFn: null,
|
|
44
|
+
});
|
|
45
|
+
const items = [
|
|
46
|
+
...recallResult.facts.map((c) => ({ ...c, tier: "fact" })),
|
|
47
|
+
...recallResult.asides.map((c) => ({ ...c, tier: "aside" })),
|
|
48
|
+
];
|
|
49
|
+
memory = { items, backends: null, retrieval: null };
|
|
50
|
+
} else {
|
|
51
|
+
memory = backendManager
|
|
52
|
+
? await backendManager.queryCombined(agentId, objective, {
|
|
53
|
+
workspaceRoot,
|
|
54
|
+
backendOrder,
|
|
55
|
+
qmdSupplementLimit,
|
|
56
|
+
fileQuery: retrievalQueries?.fileQuery || objective,
|
|
57
|
+
qmdQuery: retrievalQueries?.qmdQuery || objective,
|
|
58
|
+
limit: memoryLimit,
|
|
59
|
+
retrievalBlend
|
|
60
|
+
})
|
|
61
|
+
: await memoryStore.query(agentId, objective, { limit: memoryLimit, retrievalBlend });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
agentId,
|
|
66
|
+
objective,
|
|
67
|
+
conversationContext: conversationContext || null,
|
|
68
|
+
budget: {
|
|
69
|
+
maxTokens: budget.maxTokens ?? 8000,
|
|
70
|
+
maxRuntimeSeconds: budget.maxRuntimeSeconds ?? 60
|
|
71
|
+
},
|
|
72
|
+
layers: {
|
|
73
|
+
systemRules: [
|
|
74
|
+
"Transcript is not the system of record.",
|
|
75
|
+
"Use checkpoint and memory before requesting more history.",
|
|
76
|
+
"Do not write durable memory without policy approval."
|
|
77
|
+
],
|
|
78
|
+
identity,
|
|
79
|
+
interactionContract,
|
|
80
|
+
outputContract,
|
|
81
|
+
reportGuidance,
|
|
82
|
+
checkpoint,
|
|
83
|
+
retrieval: memory.backends || null,
|
|
84
|
+
retrievalMeta: memory.retrieval || null,
|
|
85
|
+
retrievalQueries,
|
|
86
|
+
memory: memory.items.map((item) => ({
|
|
87
|
+
entryId: item.entryId || null,
|
|
88
|
+
type: item.type || "memory",
|
|
89
|
+
category: item.category || "general",
|
|
90
|
+
title: item.title || "",
|
|
91
|
+
content: item.content || "",
|
|
92
|
+
summary: item.summary || "",
|
|
93
|
+
reason: item.reason || "",
|
|
94
|
+
score: Number((item.score ?? 0).toFixed(4)),
|
|
95
|
+
tier: item.tier || null,
|
|
96
|
+
sourceBackend: item.sourceBackend || "file",
|
|
97
|
+
candidateSource: item.candidateSource || "indexed",
|
|
98
|
+
lexicalScore: Number((item.lexicalScore ?? 0).toFixed(4)),
|
|
99
|
+
embeddingSimilarity: Number((item.embeddingSimilarity ?? 0).toFixed(4)),
|
|
100
|
+
embeddingFreshness: item.embeddingFreshness || "missing",
|
|
101
|
+
retrievalSources: item.retrievalSources || ["lexical"]
|
|
102
|
+
})),
|
|
103
|
+
tools: allowedTools,
|
|
104
|
+
artifacts: artifactRefs
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|