blun-king-cli 9.1.566 → 9.1.569
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/agent-spine-plugin/.claude-plugin/marketplace.json +1 -1
- package/agent-spine-plugin/.claude-plugin/plugin.json +1 -1
- package/agent-spine-plugin/.codex-plugin/plugin.json +2 -1
- package/agent-spine-plugin/CHANGELOG.md +1581 -0
- package/agent-spine-plugin/README.md +30 -4
- package/agent-spine-plugin/blun.plugin.json +3 -3
- package/agent-spine-plugin/docs/acceptance.md +61 -0
- package/agent-spine-plugin/docs/assignment-continuation.md +48 -0
- package/agent-spine-plugin/docs/host-integration.md +178 -0
- package/agent-spine-plugin/docs/preflight-recall.md +69 -0
- package/agent-spine-plugin/docs/preservation-contract.md +53 -0
- package/agent-spine-plugin/docs/quality-gates.md +50 -0
- package/agent-spine-plugin/docs/releasing.md +85 -0
- package/agent-spine-plugin/docs/session-timeline.md +251 -0
- package/agent-spine-plugin/docs/source-roots.md +113 -0
- package/agent-spine-plugin/docs/structured-completion.md +67 -0
- package/agent-spine-plugin/docs/world-model.md +94 -0
- package/agent-spine-plugin/hooks/codex.json +2 -2
- package/agent-spine-plugin/hooks/hooks.json +1 -1
- package/agent-spine-plugin/hooks/version.json +1 -1
- package/agent-spine-plugin/package.json +13 -3
- package/agent-spine-plugin/scripts/check-codex-install.js +226 -0
- package/agent-spine-plugin/scripts/check-hosts.js +14 -5
- package/agent-spine-plugin/scripts/check-install-hook.js +226 -0
- package/agent-spine-plugin/scripts/check-install-selfstarter.js +154 -0
- package/agent-spine-plugin/scripts/check-install.js +478 -0
- package/agent-spine-plugin/scripts/check-line-budget.js +58 -0
- package/agent-spine-plugin/scripts/check-syntax.js +29 -0
- package/agent-spine-plugin/scripts/github-actions.js +11 -0
- package/agent-spine-plugin/scripts/hermetic-process.js +183 -0
- package/agent-spine-plugin/scripts/release-check.js +145 -0
- package/agent-spine-plugin/scripts/run-acceptance.js +19 -0
- package/agent-spine-plugin/scripts/run-checks.js +47 -0
- package/agent-spine-plugin/scripts/run-tests-hermetic.js +89 -0
- package/agent-spine-plugin/skills/agent-spine/SKILL.md +16 -2
- package/agent-spine-plugin/spine-example/1-identity.md +12 -0
- package/agent-spine-plugin/spine-example/2-voice.md +6 -0
- package/agent-spine-plugin/spine-example/3-conduct.md +8 -0
- package/agent-spine-plugin/spine-example/4-history.md +4 -0
- package/agent-spine-plugin/src/cli-agent.js +296 -0
- package/agent-spine-plugin/src/cli-attention.js +95 -0
- package/agent-spine-plugin/src/cli-autonomy.js +36 -0
- package/agent-spine-plugin/src/cli-common.js +71 -0
- package/agent-spine-plugin/src/cli-continuity.js +116 -0
- package/agent-spine-plugin/src/cli-core.js +128 -0
- package/agent-spine-plugin/src/cli-diagnostics.js +291 -0
- package/agent-spine-plugin/src/cli-host.js +21 -0
- package/agent-spine-plugin/src/cli-learning.js +305 -0
- package/agent-spine-plugin/src/cli-premortem.js +16 -0
- package/agent-spine-plugin/src/cli-sharing.js +230 -0
- package/agent-spine-plugin/src/cli.js +40 -1350
- package/agent-spine-plugin/src/codex-reader-launcher.js +182 -0
- package/agent-spine-plugin/src/hook.js +185 -567
- package/agent-spine-plugin/src/index.js +16 -2
- package/agent-spine-plugin/src/lib/acceptance.js +113 -11
- package/agent-spine-plugin/src/lib/action-lesson-recall.js +53 -0
- package/agent-spine-plugin/src/lib/attention-context.js +167 -0
- package/agent-spine-plugin/src/lib/attention-events.js +113 -0
- package/agent-spine-plugin/src/lib/attention-privacy.js +72 -0
- package/agent-spine-plugin/src/lib/attention-schema.js +164 -0
- package/agent-spine-plugin/src/lib/attention-storage.js +93 -0
- package/agent-spine-plugin/src/lib/attention.js +9 -600
- package/agent-spine-plugin/src/lib/audit-premortem.js +223 -0
- package/agent-spine-plugin/src/lib/audit.js +56 -6
- package/agent-spine-plugin/src/lib/autonomy-policy.js +110 -0
- package/agent-spine-plugin/src/lib/autonomy-store.js +202 -0
- package/agent-spine-plugin/src/lib/autonomy.js +8 -0
- package/agent-spine-plugin/src/lib/briefing.js +67 -4
- package/agent-spine-plugin/src/lib/catalog-document-read.js +51 -0
- package/agent-spine-plugin/src/lib/catalog.js +1 -1
- package/agent-spine-plugin/src/lib/codex-installation.js +231 -0
- package/agent-spine-plugin/src/lib/codex-skill-installation.js +211 -0
- package/agent-spine-plugin/src/lib/context.js +3 -1
- package/agent-spine-plugin/src/lib/delivery-agent-usage.js +224 -0
- package/agent-spine-plugin/src/lib/delivery-assignment.js +220 -0
- package/agent-spine-plugin/src/lib/delivery-command-actions.js +453 -0
- package/agent-spine-plugin/src/lib/delivery-knowledge.js +78 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-binding.js +107 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-closure.js +171 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-codec.js +45 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-correction.js +101 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-file.js +21 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-index.js +493 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-inspection.js +37 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-recovery.js +120 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-rejection.js +65 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-results.js +28 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-session-guard.js +46 -0
- package/agent-spine-plugin/src/lib/delivery-premortem-write-ledger.js +285 -0
- package/agent-spine-plugin/src/lib/delivery-premortem.js +499 -0
- package/agent-spine-plugin/src/lib/delivery-shell-heredoc.js +115 -0
- package/agent-spine-plugin/src/lib/delivery-shell-substitutions.js +111 -0
- package/agent-spine-plugin/src/lib/delivery-shell-wrapper.js +64 -0
- package/agent-spine-plugin/src/lib/delivery-target.js +73 -0
- package/agent-spine-plugin/src/lib/delivery-verification.js +445 -0
- package/agent-spine-plugin/src/lib/documents.js +27 -5
- package/agent-spine-plugin/src/lib/filesystem-retry.js +2 -0
- package/agent-spine-plugin/src/lib/gateway-common.js +68 -0
- package/agent-spine-plugin/src/lib/gateway-control.js +302 -0
- package/agent-spine-plugin/src/lib/gateway-delivery.js +103 -0
- package/agent-spine-plugin/src/lib/gateway-execution.js +350 -0
- package/agent-spine-plugin/src/lib/gateway-host-lifecycle.js +185 -0
- package/agent-spine-plugin/src/lib/gateway-inspection.js +85 -0
- package/agent-spine-plugin/src/lib/gateway-knowledge.js +129 -0
- package/agent-spine-plugin/src/lib/gateway-policy-provenance.js +197 -0
- package/agent-spine-plugin/src/lib/gateway-premortem-disposition.js +82 -0
- package/agent-spine-plugin/src/lib/gateway-premortem.js +363 -0
- package/agent-spine-plugin/src/lib/gateway-runs.js +300 -0
- package/agent-spine-plugin/src/lib/gateway-runtime-identity.js +31 -0
- package/agent-spine-plugin/src/lib/gateway-runtime-records.js +21 -0
- package/agent-spine-plugin/src/lib/gateway-runtime.js +18 -1623
- package/agent-spine-plugin/src/lib/gateway-state-transaction.js +356 -0
- package/agent-spine-plugin/src/lib/gateway-state.js +342 -0
- package/agent-spine-plugin/src/lib/hook-artifact-guards.js +356 -0
- package/agent-spine-plugin/src/lib/hook-audit.js +16 -2
- package/agent-spine-plugin/src/lib/hook-briefing-use.js +68 -0
- package/agent-spine-plugin/src/lib/hook-context.js +424 -0
- package/agent-spine-plugin/src/lib/hook-final-message.js +26 -0
- package/agent-spine-plugin/src/lib/hook-input.js +27 -0
- package/agent-spine-plugin/src/lib/hook-output.js +140 -0
- package/agent-spine-plugin/src/lib/hook-premortem.js +257 -0
- package/agent-spine-plugin/src/lib/hook-process-advisory.js +37 -0
- package/agent-spine-plugin/src/lib/hook-protection.js +95 -0
- package/agent-spine-plugin/src/lib/hook-stop-verification.js +84 -0
- package/agent-spine-plugin/src/lib/hook-timeline.js +91 -0
- package/agent-spine-plugin/src/lib/identifier-analysis.js +446 -0
- package/agent-spine-plugin/src/lib/indexed-memory.js +23 -6
- package/agent-spine-plugin/src/lib/knowledge-evidence.js +431 -0
- package/agent-spine-plugin/src/lib/learning-applications.js +441 -0
- package/agent-spine-plugin/src/lib/learning-candidates.js +274 -0
- package/agent-spine-plugin/src/lib/learning-context.js +130 -0
- package/agent-spine-plugin/src/lib/learning-delivery-contracts.js +310 -0
- package/agent-spine-plugin/src/lib/learning-evaluation-contracts.js +350 -0
- package/agent-spine-plugin/src/lib/learning-evaluation-registration.js +372 -0
- package/agent-spine-plugin/src/lib/learning-evaluation-revocation.js +292 -0
- package/agent-spine-plugin/src/lib/learning-evidence-contracts.js +321 -0
- package/agent-spine-plugin/src/lib/learning-findings.js +458 -0
- package/agent-spine-plugin/src/lib/learning-measurement-contracts.js +285 -0
- package/agent-spine-plugin/src/lib/learning-measurements.js +265 -0
- package/agent-spine-plugin/src/lib/learning-outcome-contracts.js +165 -0
- package/agent-spine-plugin/src/lib/learning-outcomes.js +343 -0
- package/agent-spine-plugin/src/lib/learning-reconciliation.js +290 -0
- package/agent-spine-plugin/src/lib/learning-retry-contracts.js +188 -0
- package/agent-spine-plugin/src/lib/learning-schema.js +229 -0
- package/agent-spine-plugin/src/lib/learning-scope-targets.js +424 -0
- package/agent-spine-plugin/src/lib/learning-state-upgrade.js +477 -0
- package/agent-spine-plugin/src/lib/learning-status-configuration.js +476 -0
- package/agent-spine-plugin/src/lib/learning-storage.js +203 -0
- package/agent-spine-plugin/src/lib/learning-trial-recovery.js +218 -0
- package/agent-spine-plugin/src/lib/learning-validation-contracts.js +375 -0
- package/agent-spine-plugin/src/lib/learning-validation-renewal.js +298 -0
- package/agent-spine-plugin/src/lib/learning-validation-runtime.js +234 -0
- package/agent-spine-plugin/src/lib/learning.js +36 -6923
- package/agent-spine-plugin/src/lib/lesson-recall-session.js +172 -0
- package/agent-spine-plugin/src/lib/mcp-autonomy-tools.js +37 -0
- package/agent-spine-plugin/src/lib/mcp-delivery-completion.js +124 -0
- package/agent-spine-plugin/src/lib/mcp-delivery-tools.js +45 -0
- package/agent-spine-plugin/src/lib/mcp-premortem.js +68 -0
- package/agent-spine-plugin/src/lib/mcp-runtime.js +269 -0
- package/agent-spine-plugin/src/lib/mcp-source-context.js +51 -0
- package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +164 -0
- package/agent-spine-plugin/src/lib/mcp-world-tools.js +52 -0
- package/agent-spine-plugin/src/lib/owned-file-lock.js +30 -10
- package/agent-spine-plugin/src/lib/project-portfolio.js +176 -0
- package/agent-spine-plugin/src/lib/selfstarter-core.js +382 -0
- package/agent-spine-plugin/src/lib/selfstarter-jobs.js +149 -0
- package/agent-spine-plugin/src/lib/selfstarter-lease.js +204 -0
- package/agent-spine-plugin/src/lib/selfstarter-policy.js +90 -0
- package/agent-spine-plugin/src/lib/selfstarter-workspace.js +111 -0
- package/agent-spine-plugin/src/lib/selfstarter.js +11 -911
- package/agent-spine-plugin/src/lib/session-timeline-auth.js +316 -0
- package/agent-spine-plugin/src/lib/session-timeline-codex.js +58 -0
- package/agent-spine-plugin/src/lib/session-timeline-contract.js +48 -0
- package/agent-spine-plugin/src/lib/session-timeline-enrollment-source.js +41 -0
- package/agent-spine-plugin/src/lib/session-timeline-enrollment-storage.js +132 -0
- package/agent-spine-plugin/src/lib/session-timeline-enrollment-transport.js +17 -0
- package/agent-spine-plugin/src/lib/session-timeline-enrollment.js +500 -0
- package/agent-spine-plugin/src/lib/session-timeline-event-extract.js +157 -0
- package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +74 -0
- package/agent-spine-plugin/src/lib/session-timeline-host-receipt.js +117 -0
- package/agent-spine-plugin/src/lib/session-timeline-invocation.js +201 -0
- package/agent-spine-plugin/src/lib/session-timeline-king.js +79 -0
- package/agent-spine-plugin/src/lib/session-timeline-prior.js +59 -0
- package/agent-spine-plugin/src/lib/session-timeline-provider.js +34 -0
- package/agent-spine-plugin/src/lib/session-timeline-query.js +55 -0
- package/agent-spine-plugin/src/lib/session-timeline-results.js +31 -0
- package/agent-spine-plugin/src/lib/session-timeline-root.js +11 -0
- package/agent-spine-plugin/src/lib/session-timeline-search.js +82 -0
- package/agent-spine-plugin/src/lib/session-timeline-sid-acl.js +217 -0
- package/agent-spine-plugin/src/lib/session-timeline-source.js +83 -0
- package/agent-spine-plugin/src/lib/session-timeline-state.js +45 -0
- package/agent-spine-plugin/src/lib/session-timeline-transport.js +50 -0
- package/agent-spine-plugin/src/lib/session-timeline-windows-acl.js +148 -0
- package/agent-spine-plugin/src/lib/session-timeline.js +453 -0
- package/agent-spine-plugin/src/lib/source-roots.js +69 -136
- package/agent-spine-plugin/src/lib/source-tree-scan.js +178 -0
- package/agent-spine-plugin/src/lib/task-knowledge-context.js +78 -0
- package/agent-spine-plugin/src/lib/timeline-tool-guard.js +202 -0
- package/agent-spine-plugin/src/lib/world-knowledge.js +249 -0
- package/agent-spine-plugin/src/lib/world-model.js +278 -0
- package/agent-spine-plugin/src/mcp.js +20 -161
- package/agent-spine-plugin/src/version.js +1 -1
- package/agent-spine-plugin/src/worker.js +22 -5
- package/bin/agent-resume-snapshot.cjs +2 -2
- package/bin/agentspine-king-goal-inbox.mjs +111 -0
- package/bin/agentspine-king-goal-intake.mjs +106 -0
- package/bin/baseline-skill-performance-policy.cjs +1 -16
- package/bin/core-bootstrap.js +2 -0
- package/bin/curiosity-scout-policy.cjs +5 -1
- package/bin/input-draft-persistence.cjs +2 -2
- package/bin/king-tui-function-contract.json +33 -0
- package/bin/launcher-restart-policy.cjs +150 -0
- package/bin/launcher-runtime.js +56 -44
- package/bin/managed-context-startup-policy.cjs +27 -0
- package/bin/managed-plugin-selection.cjs +116 -0
- package/bin/mistake-relevance-policy.cjs +1 -1
- package/bin/observer-hooks.cjs +14 -0
- package/bin/oversized-context-offload-policy.cjs +86 -0
- package/bin/plugin-bootstrap.js +7 -40
- package/bin/proactive-compaction-policy.cjs +1 -1
- package/bin/provider-model-refresh-deadline.cjs +53 -0
- package/bin/provider-model-refresh-policy.cjs +107 -0
- package/bin/release-artifact-freeze-policy.cjs +30 -0
- package/bin/repeated-user-message-projection.cjs +3 -126
- package/bin/research-page-result.cjs +74 -0
- package/bin/runtime-exit-ledger.cjs +1 -0
- package/bin/session-compaction-policy.cjs +84 -0
- package/bin/skill-listing-performance-policy.cjs +2 -2
- package/bin/standard-tools-bootstrap.js +0 -37
- package/bin/subagent-skill-policy.cjs +3 -1
- package/bin/telegram-approval-relay.cjs +12 -7
- package/bin/telegram-private-conversation-policy.cjs +3 -2
- package/bin/telegram-queue-handoff-policy.cjs +24 -0
- package/bin/thinking-activity-status-policy.cjs +1 -1
- package/bin/thinking-only-guard.cjs +16 -12
- package/bin/tool-call-loop-policy.cjs +0 -2
- package/bin/tool-result-offload-policy.cjs +11 -2
- package/bin/tui-functional-contract.cjs +55 -0
- package/bin/update-notice.js +18 -14
- package/bin/user-message-offload-policy.cjs +1 -1
- package/bin/user-prompt-hook-origin-policy.cjs +34 -0
- package/bin/windows-node-crash-dump.cjs +110 -0
- package/blun.mjs +2517 -1048
- package/codebase-index/codebase_index.py +4 -3
- package/package.json +3 -17
- package/standard-skills/research-evidence/SKILL.md +39 -0
- package/standard-skills/research-evidence/references/evidence-format.md +82 -0
- package/standard-skills/research-evidence/scripts/evidence-collection.cjs +254 -0
- package/standard-skills/research-evidence/scripts/score-report.cjs +112 -0
- package/standard-skills/web-lesen/SKILL.md +37 -22
- package/standard-skills/web-lesen/scripts/crawl_public.py +376 -0
- package/telegram-plugin/DELIVERY.md +36 -0
- package/telegram-plugin/bin/telegram-approval-relay.cjs +13 -7
- package/telegram-plugin/bin/telegram-launcher-status-queue.cjs +122 -0
- package/telegram-plugin/bin/telegram-private-conversation-policy.cjs +3 -2
- package/telegram-plugin/bin/telegram-reply-parts.cjs +149 -0
- package/telegram-plugin/dist/bridge.mjs +11 -57
- package/telegram-plugin/dist/mcp-server.mjs +33 -4
- package/agent-spine-plugin/skill/SKILL.md +0 -76
- package/bin/mnemo-connect-heartbeat.cjs +0 -204
- package/bin/mnemo-tool-agent-policy.cjs +0 -22
- package/telegram-plugin/bin/telegram-mnemo-capture.cjs +0 -297
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { replaceFileWithRetry } from "./filesystem-retry.js";
|
|
5
|
+
import { withOwnedFileLock } from "./owned-file-lock.js";
|
|
6
|
+
import { canonicalPath, projectStateDir } from "./paths.js";
|
|
7
|
+
|
|
8
|
+
const SCHEMA = "agentspine.world-model/v1";
|
|
9
|
+
const ASSERTION_SCHEMA = "agentspine.world-assertion/v1";
|
|
10
|
+
const MAX_STATE_BYTES = 5 * 1024 * 1024;
|
|
11
|
+
const MAX_VALUE_BYTES = 8192;
|
|
12
|
+
const EVIDENCE_KINDS = new Set([
|
|
13
|
+
"objective-measurement", "explicit-user-feedback", "model-suggestion"
|
|
14
|
+
]);
|
|
15
|
+
const PRIVACY = new Set(["private", "shared", "group"]);
|
|
16
|
+
const STABLE_ID = /^[a-z][a-z0-9-]{0,31}:[A-Za-z0-9][A-Za-z0-9._/-]{0,190}$/;
|
|
17
|
+
const PREDICATE = /^[a-z][a-z0-9.-]{0,127}$/;
|
|
18
|
+
const DIGEST = /^[a-f0-9]{64}$/;
|
|
19
|
+
const FORBIDDEN = /(^|[-_.])(permissions?|rights?|authori[sz]ation|credentials?|secrets?|tokens?|api[-_]?keys?|delegation|tool[-_]?access|production[-_]?access|payments?|spending)([-_.]|$)/i;
|
|
20
|
+
|
|
21
|
+
function sha256(value) {
|
|
22
|
+
return createHash("sha256").update(value).digest("hex");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function canonicalValue(value, path = "value") {
|
|
26
|
+
if (value === null || ["string", "boolean"].includes(typeof value)) return value;
|
|
27
|
+
if (typeof value === "number") {
|
|
28
|
+
if (!Number.isFinite(value)) throw new Error(`${path} numbers must be finite`);
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(value)) return value.map((item, index) => canonicalValue(item, `${path}[${index}]`));
|
|
32
|
+
if (!value || typeof value !== "object" || Object.getPrototypeOf(value) !== Object.prototype) {
|
|
33
|
+
throw new Error(`${path} must be JSON data`);
|
|
34
|
+
}
|
|
35
|
+
const result = {};
|
|
36
|
+
for (const key of Object.keys(value).sort()) {
|
|
37
|
+
if (FORBIDDEN.test(key)) throw new Error(`${path}.${key} is authority data and cannot enter the world model`);
|
|
38
|
+
result[key] = canonicalValue(value[key], `${path}.${key}`);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function exactTime(value, field, fallback = null) {
|
|
44
|
+
if ((value === null || value === undefined) && fallback !== null) return fallback;
|
|
45
|
+
if (typeof value !== "string" || !value) throw new Error(`${field} is required`);
|
|
46
|
+
const date = new Date(value);
|
|
47
|
+
if (!Number.isFinite(date.getTime()) || date.toISOString() !== value) {
|
|
48
|
+
throw new Error(`${field} must be an exact ISO timestamp`);
|
|
49
|
+
}
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function stableId(value, field) {
|
|
54
|
+
if (typeof value !== "string" || !STABLE_ID.test(value)) throw new Error(`${field} must be a stable namespaced id`);
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function optionalStableId(value, field) {
|
|
59
|
+
return value === null || value === undefined ? null : stableId(value, field);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function emptyModel(root) {
|
|
63
|
+
return { schema: SCHEMA, root, revision: 0, assertions: [], events: [], authority: "context-only" };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function validateStoredAssertion(assertion, validKnowledgeFields) {
|
|
67
|
+
if (!assertion || assertion.schema !== ASSERTION_SCHEMA || assertion.authority !== "context-only") return false;
|
|
68
|
+
if (!STABLE_ID.test(assertion.id || "") || !STABLE_ID.test(assertion.subjectId || "")) return false;
|
|
69
|
+
if (!PREDICATE.test(assertion.predicate || "") || FORBIDDEN.test(assertion.predicate)
|
|
70
|
+
|| !EVIDENCE_KINDS.has(assertion.evidenceKind)) return false;
|
|
71
|
+
if (!PRIVACY.has(assertion.privacy) || !DIGEST.test(assertion.evidenceDigest || "")) return false;
|
|
72
|
+
if ((assertion.privacy === "group") !== Boolean(assertion.groupId)
|
|
73
|
+
|| (assertion.groupId && !STABLE_ID.test(assertion.groupId))
|
|
74
|
+
|| (assertion.projectId && !STABLE_ID.test(assertion.projectId))
|
|
75
|
+
|| !STABLE_ID.test(assertion.evidenceId || "") || !Array.isArray(assertion.supersedes)
|
|
76
|
+
|| assertion.supersedes.some((item) => !STABLE_ID.test(item))) return false;
|
|
77
|
+
if (!validKnowledgeFields(assertion)) return false;
|
|
78
|
+
try {
|
|
79
|
+
if (exactTime(assertion.observedAt, "observedAt") !== assertion.observedAt) return false;
|
|
80
|
+
if (assertion.expiresAt) exactTime(assertion.expiresAt, "expiresAt");
|
|
81
|
+
const value = canonicalValue(assertion.value);
|
|
82
|
+
if (sha256(JSON.stringify(value)) !== assertion.valueDigest) return false;
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return assertion.status === (assertion.evidenceKind === "model-suggestion" ? "proposed" : "established");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function normalizeModel(value, root, validKnowledgeFields) {
|
|
90
|
+
if (!value || value.schema !== SCHEMA || value.root !== root || value.authority !== "context-only"
|
|
91
|
+
|| !Number.isInteger(value.revision) || value.revision < 0 || !Array.isArray(value.assertions)
|
|
92
|
+
|| !Array.isArray(value.events) || value.assertions.some((item) => !validateStoredAssertion(item, validKnowledgeFields))
|
|
93
|
+
|| value.events.some((item) => !item || item.authority !== "context-only")) {
|
|
94
|
+
throw new Error("world model state is invalid");
|
|
95
|
+
}
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function stateNames(root) {
|
|
100
|
+
const canonicalRoot = await canonicalPath(root);
|
|
101
|
+
const directory = await projectStateDir(canonicalRoot);
|
|
102
|
+
const path = join(directory, "world-model.json");
|
|
103
|
+
return { root: canonicalRoot, path, lockPath: `${path}.lock` };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function readModel(names) {
|
|
107
|
+
try {
|
|
108
|
+
const metadata = await stat(names.path);
|
|
109
|
+
if (metadata.size > MAX_STATE_BYTES) throw new Error("world model exceeds the 5 MiB read limit");
|
|
110
|
+
const { validKnowledgeFields } = await import("./world-knowledge.js");
|
|
111
|
+
return normalizeModel(JSON.parse(await readFile(names.path, "utf8")), names.root, validKnowledgeFields);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (error.code === "ENOENT") return emptyModel(names.root);
|
|
114
|
+
if (error instanceof SyntaxError) throw new Error("world model state is not valid JSON");
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function writeModel(model, names, assertOwned) {
|
|
120
|
+
const content = `${JSON.stringify(model, null, 2)}\n`;
|
|
121
|
+
if (Buffer.byteLength(content) > MAX_STATE_BYTES) throw new Error("world model exceeds 5 MiB");
|
|
122
|
+
const temporary = `${names.path}.${process.pid}.${randomUUID()}.tmp`;
|
|
123
|
+
await writeFile(temporary, content, { mode: 0o600 });
|
|
124
|
+
await replaceFileWithRetry(temporary, names.path, { beforeAttempt: assertOwned });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function assertionInput(input, now, normalizeKnowledgeFields) {
|
|
128
|
+
const id = stableId(input.id, "id");
|
|
129
|
+
const subjectId = stableId(input.subjectId, "subjectId");
|
|
130
|
+
if (typeof input.predicate !== "string" || !PREDICATE.test(input.predicate)) {
|
|
131
|
+
throw new Error("predicate must be a lowercase dotted key");
|
|
132
|
+
}
|
|
133
|
+
if (FORBIDDEN.test(input.predicate)) throw new Error("authority predicates cannot enter the world model");
|
|
134
|
+
if (!EVIDENCE_KINDS.has(input.evidenceKind)) throw new Error("unsupported evidenceKind");
|
|
135
|
+
const evidenceId = stableId(input.evidenceId, "evidenceId");
|
|
136
|
+
if (!DIGEST.test(input.evidenceDigest || "")) throw new Error("evidenceDigest must be a lowercase SHA-256 digest");
|
|
137
|
+
const observedAt = exactTime(input.observedAt, "observedAt");
|
|
138
|
+
const nowTime = new Date(now).getTime();
|
|
139
|
+
if (new Date(observedAt).getTime() > nowTime) throw new Error("observedAt cannot be in the future");
|
|
140
|
+
const expiresAt = input.expiresAt === null || input.expiresAt === undefined
|
|
141
|
+
? null : exactTime(input.expiresAt, "expiresAt");
|
|
142
|
+
if (expiresAt && new Date(expiresAt).getTime() <= new Date(observedAt).getTime()) {
|
|
143
|
+
throw new Error("expiresAt must be after observedAt");
|
|
144
|
+
}
|
|
145
|
+
const privacy = input.privacy || "private";
|
|
146
|
+
if (!PRIVACY.has(privacy)) throw new Error("unsupported privacy");
|
|
147
|
+
const groupId = optionalStableId(input.groupId, "groupId");
|
|
148
|
+
if ((privacy === "group") !== Boolean(groupId)) throw new Error("group privacy requires exactly one groupId");
|
|
149
|
+
const value = canonicalValue(input.value);
|
|
150
|
+
if (Buffer.byteLength(JSON.stringify(value)) > MAX_VALUE_BYTES) throw new Error("value exceeds 8 KiB");
|
|
151
|
+
const supersedes = [...new Set((input.supersedes || []).map((item) => stableId(item, "supersedes")))].sort();
|
|
152
|
+
const knowledge = normalizeKnowledgeFields(input, input.evidenceKind, value);
|
|
153
|
+
return {
|
|
154
|
+
schema: ASSERTION_SCHEMA, id, subjectId, predicate: input.predicate, value,
|
|
155
|
+
valueDigest: sha256(JSON.stringify(value)), evidenceKind: input.evidenceKind,
|
|
156
|
+
evidenceId, evidenceDigest: input.evidenceDigest, observedAt, expiresAt,
|
|
157
|
+
projectId: optionalStableId(input.projectId, "projectId"), groupId, privacy,
|
|
158
|
+
supersedes, reason: typeof input.reason === "string" ? input.reason.trim().slice(0, 500) : "",
|
|
159
|
+
status: input.evidenceKind === "model-suggestion" ? "proposed" : "established",
|
|
160
|
+
recordedAt: new Date(now).toISOString(), authority: "context-only",
|
|
161
|
+
...(knowledge.knowledgeKind ? knowledge : {})
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function assertionDigest(assertion) {
|
|
166
|
+
const { recordedAt: _recordedAt, ...material } = assertion;
|
|
167
|
+
return sha256(JSON.stringify(material));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function recordWorldAssertion(input = {}) {
|
|
171
|
+
const now = input.now || new Date();
|
|
172
|
+
const { normalizeKnowledgeFields } = await import("./world-knowledge.js");
|
|
173
|
+
const candidate = assertionInput(input, now, normalizeKnowledgeFields);
|
|
174
|
+
const names = await stateNames(input.root || process.cwd());
|
|
175
|
+
return withOwnedFileLock(names.lockPath, async ({ assertOwned }) => {
|
|
176
|
+
const model = await readModel(names);
|
|
177
|
+
const existing = model.assertions.find((item) => item.id === candidate.id);
|
|
178
|
+
if (existing) {
|
|
179
|
+
if (assertionDigest(existing) !== assertionDigest(candidate)) throw new Error(`world assertion id is already used: ${candidate.id}`);
|
|
180
|
+
return { status: "duplicate", assertion: structuredClone(existing), revision: model.revision, authority: "context-only" };
|
|
181
|
+
}
|
|
182
|
+
for (const id of candidate.supersedes) {
|
|
183
|
+
const previous = model.assertions.find((item) => item.id === id);
|
|
184
|
+
if (!previous) throw new Error(`superseded assertion does not exist: ${id}`);
|
|
185
|
+
if (previous.subjectId !== candidate.subjectId || previous.predicate !== candidate.predicate) {
|
|
186
|
+
throw new Error("supersession must keep the same subject and predicate");
|
|
187
|
+
}
|
|
188
|
+
if (candidate.status !== "established") throw new Error("model suggestions cannot supersede established context");
|
|
189
|
+
}
|
|
190
|
+
model.revision += 1;
|
|
191
|
+
model.assertions.push(candidate);
|
|
192
|
+
model.events.push({ kind: candidate.supersedes.length ? "resolved" : "recorded", assertionId: candidate.id,
|
|
193
|
+
supersedes: candidate.supersedes, at: candidate.recordedAt, authority: "context-only" });
|
|
194
|
+
await writeModel(model, names, assertOwned);
|
|
195
|
+
return { status: "recorded", assertion: structuredClone(candidate), revision: model.revision, authority: "context-only" };
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function visible(assertion, { includePrivate, groupId, projectId }) {
|
|
200
|
+
if (assertion.privacy === "private" && (!includePrivate || groupId)) return false;
|
|
201
|
+
if (assertion.privacy === "group" && assertion.groupId !== groupId) return false;
|
|
202
|
+
return assertion.projectId === null || assertion.projectId === projectId;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function publicAssertion(assertion) {
|
|
206
|
+
return structuredClone(assertion);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export async function worldContext({
|
|
210
|
+
root = process.cwd(), subjectId = null, projectId = null, groupId = null,
|
|
211
|
+
includePrivate = false, includeKnowledgeHistory = false, continuationTaskId = null,
|
|
212
|
+
maxItems = 100, now = new Date()
|
|
213
|
+
} = {}) {
|
|
214
|
+
if (groupId !== null && includePrivate) throw new Error("private world context cannot be assembled for a group audience");
|
|
215
|
+
if (!Number.isInteger(maxItems) || maxItems < 1 || maxItems > 500) throw new Error("maxItems must be between 1 and 500");
|
|
216
|
+
subjectId = optionalStableId(subjectId, "subjectId");
|
|
217
|
+
projectId = optionalStableId(projectId, "projectId");
|
|
218
|
+
groupId = optionalStableId(groupId, "groupId");
|
|
219
|
+
continuationTaskId = optionalStableId(continuationTaskId, "continuationTaskId");
|
|
220
|
+
const names = await stateNames(root);
|
|
221
|
+
const model = await readModel(names);
|
|
222
|
+
const { structuredKnowledgeView } = await import("./world-knowledge.js");
|
|
223
|
+
const cutoff = new Date(now).getTime();
|
|
224
|
+
if (!Number.isFinite(cutoff)) throw new Error("now is invalid");
|
|
225
|
+
const candidates = model.assertions.filter((item) => visible(item, { includePrivate, groupId, projectId })
|
|
226
|
+
&& (!subjectId || item.subjectId === subjectId));
|
|
227
|
+
const stale = candidates.filter((item) => item.expiresAt && new Date(item.expiresAt).getTime() <= cutoff);
|
|
228
|
+
const current = candidates.filter((item) => !item.expiresAt || new Date(item.expiresAt).getTime() > cutoff);
|
|
229
|
+
const superseded = new Set(current.flatMap((item) => item.status === "established" ? item.supersedes : []));
|
|
230
|
+
const active = current.filter((item) => !superseded.has(item.id));
|
|
231
|
+
const proposals = active.filter((item) => item.status === "proposed");
|
|
232
|
+
const established = active.filter((item) => item.status === "established");
|
|
233
|
+
const buckets = new Map();
|
|
234
|
+
for (const item of established) {
|
|
235
|
+
const key = `${item.subjectId}\0${item.predicate}`;
|
|
236
|
+
if (!buckets.has(key)) buckets.set(key, []);
|
|
237
|
+
buckets.get(key).push(item);
|
|
238
|
+
}
|
|
239
|
+
const facts = [];
|
|
240
|
+
const conflicts = [];
|
|
241
|
+
const conflictAssertions = [];
|
|
242
|
+
for (const assertions of buckets.values()) {
|
|
243
|
+
const values = new Set(assertions.map((item) => item.valueDigest));
|
|
244
|
+
if (values.size > 1) {
|
|
245
|
+
conflictAssertions.push(assertions);
|
|
246
|
+
conflicts.push({ subjectId: assertions[0].subjectId, predicate: assertions[0].predicate,
|
|
247
|
+
assertions: assertions.map(publicAssertion), authority: "context-only" });
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const newest = [...assertions].sort((a, b) => b.observedAt.localeCompare(a.observedAt))[0];
|
|
251
|
+
facts.push({ subjectId: newest.subjectId, predicate: newest.predicate, value: structuredClone(newest.value),
|
|
252
|
+
assertionIds: assertions.map((item) => item.id).sort(), evidenceKinds: [...new Set(assertions.map((item) => item.evidenceKind))].sort(),
|
|
253
|
+
latestObservedAt: newest.observedAt, expiresAt: newest.expiresAt, privacy: newest.privacy,
|
|
254
|
+
groupId: newest.groupId, projectId: newest.projectId, authority: "context-only" });
|
|
255
|
+
}
|
|
256
|
+
const order = (left, right) => left.subjectId.localeCompare(right.subjectId) || left.predicate.localeCompare(right.predicate);
|
|
257
|
+
facts.sort(order); conflicts.sort(order); proposals.sort(order); stale.sort(order);
|
|
258
|
+
const knowledge = structuredKnowledgeView({
|
|
259
|
+
candidates, stale, active, supersededIds: superseded, conflictAssertions,
|
|
260
|
+
includeHistory: Boolean(includeKnowledgeHistory), continuationTaskId, maxItems
|
|
261
|
+
});
|
|
262
|
+
return {
|
|
263
|
+
schema: "agentspine.world-context/v1", root: names.root, revision: model.revision,
|
|
264
|
+
facts: facts.slice(0, maxItems), conflicts: conflicts.slice(0, maxItems),
|
|
265
|
+
proposals: proposals.slice(0, maxItems).map(publicAssertion), stale: stale.slice(0, maxItems).map(publicAssertion),
|
|
266
|
+
knowledge,
|
|
267
|
+
omitted: { facts: Math.max(0, facts.length - maxItems), conflicts: Math.max(0, conflicts.length - maxItems),
|
|
268
|
+
proposals: Math.max(0, proposals.length - maxItems), stale: Math.max(0, stale.length - maxItems) },
|
|
269
|
+
uncertainty: { requiresResolution: conflicts.length > 0, conflicts: conflicts.length,
|
|
270
|
+
proposals: proposals.length, stale: stale.length },
|
|
271
|
+
authority: "context-only",
|
|
272
|
+
note: "Only unexpired, non-conflicting measured or explicitly user-confirmed assertions are facts. Model suggestions remain proposals and grant no authority."
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export async function worldModelStatePath(root = process.cwd()) {
|
|
277
|
+
return (await stateNames(root)).path;
|
|
278
|
+
}
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
import { VERSION } from "./version.js";
|
|
2
2
|
import { isMainModule } from "./lib/runtime.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from "./lib/attention.js";
|
|
11
|
-
import {
|
|
12
|
-
addLearningEvidence, configureLearning, deleteLearning, evaluateLearning,
|
|
13
|
-
learningContext, learningOutcomeStatus, proposeLearning, reviewLearning, rollbackLearning
|
|
14
|
-
} from "./lib/learning.js";
|
|
15
|
-
import { checkDelegation, createTask, taskContext, updateTask } from "./lib/coordination.js";
|
|
16
|
-
import { sharedContext } from "./lib/sharing.js";
|
|
17
|
-
import { resolveGoalKnowledgeGap } from "./lib/gateway-runtime.js";
|
|
18
|
-
import {
|
|
19
|
-
annotateDocument, linkDocuments, linkEntities,
|
|
20
|
-
relationshipContext, upsertEntity
|
|
21
|
-
} from "./lib/graph.js";
|
|
3
|
+
import { deliveryPremortemRecoveryTool, deliveryPremortemTool } from "./lib/mcp-premortem.js";
|
|
4
|
+
import { deliveryKnowledgeTool, sessionBriefingTool } from "./lib/mcp-delivery-tools.js";
|
|
5
|
+
import { worldModelTools } from "./lib/mcp-world-tools.js";
|
|
6
|
+
import { sessionTimelineTools } from "./lib/mcp-timeline-tools.js";
|
|
7
|
+
import { startMcpProtocol } from "./lib/mcp-runtime.js";
|
|
8
|
+
import { deliveryCompletionTool } from "./lib/mcp-delivery-completion.js";
|
|
9
|
+
import { autonomyTools } from "./lib/mcp-autonomy-tools.js";
|
|
22
10
|
|
|
23
11
|
const tools = [
|
|
24
12
|
{
|
|
@@ -38,34 +26,19 @@ const tools = [
|
|
|
38
26
|
}
|
|
39
27
|
}
|
|
40
28
|
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
properties: {
|
|
47
|
-
root: { type: "string" }, cwd: { type: "string" },
|
|
48
|
-
host: { type: "string", enum: ["codex", "claude", "generic"] },
|
|
49
|
-
entityId: { anyOf: [{ type: "string" }, { type: "null" }] },
|
|
50
|
-
userId: { anyOf: [{ type: "string" }, { type: "null" }] },
|
|
51
|
-
tenantId: { anyOf: [{ type: "string" }, { type: "null" }] },
|
|
52
|
-
groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
|
|
53
|
-
projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
|
|
54
|
-
currentTaskId: { anyOf: [{ type: "string" }, { type: "null" }] },
|
|
55
|
-
includePrivate: { type: "boolean" }, focusActive: { type: "boolean" },
|
|
56
|
-
includeSourceContent: { type: "boolean" },
|
|
57
|
-
maxBytes: { type: "integer", minimum: 4096, maximum: 262144 },
|
|
58
|
-
now: { type: "string" }
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
},
|
|
29
|
+
sessionBriefingTool,
|
|
30
|
+
deliveryKnowledgeTool,
|
|
31
|
+
...worldModelTools,
|
|
32
|
+
...sessionTimelineTools,
|
|
33
|
+
...autonomyTools,
|
|
62
34
|
{
|
|
63
35
|
name: "read_document",
|
|
64
|
-
description: "Read
|
|
36
|
+
description: "Read a bounded host-indexed Markdown source byte range with verified SHA-256 provenance; no broad home scan.",
|
|
65
37
|
inputSchema: {
|
|
66
38
|
type: "object", required: ["path"],
|
|
67
39
|
properties: {
|
|
68
40
|
root: { type: "string" }, path: { type: "string" },
|
|
41
|
+
host: { type: "string", enum: ["codex", "claude", "generic"] },
|
|
69
42
|
offset: { type: "integer", minimum: 0 }, length: { type: "integer", minimum: 1, maximum: 1048576 }
|
|
70
43
|
}
|
|
71
44
|
}
|
|
@@ -464,132 +437,18 @@ const tools = [
|
|
|
464
437
|
}
|
|
465
438
|
}
|
|
466
439
|
},
|
|
467
|
-
{
|
|
468
|
-
name: "goal_clarify",
|
|
469
|
-
description: "Resolve one exact open knowledge gap for a dependency-bound goal-plan step. This binds context only and grants no authority.",
|
|
470
|
-
inputSchema: {
|
|
471
|
-
type: "object", additionalProperties: false,
|
|
472
|
-
required: ["goalId", "gapId", "answer", "confirmLocalGoal"],
|
|
473
|
-
properties: {
|
|
474
|
-
root: { type: "string" }, goalId: { type: "string" }, gapId: { type: "string" },
|
|
475
|
-
answer: { type: "string", maxLength: 2000 },
|
|
476
|
-
answerSource: { type: "string", enum: ["owner-input", "objective-observation"] },
|
|
477
|
-
sourceDigest: { anyOf: [{ type: "string", pattern: "^[a-f0-9]{64}$" }, { type: "null" }] },
|
|
478
|
-
confirmLocalGoal: { type: "boolean", const: true }
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
},
|
|
482
440
|
{
|
|
483
441
|
name: "audit",
|
|
484
442
|
description: "Run AgentSpine's ten deterministic quality gates for discovery, hierarchy, links, authority, privacy, budget, and source preservation.",
|
|
485
443
|
inputSchema: { type: "object", properties: { root: { type: "string" } } }
|
|
486
|
-
}
|
|
444
|
+
},
|
|
445
|
+
deliveryPremortemTool,
|
|
446
|
+
deliveryCompletionTool,
|
|
447
|
+
deliveryPremortemRecoveryTool
|
|
487
448
|
];
|
|
488
449
|
|
|
489
|
-
function
|
|
490
|
-
return {
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
async function callTool(name, args = {}) {
|
|
494
|
-
const root = args.root || process.cwd();
|
|
495
|
-
if (name === "scan") return textResult(await scanAndSave(root));
|
|
496
|
-
if (name === "resolve_context") return textResult(await resolveContext({
|
|
497
|
-
root,
|
|
498
|
-
cwd: args.cwd || root,
|
|
499
|
-
host: args.host || "generic",
|
|
500
|
-
maxBytes: args.maxBytes ?? 65536,
|
|
501
|
-
includeContent: args.includeContent ?? true
|
|
502
|
-
}));
|
|
503
|
-
if (name === "session_briefing") return textResult(await sessionBriefing({ ...args, root }));
|
|
504
|
-
if (name === "read_document") return textResult(await readDocument({
|
|
505
|
-
root, path: args.path, offset: args.offset ?? 0, length: args.length ?? 65536
|
|
506
|
-
}));
|
|
507
|
-
if (name === "verify") return textResult(await verifyCatalog(root));
|
|
508
|
-
if (name === "link_documents") return textResult(await linkDocuments({ ...args, root }));
|
|
509
|
-
if (name === "annotate_document") return textResult(await annotateDocument({ ...args, root }));
|
|
510
|
-
if (name === "upsert_entity") return textResult(await upsertEntity({ ...args, root }));
|
|
511
|
-
if (name === "link_entities") return textResult(await linkEntities({ ...args, root }));
|
|
512
|
-
if (name === "relationship_context") return textResult(await relationshipContext({ ...args, root }));
|
|
513
|
-
if (name === "upsert_attention") return textResult(await upsertAttention({ ...args, root }));
|
|
514
|
-
if (name === "record_activity") return textResult(await recordActivity({ ...args, root }));
|
|
515
|
-
if (name === "attention_context") return textResult(await attentionContext({ ...args, root }));
|
|
516
|
-
if (name === "resolve_attention") return textResult(await resolveAttention({ ...args, root }));
|
|
517
|
-
if (name === "configure_attention") return textResult(await configureAttention({ ...args, root }));
|
|
518
|
-
if (name === "delete_attention") return textResult(await deleteAttention({ ...args, root }));
|
|
519
|
-
if (name === "propose_learning") return textResult(await proposeLearning({ ...args, root }));
|
|
520
|
-
if (name === "add_learning_evidence") return textResult(await addLearningEvidence({ ...args, root }));
|
|
521
|
-
if (name === "review_learning") return textResult(await reviewLearning({ ...args, root }));
|
|
522
|
-
if (name === "learning_context") return textResult(await learningContext({ ...args, root }));
|
|
523
|
-
if (name === "learning_outcome_status") return textResult(await learningOutcomeStatus({ ...args, root }));
|
|
524
|
-
if (name === "evaluate_learning") return textResult(await evaluateLearning({ ...args, root }));
|
|
525
|
-
if (name === "rollback_learning") return textResult(await rollbackLearning({ ...args, root }));
|
|
526
|
-
if (name === "configure_learning") return textResult(await configureLearning({ ...args, root }));
|
|
527
|
-
if (name === "delete_learning") return textResult(await deleteLearning({ ...args, root }));
|
|
528
|
-
if (name === "check_delegation") return textResult(await checkDelegation({ ...args, root }));
|
|
529
|
-
if (name === "create_task") return textResult(await createTask({ ...args, root }));
|
|
530
|
-
if (name === "update_task") return textResult(await updateTask({ ...args, root }));
|
|
531
|
-
if (name === "task_context") return textResult(await taskContext({ ...args, root }));
|
|
532
|
-
if (name === "shared_context") return textResult(await sharedContext({ ...args, root }));
|
|
533
|
-
if (name === "goal_clarify") return textResult(await resolveGoalKnowledgeGap({
|
|
534
|
-
root,
|
|
535
|
-
goalId: args.goalId,
|
|
536
|
-
gapId: args.gapId,
|
|
537
|
-
answer: args.answer,
|
|
538
|
-
answerSource: args.answerSource || "owner-input",
|
|
539
|
-
sourceDigest: args.sourceDigest || null,
|
|
540
|
-
confirmation: args.confirmLocalGoal === true ? "local-owner-confirmed" : null
|
|
541
|
-
}));
|
|
542
|
-
if (name === "audit") return textResult(await runAudit(root));
|
|
543
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
async function dispatch(message) {
|
|
547
|
-
const { id, method, params = {} } = message;
|
|
548
|
-
if (method === "initialize") {
|
|
549
|
-
return {
|
|
550
|
-
jsonrpc: "2.0", id,
|
|
551
|
-
result: {
|
|
552
|
-
protocolVersion: params.protocolVersion || "2025-06-18",
|
|
553
|
-
capabilities: { tools: { listChanged: false } },
|
|
554
|
-
serverInfo: { name: "agent-spine", version: VERSION }
|
|
555
|
-
}
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
if (method === "ping") return { jsonrpc: "2.0", id, result: {} };
|
|
559
|
-
if (method === "tools/list") return { jsonrpc: "2.0", id, result: { tools } };
|
|
560
|
-
if (method === "tools/call") {
|
|
561
|
-
try {
|
|
562
|
-
return { jsonrpc: "2.0", id, result: await callTool(params.name, params.arguments) };
|
|
563
|
-
} catch (error) {
|
|
564
|
-
return { jsonrpc: "2.0", id, result: textResult({ error: error.message }, true) };
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
if (method?.startsWith("notifications/")) return null;
|
|
568
|
-
return { jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } };
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
export function startMcpServer(input = process.stdin, output = process.stdout) {
|
|
572
|
-
input.setEncoding("utf8");
|
|
573
|
-
let buffer = "";
|
|
574
|
-
let queue = Promise.resolve();
|
|
575
|
-
input.on("data", (chunk) => {
|
|
576
|
-
buffer += chunk;
|
|
577
|
-
let newline;
|
|
578
|
-
while ((newline = buffer.indexOf("\n")) >= 0) {
|
|
579
|
-
const line = buffer.slice(0, newline).trim();
|
|
580
|
-
buffer = buffer.slice(newline + 1);
|
|
581
|
-
if (!line) continue;
|
|
582
|
-
queue = queue.then(async () => {
|
|
583
|
-
let response;
|
|
584
|
-
try {
|
|
585
|
-
response = await dispatch(JSON.parse(line));
|
|
586
|
-
} catch (error) {
|
|
587
|
-
response = { jsonrpc: "2.0", id: null, error: { code: -32700, message: error.message } };
|
|
588
|
-
}
|
|
589
|
-
if (response) output.write(`${JSON.stringify(response)}\n`);
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
});
|
|
450
|
+
export function startMcpServer(input = process.stdin, output = process.stdout, { environment = process.env } = {}) {
|
|
451
|
+
return startMcpProtocol({ input, output, tools, version: VERSION, environment });
|
|
593
452
|
}
|
|
594
453
|
|
|
595
454
|
if (isMainModule(import.meta.url)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.73.0";
|
|
@@ -5,11 +5,12 @@ import { lstat, realpath } from "node:fs/promises";
|
|
|
5
5
|
import { isAbsolute, join, resolve } from "node:path";
|
|
6
6
|
import {
|
|
7
7
|
claimGatewayWork, completeGatewayRun, deliverPrepared, executionAttemptForStep,
|
|
8
|
-
failGatewayRun, loadGatewayRuntime, reconcileGateway, updateGatewayHealth
|
|
8
|
+
failGatewayRun, loadGatewayRuntime, markGatewayHostStarted, reconcileGateway, updateGatewayHealth
|
|
9
9
|
} from "./lib/gateway-runtime.js";
|
|
10
10
|
import { createTelegramAdapter } from "./lib/telegram-adapter.js";
|
|
11
11
|
import { acknowledgeChannelDelivery, loadChannelRuntime } from "./lib/channel-runtime.js";
|
|
12
12
|
import { loadPersonaRuntime, syncPersonaRosterFromEnvironment } from "./lib/persona-runtime.js";
|
|
13
|
+
import { selfHelpPolicyForWorkItem } from "./lib/knowledge-evidence.js";
|
|
13
14
|
import { isMainModule } from "./lib/runtime.js";
|
|
14
15
|
|
|
15
16
|
const MAX_FRAME = 64 * 1024;
|
|
@@ -105,11 +106,20 @@ async function hostWorkItem(root, item) {
|
|
|
105
106
|
goal: goal ? structuredClone(goal) : null,
|
|
106
107
|
goalStep: goalStep ? { ...structuredClone(goalStep),
|
|
107
108
|
...(executionAttempt === null ? {} : { executionAttempt }) } : null,
|
|
109
|
+
selfHelpPolicy: goalStep ? selfHelpPolicyForWorkItem(goalStep) : null,
|
|
108
110
|
hostEnvironment: {
|
|
109
111
|
AGENTSPINE_GATEWAY_CONTEXT: "agentspine.gateway-start/v1",
|
|
110
112
|
AGENTSPINE_ENTITY_ID: item.agentId,
|
|
111
113
|
AGENTSPINE_PROJECT_ID: item.projectId,
|
|
114
|
+
AGENTSPINE_HOST: identity.host,
|
|
115
|
+
AGENTSPINE_GATEWAY_QUEUE_ID: item.queueId,
|
|
116
|
+
AGENTSPINE_GATEWAY_ATTEMPT: String(item.attempts),
|
|
112
117
|
...(item.groupId === null ? {} : { AGENTSPINE_GROUP_ID: item.groupId }),
|
|
118
|
+
...(goalStep ? {
|
|
119
|
+
AGENTSPINE_GOAL_ID: goal.goalId,
|
|
120
|
+
AGENTSPINE_GOAL_STEP_ID: goalStep.stepId,
|
|
121
|
+
AGENTSPINE_PLAN_DEFINITIONS_DIGEST: goal.plan.definitionsDigest
|
|
122
|
+
} : {}),
|
|
113
123
|
...(event ? {
|
|
114
124
|
AGENTSPINE_CHANNEL_EVENT_ID: event.eventId,
|
|
115
125
|
AGENTSPINE_CHANNEL_PROVIDER: event.provider
|
|
@@ -151,22 +161,29 @@ export async function runWorkerTick({ root = process.cwd(), workerId = "gateway-
|
|
|
151
161
|
return { status: delivery.outbox.status, processed: true, outboxId: delivery.outbox.outboxId,
|
|
152
162
|
recoveredDelivery: true };
|
|
153
163
|
}
|
|
154
|
-
const claim = await claimGatewayWork({ root, workerId, now });
|
|
164
|
+
const claim = await claimGatewayWork({ root, workerId, executionMode: "host-effect", now });
|
|
155
165
|
if (!claim.item) return { status: claim.reason, processed: false };
|
|
166
|
+
await markGatewayHostStarted({ root, queueId: claim.item.queueId, workerId,
|
|
167
|
+
claimedAt: claim.item.lease.claimedAt, attempt: claim.item.attempts, now });
|
|
156
168
|
let result;
|
|
157
169
|
try { result = await hostRunner(await hostWorkItem(root, claim.item), { env }); }
|
|
158
170
|
catch (error) {
|
|
159
171
|
const failed = await failGatewayRun({ root, queueId: claim.item.queueId, workerId,
|
|
172
|
+
claimedAt: claim.item.lease.claimedAt, attempt: claim.item.attempts,
|
|
160
173
|
error: "Host runtime unavailable: " + String(error.message).slice(0, 400), now });
|
|
161
174
|
return { status: failed.item.status, processed: true, queueId: failed.item.queueId, retryAt: failed.item.availableAt };
|
|
162
175
|
}
|
|
163
|
-
const completed = await completeGatewayRun({ root, queueId: claim.item.queueId, workerId,
|
|
176
|
+
const completed = await completeGatewayRun({ root, queueId: claim.item.queueId, workerId,
|
|
177
|
+
claimedAt: claim.item.lease.claimedAt, attempt: claim.item.attempts, result, now });
|
|
164
178
|
if (!completed.outbox) return {
|
|
165
179
|
status: completed.clarification ? "needs-clarification"
|
|
166
|
-
: completed.exploration ? "exploring" : completed.
|
|
180
|
+
: completed.exploration ? "exploring" : completed.selfHelp ? "self-help-resolved"
|
|
181
|
+
: completed.selfHelpRequired ? "self-help-required" : completed.item.status,
|
|
167
182
|
processed: true, queueId: completed.item.queueId,
|
|
168
183
|
...(completed.clarification ? { clarification: completed.clarification } : {}),
|
|
169
|
-
...(completed.exploration ? { exploration: completed.exploration } : {})
|
|
184
|
+
...(completed.exploration ? { exploration: completed.exploration } : {}),
|
|
185
|
+
...(completed.selfHelp ? { selfHelp: completed.selfHelp } : {}),
|
|
186
|
+
...(completed.selfHelpRequired ? { selfHelpRequired: completed.selfHelpRequired } : {})
|
|
170
187
|
};
|
|
171
188
|
const delivery = await deliverPrepared({ root, outboxId: completed.outbox.outboxId,
|
|
172
189
|
adapter: deliveryAdapter, now });
|
|
@@ -94,7 +94,7 @@ function createAgentResumeSnapshot(agent) {
|
|
|
94
94
|
goal: agent.goal.state ?? null,
|
|
95
95
|
turn: {
|
|
96
96
|
turnId: agent.turn.turnId,
|
|
97
|
-
telegramDeliveryIdentities: agent.turn.telegramDeliveryLedger
|
|
97
|
+
telegramDeliveryIdentities: agent.turn.telegramDeliveryLedger?.snapshot?.() ?? [],
|
|
98
98
|
},
|
|
99
99
|
microCompaction: { cutoff: agent.microCompaction.cutoff },
|
|
100
100
|
};
|
|
@@ -211,7 +211,7 @@ function restoreAgentResumeSnapshot(agent, snapshot) {
|
|
|
211
211
|
agent.turn.turnId = Number.isInteger(snapshot.turn.turnId) ? snapshot.turn.turnId : -1;
|
|
212
212
|
agent.turn.activeTurn = 'resuming';
|
|
213
213
|
agent.turn.steerBuffer.length = 0;
|
|
214
|
-
agent.turn.telegramDeliveryLedger
|
|
214
|
+
agent.turn.telegramDeliveryLedger?.restore?.(snapshot.turn.telegramDeliveryIdentities);
|
|
215
215
|
agent.microCompaction.cutoff = Number.isInteger(snapshot.microCompaction?.cutoff)
|
|
216
216
|
? Math.max(0, Math.min(snapshot.context.history.length, snapshot.microCompaction.cutoff))
|
|
217
217
|
: 0;
|