@midhunxavier/code-flow 0.0.0-stage → 0.1.0-preview.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/LICENSE +21 -0
- package/README.md +122 -2
- package/bin/codeflow.mjs +8 -0
- package/bin/launcher.mjs +127 -0
- package/dist/remote-host.json +1 -0
- package/licenses/inter.txt +92 -0
- package/licenses/preact.txt +21 -0
- package/licenses/signals-core.txt +21 -0
- package/licenses/signals.txt +21 -0
- package/licenses/typescript-notices.txt +193 -0
- package/licenses/typescript.txt +55 -0
- package/licenses/uqr.txt +22 -0
- package/licenses/xterm-fit.txt +19 -0
- package/licenses/xterm.txt +21 -0
- package/package.json +34 -4
- package/public/assets/browser-7XI52A6V.js +8 -0
- package/public/assets/changes-EYFPASEI.js +1 -0
- package/public/assets/chunk-7H3ZLTKA.js +2 -0
- package/public/assets/chunk-L7TUCIYF.js +1 -0
- package/public/assets/chunk-LLMQG23J.js +163 -0
- package/public/assets/main-KIRM2OEK.js +1 -0
- package/public/assets/settings-QJZVW22Y.js +9 -0
- package/public/assets/styles-615EBB20.css +32 -0
- package/public/assets/terminal-A2FI2NAX.js +62 -0
- package/public/assets.json +1 -0
- package/public/bundle-meta.json +1 -0
- package/public/docs/.nojekyll +0 -0
- package/public/docs/accessibility.html +19 -0
- package/public/docs/acp-agents.html +117 -0
- package/public/docs/agent-containers.html +79 -0
- package/public/docs/api.html +1130 -0
- package/public/docs/app.js +342 -0
- package/public/docs/application-improvements.html +275 -0
- package/public/docs/architecture-explorer.html +19 -0
- package/public/docs/architecture.html +149 -0
- package/public/docs/auto-team.html +142 -0
- package/public/docs/automations.html +109 -0
- package/public/docs/checkpoint-review.html +37 -0
- package/public/docs/code-hosts.html +35 -0
- package/public/docs/code-intelligence.html +35 -0
- package/public/docs/configuration.html +346 -0
- package/public/docs/context-inspection.html +23 -0
- package/public/docs/coordinated-changes.html +28 -0
- package/public/docs/deferred-runs.html +25 -0
- package/public/docs/dependency-discovery.html +19 -0
- package/public/docs/documentation.html +84 -0
- package/public/docs/dynamic-teams-evaluation.html +174 -0
- package/public/docs/dynamic-teams-implementation-plan.html +882 -0
- package/public/docs/dynamic-teams-implementation-review-1.html +141 -0
- package/public/docs/dynamic-teams-plan-review-0.html +204 -0
- package/public/docs/extensions.html +42 -0
- package/public/docs/external-sources.html +96 -0
- package/public/docs/filesystem-policy.html +26 -0
- package/public/docs/filtered-tasks.html +44 -0
- package/public/docs/font-license.txt +92 -0
- package/public/docs/getting-started.html +118 -0
- package/public/docs/github-review-threads.html +26 -0
- package/public/docs/github.html +157 -0
- package/public/docs/help.html +69 -0
- package/public/docs/index.html +77 -0
- package/public/docs/installation.html +74 -0
- package/public/docs/launch-profiles.html +48 -0
- package/public/docs/maintenance.html +60 -0
- package/public/docs/memory-claims.html +31 -0
- package/public/docs/memory-storage.html +58 -0
- package/public/docs/notifications.html +39 -0
- package/public/docs/patterns.html +195 -0
- package/public/docs/performance-results.html +210 -0
- package/public/docs/performance.html +166 -0
- package/public/docs/project-conventions.html +22 -0
- package/public/docs/project-first-navigation-plan.html +587 -0
- package/public/docs/project-teams-implementation-plan.html +772 -0
- package/public/docs/project-teams.html +99 -0
- package/public/docs/provider-compatibility.html +20 -0
- package/public/docs/releasing.html +61 -0
- package/public/docs/remote-workers.html +64 -0
- package/public/docs/review-inbox.html +26 -0
- package/public/docs/run-storage.html +23 -0
- package/public/docs/runs.html +141 -0
- package/public/docs/search.json +1 -0
- package/public/docs/security.html +133 -0
- package/public/docs/shared-projects.html +31 -0
- package/public/docs/ssh-projects.html +60 -0
- package/public/docs/style.css +768 -0
- package/public/docs/task-reruns.html +30 -0
- package/public/docs/team-collaboration.html +379 -0
- package/public/docs/verification.html +22 -0
- package/public/docs/vscode.html +53 -0
- package/public/docs/worker-permission-verification.html +122 -0
- package/public/index.html +17 -0
- package/scripts/check-node.mjs +7 -0
- package/scripts/codeflow.mjs +55 -0
- package/scripts/pty.py +273 -0
- package/src/acp-connections.js +110 -0
- package/src/adapters/acp-presets.js +37 -0
- package/src/adapters/acp-types.js +0 -0
- package/src/adapters/acp.js +328 -0
- package/src/adapters/agy-auth.js +67 -0
- package/src/adapters/agy-isolation.js +223 -0
- package/src/adapters/agy.js +297 -0
- package/src/adapters/claude.js +220 -0
- package/src/adapters/codex-app-server.js +518 -0
- package/src/adapters/codex.js +299 -0
- package/src/adapters/errors.js +57 -0
- package/src/adapters/index.js +91 -0
- package/src/adapters/managed-permissions.js +37 -0
- package/src/adapters/model-discovery.js +100 -0
- package/src/adapters/process.js +418 -0
- package/src/adapters/team-tools.js +102 -0
- package/src/adapters/types.js +0 -0
- package/src/agent-container.js +155 -0
- package/src/approvals.js +121 -0
- package/src/architecture.js +456 -0
- package/src/assets.js +44 -0
- package/src/automation.js +901 -0
- package/src/certificate-renewal.js +75 -0
- package/src/chat-handoff.js +55 -0
- package/src/checkpoint-review.js +72 -0
- package/src/checks.js +303 -0
- package/src/code-host-git.js +102 -0
- package/src/code-hosts-api.js +99 -0
- package/src/code-hosts.js +482 -0
- package/src/code-intelligence-worker.mjs +452 -0
- package/src/code-intelligence.js +141 -0
- package/src/collab/api.js +365 -0
- package/src/collab/checkpoints.js +198 -0
- package/src/collab/connections.js +139 -0
- package/src/collab/host-execution.js +347 -0
- package/src/collab/hub-client.js +210 -0
- package/src/collab/link-api.js +229 -0
- package/src/collab/link.js +595 -0
- package/src/collab/model.js +496 -0
- package/src/collab/runner.js +1014 -0
- package/src/collab/store.js +1452 -0
- package/src/collab/terminals.js +361 -0
- package/src/collaboration-reviews.js +116 -0
- package/src/config.js +48 -0
- package/src/context-budget.js +14 -0
- package/src/context-inspection.js +204 -0
- package/src/context.js +154 -0
- package/src/conventions.js +283 -0
- package/src/coordinated-api.js +247 -0
- package/src/deferred-launch.js +82 -0
- package/src/deferred-runs.js +515 -0
- package/src/dependency-health.js +35 -0
- package/src/diff.js +43 -0
- package/src/docs-help.js +316 -0
- package/src/doctor.js +232 -0
- package/src/engine.js +4366 -0
- package/src/env.js +44 -0
- package/src/experience.js +50 -0
- package/src/extensions.js +144 -0
- package/src/filesystem-access.js +107 -0
- package/src/filesystem-policy.js +238 -0
- package/src/filtered-task-limits.js +93 -0
- package/src/filtered-tasks-api.js +53 -0
- package/src/filtered-tasks.js +576 -0
- package/src/fs.js +57 -0
- package/src/git.js +626 -0
- package/src/github-review-threads.js +181 -0
- package/src/github.js +538 -0
- package/src/http.js +69 -0
- package/src/improve-settings.js +38 -0
- package/src/improve.js +187 -0
- package/src/integrations.js +373 -0
- package/src/integrity.js +259 -0
- package/src/journey-api.js +77 -0
- package/src/journey-replay.js +65 -0
- package/src/journey-store.js +97 -0
- package/src/journeys.js +60 -0
- package/src/json.js +81 -0
- package/src/launch-profiles.js +292 -0
- package/src/learnings.js +80 -0
- package/src/log.js +39 -0
- package/src/maintenance.js +335 -0
- package/src/memory-analysis.js +182 -0
- package/src/memory-claims.js +153 -0
- package/src/memory-sqlite.js +236 -0
- package/src/memory.js +552 -0
- package/src/merge-validation.js +58 -0
- package/src/model-catalog.js +238 -0
- package/src/model-research.js +91 -0
- package/src/notes.js +176 -0
- package/src/notifications.js +413 -0
- package/src/patterns/auto-team-model.js +212 -0
- package/src/patterns/auto-team.js +659 -0
- package/src/patterns/council.js +199 -0
- package/src/patterns/debate.js +188 -0
- package/src/patterns/findings.js +141 -0
- package/src/patterns/index.js +19 -0
- package/src/patterns/loop.js +158 -0
- package/src/patterns/pipeline.js +125 -0
- package/src/patterns/prompts.js +181 -0
- package/src/patterns/race.js +147 -0
- package/src/patterns/schemas.js +40 -0
- package/src/patterns/solo.js +49 -0
- package/src/patterns/supervisor.js +235 -0
- package/src/patterns/task-draft.js +29 -0
- package/src/patterns/tdd.js +231 -0
- package/src/patterns/team-strategies.js +67 -0
- package/src/performance.js +43 -0
- package/src/persistence-status.js +35 -0
- package/src/phone-sessions.js +96 -0
- package/src/preview-overlay.js +383 -0
- package/src/preview.js +219 -0
- package/src/privacy.js +23 -0
- package/src/project-report.js +22 -0
- package/src/project-team-model.js +178 -0
- package/src/project-team-policy.js +42 -0
- package/src/project-team-store.js +213 -0
- package/src/project-teams-api.js +36 -0
- package/src/project-teams.js +551 -0
- package/src/projects.js +286 -0
- package/src/provider-eligibility.js +9 -0
- package/src/readiness.js +98 -0
- package/src/remote-tls.js +66 -0
- package/src/remote-workers/bundles.js +198 -0
- package/src/remote-workers/client.js +332 -0
- package/src/remote-workers/durability.js +37 -0
- package/src/remote-workers/protocol.js +68 -0
- package/src/remote-workers/stdio.js +138 -0
- package/src/remote-workers/worker.js +304 -0
- package/src/remote.js +204 -0
- package/src/repo-config.js +201 -0
- package/src/repo-coordination.js +732 -0
- package/src/repo-tree.js +106 -0
- package/src/retention-policy.js +466 -0
- package/src/review-inbox.js +38 -0
- package/src/routing.js +49 -0
- package/src/run-analysis.js +111 -0
- package/src/run-cold-store.js +231 -0
- package/src/run-environment.js +15 -0
- package/src/run-history.js +91 -0
- package/src/run-log.js +160 -0
- package/src/run-permissions.js +110 -0
- package/src/run-projection.js +75 -0
- package/src/run-recovery.js +53 -0
- package/src/run-status.js +54 -0
- package/src/serve.js +138 -0
- package/src/server.js +2738 -0
- package/src/shared-access.js +103 -0
- package/src/shared-accounts.js +301 -0
- package/src/shared-api.js +239 -0
- package/src/shell.js +346 -0
- package/src/ship.js +344 -0
- package/src/slots.js +74 -0
- package/src/snapshot.js +388 -0
- package/src/source-context.js +17 -0
- package/src/sse-writer.js +70 -0
- package/src/ssh-projects/bootstrap.mjs +95 -0
- package/src/ssh-projects/config.js +45 -0
- package/src/ssh-projects/gateway.js +143 -0
- package/src/ssh-projects/manager.js +320 -0
- package/src/ssh-projects/team-autostart.mjs +93 -0
- package/src/ssh-projects/team-remove.mjs +69 -0
- package/src/ssh-projects/team-status.mjs +39 -0
- package/src/ssh-projects/transport.js +116 -0
- package/src/stats.js +139 -0
- package/src/store.js +544 -0
- package/src/stream.js +91 -0
- package/src/sweeps.js +241 -0
- package/src/tailscale.js +101 -0
- package/src/task-draft.js +23 -0
- package/src/team-evaluation.js +242 -0
- package/src/team-mailbox.js +160 -0
- package/src/team-memory.js +84 -0
- package/src/team-rerun-graph.js +15 -0
- package/src/team-reruns.js +130 -0
- package/src/team-scheduler.js +121 -0
- package/src/team-template-store.js +98 -0
- package/src/team-templates.js +117 -0
- package/src/team-tool-client.mjs +79 -0
- package/src/team-tools.js +176 -0
- package/src/teams.js +0 -0
- package/src/templates.js +62 -0
- package/src/transcript.js +134 -0
- package/src/verification-integrity.js +27 -0
- package/src/verification.js +183 -0
- package/src/watch.js +195 -0
- package/src/work-queue.js +47 -0
- package/src/workflow-control.js +83 -0
- package/src/workspace-api.js +207 -0
- package/src/workspace-changes.js +61 -0
- package/src/workspace.js +146 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
const LIMITS = { entries: 500, bodyChars: 2e4, claimsPerEntry: 12, claims: 1e3, comparedPairs: 5e4, findings: 100, snippetChars: 512, structuredChars: 8192 };
|
|
2
|
+
const ACTIONS = { use: ["use", true], utilize: ["use", true], employ: ["use", true], allow: ["allow", true], permit: ["allow", true], accept: ["allow", true], deny: ["allow", false], block: ["allow", false], disallow: ["allow", false], forbid: ["allow", false], reject: ["allow", false], require: ["require", true], enforce: ["require", true], encrypt: ["encrypt", true], cache: ["cache", true], memoize: ["cache", true], validate: ["validate", true], verify: ["validate", true], store: ["store", true], log: ["log", true], record: ["log", true], enable: ["enable", true], disable: ["enable", false] };
|
|
3
|
+
function normalized(value) {
|
|
4
|
+
return value.normalize("NFKC").toLowerCase().replace(/[`"']/g, "").replace(/time[ -]+to[ -]+live/g, "ttl").replace(/\s+/g, " ").trim().replace(/^(?:the|an?|all) /, "").replace(/\b(requests|records|responses|tokens|secrets|passwords|guests)\b/g, (word) => word.slice(0, -1));
|
|
5
|
+
}
|
|
6
|
+
const reference = (entry) => ({ id: entry.id, revision: entry.revision });
|
|
7
|
+
function unit(value) {
|
|
8
|
+
const text = String(value ?? "count").toLowerCase();
|
|
9
|
+
if (["count", "retry", "retries", "request", "requests"].includes(text)) return { name: "count", scale: 1 };
|
|
10
|
+
if (["ms", "millisecond", "milliseconds"].includes(text)) return { name: "ms", scale: 1 };
|
|
11
|
+
if (["s", "sec", "secs", "second", "seconds"].includes(text)) return { name: "ms", scale: 1e3 };
|
|
12
|
+
if (["m", "min", "mins", "minute", "minutes"].includes(text)) return { name: "ms", scale: 6e4 };
|
|
13
|
+
if (["h", "hour", "hours"].includes(text)) return { name: "ms", scale: 36e5 };
|
|
14
|
+
if (["b", "byte", "bytes"].includes(text)) return { name: "bytes", scale: 1 };
|
|
15
|
+
const sizes = { kb: 1e3, mb: 1e6, gb: 1e9, kib: 1024, mib: 1024 ** 2, gib: 1024 ** 3 };
|
|
16
|
+
return sizes[text] ? { name: "bytes", scale: sizes[text] } : void 0;
|
|
17
|
+
}
|
|
18
|
+
function numeric(min, max, rawUnit, minInclusive = true, maxInclusive = true) {
|
|
19
|
+
const dimension = unit(rawUnit);
|
|
20
|
+
if (!dimension || [min, max].some((number) => number !== null && (!Number.isFinite(number) || Math.abs(number * dimension.scale) > Number.MAX_SAFE_INTEGER)) || min !== null && max !== null && min > max) return;
|
|
21
|
+
return { kind: "number", min: min === null ? null : min * dimension.scale, max: max === null ? null : max * dimension.scale, minInclusive, maxInclusive, unit: dimension.name };
|
|
22
|
+
}
|
|
23
|
+
function structured(raw) {
|
|
24
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
25
|
+
const input = raw;
|
|
26
|
+
if (Object.keys(input).some((key) => !["subject", "predicate", "value", "min", "max", "unit"].includes(key)) || typeof input.subject !== "string" || !input.subject.trim() || input.subject.length > 120 || typeof input.predicate !== "string" || !/^[a-z][a-z-]{0,39}$/.test(input.predicate)) return;
|
|
27
|
+
let value;
|
|
28
|
+
if (input.value !== void 0 && (input.min !== void 0 || input.max !== void 0)) return;
|
|
29
|
+
if (typeof input.value === "boolean" && input.unit === void 0) value = { kind: "boolean", value: input.value };
|
|
30
|
+
else if (typeof input.value === "string" && input.value.trim() && input.value.length <= 120 && input.unit === void 0) value = { kind: "text", value: normalized(input.value) };
|
|
31
|
+
else if (typeof input.value === "number") value = numeric(input.value, input.value, input.unit);
|
|
32
|
+
else if (input.value === void 0 && (typeof input.min === "number" || typeof input.max === "number") && (input.min === void 0 || typeof input.min === "number") && (input.max === void 0 || typeof input.max === "number")) value = numeric(input.min ?? null, input.max ?? null, input.unit);
|
|
33
|
+
if (!value) return;
|
|
34
|
+
const action = ACTIONS[input.predicate];
|
|
35
|
+
if (value.kind === "boolean" && action && !action[1]) value = { ...value, value: !value.value };
|
|
36
|
+
return { subject: normalized(input.subject), predicate: action?.[0] ?? input.predicate, value };
|
|
37
|
+
}
|
|
38
|
+
function language(text) {
|
|
39
|
+
const sentence = text.trim().replace(/^[-*]\s+/, "").replace(/[.!]$/, "").trim();
|
|
40
|
+
if (sentence.length > 512 || new RegExp("\\b(if|unless|except|when|until|only|but|or|and|may|might|could)\\b|;|\\.\\s+\\p{L}", "u").test(sentence.toLowerCase())) return;
|
|
41
|
+
const setting = sentence.match(/^(?:set\s+)?(.{1,100}?)\s*(?:\s+(?:to|is|equals?)\s+|\s*[=:]\s*)(?:(at least|at most|no more than|no less than|>=|<=|>|<)\s*)?(-?\d+(?:\.\d+)?)\s*([a-z]+)?$/i);
|
|
42
|
+
if (setting) {
|
|
43
|
+
const number = Number(setting[3]), relation = setting[2]?.toLowerCase();
|
|
44
|
+
const lower = ["at least", "no less than", ">=", ">"].includes(relation ?? ""), upper = ["at most", "no more than", "<=", "<"].includes(relation ?? "");
|
|
45
|
+
const value = numeric(upper ? null : number, lower ? null : number, setting[4], relation !== ">", relation !== "<");
|
|
46
|
+
if (value) return { subject: normalized(setting[1]), predicate: "setting", value };
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const string = sentence.match(/^(?:set\s+)?(.{1,100}?)\s*(?:\s+(?:to|is|equals?)\s+|\s*[=:]\s*)["'`]([^"'`]{1,120})["'`]$/i);
|
|
50
|
+
if (string) return { subject: normalized(string[1]), predicate: "setting", value: { kind: "text", value: normalized(string[2]) } };
|
|
51
|
+
const policy = sentence.match(/^(?:(always|never|do not|don't|must not|should not|cannot)\s+)?(?:(?:must|should)\s+)?(use|utilize|employ|allow|permit|accept|deny|block|disallow|forbid|reject|require|enforce|encrypt|cache|memoize|validate|verify|store|log|record|enable|disable)\s+(.+)$/i);
|
|
52
|
+
if (policy) {
|
|
53
|
+
const action = ACTIONS[policy[2].toLowerCase()];
|
|
54
|
+
const negative = !!policy[1] && policy[1].toLowerCase() !== "always";
|
|
55
|
+
return { subject: normalized(policy[3]), predicate: action[0], value: { kind: "boolean", value: negative ? !action[1] : action[1] } };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function overlap(a, b) {
|
|
59
|
+
if (a.applicability.kind === "branch" && b.applicability.kind === "branch" && (a.applicability.branch !== b.applicability.branch || a.applicability.commit && b.applicability.commit && a.applicability.commit !== b.applicability.commit)) return false;
|
|
60
|
+
if (!a.paths.length || !b.paths.length) return true;
|
|
61
|
+
return a.paths.some((left) => b.paths.some((right) => {
|
|
62
|
+
const a2 = left.replace(/\/$/, ""), b2 = right.replace(/\/$/, "");
|
|
63
|
+
return a2 === b2 || a2.startsWith(`${b2}/`) || b2.startsWith(`${a2}/`);
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
function compare(a, b) {
|
|
67
|
+
if (a.kind !== b.kind) return;
|
|
68
|
+
if (a.kind === "boolean" && b.kind === "boolean" || a.kind === "text" && b.kind === "text") return a.value === b.value ? "equivalent" : "contradiction";
|
|
69
|
+
if (a.kind !== "number" || b.kind !== "number" || a.unit !== b.unit) return;
|
|
70
|
+
if (a.min === b.min && a.max === b.max && a.minInclusive === b.minInclusive && a.maxInclusive === b.maxInclusive) return "equivalent";
|
|
71
|
+
const disjoint = (left, right) => left.max !== null && right.min !== null && (left.max < right.min || left.max === right.min && (!left.maxInclusive || !right.minInclusive));
|
|
72
|
+
if (disjoint(a, b) || disjoint(b, a)) return "contradiction";
|
|
73
|
+
}
|
|
74
|
+
function analyzeMemoryClaims(input) {
|
|
75
|
+
const scoped = input.entries.filter((entry) => entry.scope === "project" && entry.repo === input.repo && entry.projectId === input.projectId && entry.status !== "deleted");
|
|
76
|
+
const entries = scoped.slice(0, LIMITS.entries);
|
|
77
|
+
const byId = new Map(entries.map((entry) => [entry.id, entry]));
|
|
78
|
+
const result = { claims: [], findings: [], inspectedEntries: entries.length, unsupported: 0, comparedPairs: 0, truncated: scoped.length > entries.length, limits: { ...LIMITS }, method: "Local claim grammar v1: explicit facts, policy polarity, setting values and normalized numeric intervals. Suggestions require human review; unsupported prose has no semantic correctness guarantee." };
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
if (result.claims.length >= LIMITS.claims) {
|
|
81
|
+
result.truncated = true;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
let count = 0;
|
|
85
|
+
const body = entry.body.slice(0, LIMITS.bodyChars);
|
|
86
|
+
if (body.length < entry.body.length) result.truncated = true;
|
|
87
|
+
const add = (claim, start2, end, method) => {
|
|
88
|
+
if (!claim) {
|
|
89
|
+
result.unsupported++;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (count >= LIMITS.claimsPerEntry || result.claims.length >= LIMITS.claims) {
|
|
93
|
+
result.truncated = true;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
count++;
|
|
97
|
+
result.claims.push({ ...claim, source: reference(entry), evidence: { start: start2, end, text: body.slice(start2, Math.min(end, start2 + LIMITS.snippetChars)), method } });
|
|
98
|
+
};
|
|
99
|
+
const fenced = [];
|
|
100
|
+
for (const match of body.matchAll(/```codeflow-claims[ \t]*\r?\n([\s\S]*?)\r?\n```/g)) {
|
|
101
|
+
const start2 = match.index, end = start2 + match[0].length;
|
|
102
|
+
fenced.push([start2, end]);
|
|
103
|
+
if (match[1].length > LIMITS.structuredChars) {
|
|
104
|
+
result.truncated = true;
|
|
105
|
+
result.unsupported++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const value = JSON.parse(match[1]);
|
|
110
|
+
if (!Array.isArray(value) || value.length > LIMITS.claimsPerEntry) {
|
|
111
|
+
if (Array.isArray(value)) result.truncated = true;
|
|
112
|
+
result.unsupported++;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
for (const fact of value) add(structured(fact), start2, end, "structured");
|
|
116
|
+
} catch {
|
|
117
|
+
result.unsupported++;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
let start = 0;
|
|
121
|
+
for (const line of body.split(/\n/)) {
|
|
122
|
+
const end = start + line.length;
|
|
123
|
+
if (line.trim() && !fenced.some(([a, b]) => start < b && end > a)) {
|
|
124
|
+
const claim = language(line);
|
|
125
|
+
add(claim, start, end, claim?.value.kind === "boolean" ? "policy" : "setting");
|
|
126
|
+
}
|
|
127
|
+
start = end + 1;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const groups = /* @__PURE__ */ new Map();
|
|
131
|
+
for (const claim of result.claims) {
|
|
132
|
+
const key = `${claim.subject}\0${claim.predicate}`;
|
|
133
|
+
const group = groups.get(key) ?? [];
|
|
134
|
+
group.push(claim);
|
|
135
|
+
groups.set(key, group);
|
|
136
|
+
}
|
|
137
|
+
outer: for (const claims of groups.values()) for (let a = 0; a < claims.length; a++) for (let b = a + 1; b < claims.length; b++) {
|
|
138
|
+
const left = claims[a], right = claims[b];
|
|
139
|
+
if (!overlap(byId.get(left.source.id), byId.get(right.source.id))) continue;
|
|
140
|
+
if (result.comparedPairs >= LIMITS.comparedPairs || result.findings.length >= LIMITS.findings) {
|
|
141
|
+
result.truncated = true;
|
|
142
|
+
break outer;
|
|
143
|
+
}
|
|
144
|
+
result.comparedPairs++;
|
|
145
|
+
const kind = compare(left.value, right.value);
|
|
146
|
+
if (!kind || left.source.id === right.source.id && kind !== "contradiction") continue;
|
|
147
|
+
result.findings.push({ a: left.source, b: right.source, kind, subject: left.subject, predicate: left.predicate, left, right, explanation: kind === "equivalent" ? "These supported claims normalize to the same meaning and overlapping applicability. Review their remaining context before combining entries." : "These supported claims impose opposite policy values, different explicit settings, or disjoint numeric constraints in overlapping applicability. Review both source revisions; this does not decide which is correct." });
|
|
148
|
+
}
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
export {
|
|
152
|
+
analyzeMemoryClaims
|
|
153
|
+
};
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { chmodSync, closeSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync } from "node:fs";
|
|
4
|
+
import { basename, dirname } from "node:path";
|
|
5
|
+
const digest = (value) => createHash("sha256").update(value).digest("hex");
|
|
6
|
+
const idFor = (entry, kind, key) => digest(JSON.stringify([entry.repo, entry.projectId ?? null, kind, key]));
|
|
7
|
+
class MemoryDatabase {
|
|
8
|
+
constructor(file, validate) {
|
|
9
|
+
this.file = file;
|
|
10
|
+
this.validate = validate;
|
|
11
|
+
this.legacy = file.replace(/\.sqlite$/, "") + ".json";
|
|
12
|
+
this.backup = `${this.legacy}.pre-sqlite`;
|
|
13
|
+
}
|
|
14
|
+
legacy;
|
|
15
|
+
backup;
|
|
16
|
+
withDatabase(action) {
|
|
17
|
+
let db;
|
|
18
|
+
try {
|
|
19
|
+
mkdirSync(dirname(this.file), { recursive: true });
|
|
20
|
+
const fresh = !existsSync(this.file);
|
|
21
|
+
if (fresh && existsSync(this.backup) && !existsSync(this.legacy)) throw new Error("Database is missing but a migration backup exists; restore it explicitly before continuing");
|
|
22
|
+
const legacyRaw = this.readLegacyIfPresent();
|
|
23
|
+
const legacy = legacyRaw === void 0 ? void 0 : this.validate(legacyRaw);
|
|
24
|
+
if (fresh) {
|
|
25
|
+
try {
|
|
26
|
+
closeSync(openSync(this.file, "wx", 384));
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error.code !== "EEXIST") throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (!lstatSync(this.file).isFile() || lstatSync(this.file).isSymbolicLink()) throw new Error("Memory database must be a regular file");
|
|
32
|
+
if ((lstatSync(this.file).mode & 511) !== 384) chmodSync(this.file, 384);
|
|
33
|
+
db = new DatabaseSync(this.file);
|
|
34
|
+
db.exec("PRAGMA busy_timeout=5000; PRAGMA foreign_keys=ON; PRAGMA secure_delete=ON; PRAGMA synchronous=FULL;");
|
|
35
|
+
const version = Number(db.prepare("PRAGMA user_version").get().user_version);
|
|
36
|
+
if (version !== 0 && version !== 1) throw new Error(`Unsupported memory database schema ${version}`);
|
|
37
|
+
if (!version) {
|
|
38
|
+
db.exec("PRAGMA journal_mode=DELETE; BEGIN IMMEDIATE");
|
|
39
|
+
try {
|
|
40
|
+
if (Number(db.prepare("PRAGMA user_version").get().user_version) === 0) {
|
|
41
|
+
if (db.prepare("SELECT name FROM sqlite_master WHERE name NOT LIKE 'sqlite_%'").get()) throw new Error("Unrecognized memory database");
|
|
42
|
+
db.exec(`
|
|
43
|
+
CREATE TABLE metadata (key TEXT PRIMARY KEY, value TEXT NOT NULL) STRICT;
|
|
44
|
+
CREATE TABLE memories (id TEXT PRIMARY KEY, repo TEXT NOT NULL, project_id TEXT,
|
|
45
|
+
scope TEXT NOT NULL, run_id TEXT, member_id TEXT, status TEXT NOT NULL,
|
|
46
|
+
revision INTEGER NOT NULL, document TEXT NOT NULL) STRICT;
|
|
47
|
+
CREATE INDEX memory_scope ON memories(repo, project_id, scope, run_id, member_id);
|
|
48
|
+
CREATE TABLE memory_terms (memory_id TEXT NOT NULL REFERENCES memories(id), term TEXT NOT NULL,
|
|
49
|
+
weight INTEGER NOT NULL, PRIMARY KEY(memory_id, term)) STRICT;
|
|
50
|
+
CREATE INDEX memory_term_lookup ON memory_terms(term);
|
|
51
|
+
CREATE TABLE entities (id TEXT PRIMARY KEY, kind TEXT NOT NULL, key TEXT NOT NULL) STRICT;
|
|
52
|
+
CREATE TABLE relationships (owner TEXT NOT NULL REFERENCES memories(id), revision INTEGER NOT NULL,
|
|
53
|
+
source TEXT NOT NULL REFERENCES entities(id), target TEXT NOT NULL REFERENCES entities(id),
|
|
54
|
+
relation TEXT NOT NULL, target_revision INTEGER,
|
|
55
|
+
PRIMARY KEY(owner, source, target, relation)) STRICT;
|
|
56
|
+
CREATE INDEX graph_source ON relationships(source);
|
|
57
|
+
CREATE INDEX graph_target ON relationships(target);
|
|
58
|
+
PRAGMA user_version=1;
|
|
59
|
+
`);
|
|
60
|
+
for (const entry of legacy?.entries ?? []) this.writeEntry(db, entry);
|
|
61
|
+
if (legacyRaw !== void 0) {
|
|
62
|
+
const imported = this.read(db);
|
|
63
|
+
if (JSON.stringify(imported.entries) !== JSON.stringify(legacy.entries)) throw new Error("Legacy memory verification failed");
|
|
64
|
+
db.prepare("INSERT INTO metadata VALUES('legacy_hash', ?)").run(digest(legacyRaw));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
db.exec("COMMIT");
|
|
68
|
+
} catch (error) {
|
|
69
|
+
db.exec("ROLLBACK");
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (existsSync(this.legacy)) {
|
|
74
|
+
db.exec("BEGIN IMMEDIATE");
|
|
75
|
+
try {
|
|
76
|
+
const current = this.readLegacyIfPresent();
|
|
77
|
+
if (current !== void 0) {
|
|
78
|
+
const hash = db.prepare("SELECT value FROM metadata WHERE key='legacy_hash'").get()?.value;
|
|
79
|
+
if (hash !== digest(current)) throw new Error("Legacy JSON differs from the imported database; resolve it explicitly before continuing");
|
|
80
|
+
if (existsSync(this.backup)) throw new Error("Both legacy JSON and migration backup exist; resolve them explicitly");
|
|
81
|
+
renameSync(this.legacy, this.backup);
|
|
82
|
+
chmodSync(this.backup, 384);
|
|
83
|
+
this.syncDirectory();
|
|
84
|
+
}
|
|
85
|
+
db.exec("COMMIT");
|
|
86
|
+
} catch (error) {
|
|
87
|
+
db.exec("ROLLBACK");
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return action(db);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (error.code?.startsWith("ERR_SQLITE")) {
|
|
94
|
+
throw new Error(`Memory store cannot be read or updated; no operation was acknowledged: ${error.message}`, { cause: error });
|
|
95
|
+
}
|
|
96
|
+
throw error;
|
|
97
|
+
} finally {
|
|
98
|
+
db?.close();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
read(db, query) {
|
|
102
|
+
const rows = query ? db.prepare("SELECT document FROM memories WHERE repo=? AND project_id IS ? AND (? IS NULL OR scope=?) ORDER BY rowid").all(query.repo, query.projectId ?? null, query.scope ?? null, query.scope ?? null) : db.prepare("SELECT document FROM memories ORDER BY rowid").all();
|
|
103
|
+
return this.validate(JSON.stringify({ schemaVersion: 1, entries: rows.map((row) => JSON.parse(String(row.document))) }));
|
|
104
|
+
}
|
|
105
|
+
change(db, fn) {
|
|
106
|
+
db.exec("BEGIN IMMEDIATE");
|
|
107
|
+
try {
|
|
108
|
+
const doc = this.read(db);
|
|
109
|
+
const before = new Map(doc.entries.map((entry) => [entry.id, JSON.stringify(entry)]));
|
|
110
|
+
const result = fn(doc);
|
|
111
|
+
const changed = doc.entries.filter((entry) => before.get(entry.id) !== JSON.stringify(entry));
|
|
112
|
+
if (changed.length) {
|
|
113
|
+
this.validate(JSON.stringify(doc));
|
|
114
|
+
this.cleanLegacyCopies();
|
|
115
|
+
db.exec("DELETE FROM metadata WHERE key='legacy_hash'");
|
|
116
|
+
for (const entry of changed) this.writeEntry(db, entry);
|
|
117
|
+
db.exec("DELETE FROM entities WHERE id NOT IN (SELECT source FROM relationships UNION SELECT target FROM relationships)");
|
|
118
|
+
}
|
|
119
|
+
db.exec("COMMIT");
|
|
120
|
+
return result;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
db.exec("ROLLBACK");
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** An indexed Unicode token search also works on Node builds without FTS5. */
|
|
127
|
+
search(db, query, eligible) {
|
|
128
|
+
const terms = [...new Set(tokenize(query.query ?? ""))].slice(0, 32);
|
|
129
|
+
if (!terms.length || !eligible.length) return /* @__PURE__ */ new Map();
|
|
130
|
+
return new Map(db.prepare(`SELECT memory_id, SUM(weight) AS relevance FROM memory_terms
|
|
131
|
+
WHERE term IN (SELECT value FROM json_each(?)) AND memory_id IN (SELECT value FROM json_each(?))
|
|
132
|
+
GROUP BY memory_id ORDER BY relevance DESC, memory_id LIMIT 512`).all(JSON.stringify(terms), JSON.stringify(eligible)).map((row) => [String(row.memory_id), Number(row.relevance)]));
|
|
133
|
+
}
|
|
134
|
+
graph(db, entries, limit = 1e3) {
|
|
135
|
+
const allowed = new Map(entries.map((entry) => [entry.id, entry]));
|
|
136
|
+
const memoryNodes = new Map(entries.map((entry) => [idFor(entry, "memory", entry.id), entry]));
|
|
137
|
+
const entities = /* @__PURE__ */ new Map();
|
|
138
|
+
const relationships = [];
|
|
139
|
+
let truncated = false;
|
|
140
|
+
const edges = db.prepare(`SELECT r.*, s.kind AS source_kind, s.key AS source_key, t.kind AS target_kind, t.key AS target_key
|
|
141
|
+
FROM relationships r JOIN entities s ON s.id=r.source JOIN entities t ON t.id=r.target WHERE r.owner=? ORDER BY r.relation, t.key`);
|
|
142
|
+
for (const entry of entries) {
|
|
143
|
+
for (const row of edges.all(entry.id)) {
|
|
144
|
+
const target = memoryNodes.get(String(row.target));
|
|
145
|
+
if (row.target_kind === "memory" && (!target || target.revision !== row.target_revision)) continue;
|
|
146
|
+
if (!allowed.has(String(row.owner)) || row.revision !== entry.revision) continue;
|
|
147
|
+
if (relationships.length >= limit) {
|
|
148
|
+
truncated = true;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
for (const prefix of ["source", "target"]) {
|
|
152
|
+
const id = String(row[prefix]), kind = row[`${prefix}_kind`], key = String(row[`${prefix}_key`]);
|
|
153
|
+
entities.set(id, { id, kind, key, label: kind === "memory" ? memoryNodes.get(id).title : key });
|
|
154
|
+
}
|
|
155
|
+
relationships.push({
|
|
156
|
+
from: String(row.source),
|
|
157
|
+
to: String(row.target),
|
|
158
|
+
relation: row.relation,
|
|
159
|
+
evidence: { id: entry.id, revision: entry.revision },
|
|
160
|
+
...row.target_revision == null ? {} : { targetRevision: Number(row.target_revision) }
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (truncated) break;
|
|
164
|
+
}
|
|
165
|
+
return { entities: [...entities.values()], relationships, truncated };
|
|
166
|
+
}
|
|
167
|
+
writeEntry(db, entry) {
|
|
168
|
+
db.prepare(`INSERT INTO memories VALUES(?,?,?,?,?,?,?,?,?) ON CONFLICT(id) DO UPDATE SET
|
|
169
|
+
repo=excluded.repo, project_id=excluded.project_id, scope=excluded.scope, run_id=excluded.run_id,
|
|
170
|
+
member_id=excluded.member_id, status=excluded.status, revision=excluded.revision, document=excluded.document`).run(entry.id, entry.repo, entry.projectId ?? null, entry.scope, entry.runId ?? null, entry.memberId ?? null, entry.status, entry.revision, JSON.stringify(entry));
|
|
171
|
+
db.prepare("DELETE FROM memory_terms WHERE memory_id=?").run(entry.id);
|
|
172
|
+
db.prepare("DELETE FROM relationships WHERE owner=?").run(entry.id);
|
|
173
|
+
if (entry.status === "deleted") return;
|
|
174
|
+
const weights = /* @__PURE__ */ new Map();
|
|
175
|
+
for (const [value, weight] of [[entry.title, 3], [entry.tags.join(" "), 2], [entry.body, 1]]) {
|
|
176
|
+
for (const term of new Set(tokenize(value))) weights.set(term, (weights.get(term) ?? 0) + weight);
|
|
177
|
+
}
|
|
178
|
+
const termInsert = db.prepare("INSERT INTO memory_terms VALUES(?,?,?)");
|
|
179
|
+
for (const [term, weight] of weights) termInsert.run(entry.id, term, weight);
|
|
180
|
+
const entity = (kind, key) => {
|
|
181
|
+
const id = idFor(entry, kind, key);
|
|
182
|
+
db.prepare("INSERT OR IGNORE INTO entities VALUES(?,?,?)").run(id, kind, key);
|
|
183
|
+
return id;
|
|
184
|
+
};
|
|
185
|
+
const source = entity("memory", entry.id);
|
|
186
|
+
const edge = (kind, key, relation, revision) => {
|
|
187
|
+
db.prepare("INSERT OR REPLACE INTO relationships VALUES(?,?,?,?,?,?)").run(entry.id, entry.revision, source, entity(kind, key), relation, revision ?? null);
|
|
188
|
+
};
|
|
189
|
+
for (const path of entry.paths) edge("file", path, "applies_to");
|
|
190
|
+
if (entry.runId) edge("run", entry.runId, "learned_from");
|
|
191
|
+
for (const ref of entry.sourceRefs) {
|
|
192
|
+
if (ref.startsWith("run:") && ref.length > 4) edge("run", ref.slice(4), "learned_from");
|
|
193
|
+
if (/^commit:[a-f0-9]{40,64}$/i.test(ref)) edge("commit", ref.slice(7), "learned_from");
|
|
194
|
+
}
|
|
195
|
+
if (entry.applicability.kind === "branch" && entry.applicability.commit) edge("commit", entry.applicability.commit, "applies_to");
|
|
196
|
+
if (entry.verification?.commit) edge("commit", entry.verification.commit, "verified_at");
|
|
197
|
+
const sources = /* @__PURE__ */ new Map();
|
|
198
|
+
for (const ref of entry.derivedFrom) sources.set(ref.id, Math.max(sources.get(ref.id) ?? 0, ref.revision));
|
|
199
|
+
for (const [id, revision] of sources) edge("memory", id, "derived_from", revision);
|
|
200
|
+
}
|
|
201
|
+
readLegacyIfPresent() {
|
|
202
|
+
try {
|
|
203
|
+
const info = lstatSync(this.legacy);
|
|
204
|
+
if (!info.isFile() || info.isSymbolicLink() || info.size > 32 * 1024 * 1024) throw new Error("Legacy memory must be a regular file under 32 MiB");
|
|
205
|
+
return readFileSync(this.legacy, "utf8");
|
|
206
|
+
} catch (error) {
|
|
207
|
+
if (error.code === "ENOENT") return void 0;
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
cleanLegacyCopies() {
|
|
212
|
+
rmSync(this.backup, { force: true });
|
|
213
|
+
for (const name of readdirSync(dirname(this.file))) {
|
|
214
|
+
for (const path of [this.file, this.legacy]) {
|
|
215
|
+
const prefix = `${basename(path)}.`;
|
|
216
|
+
if (name.startsWith(prefix) && /^[0-9a-f-]{36}\.tmp$/.test(name.slice(prefix.length))) rmSync(`${dirname(this.file)}/${name}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
this.syncDirectory();
|
|
220
|
+
}
|
|
221
|
+
syncDirectory() {
|
|
222
|
+
if (process.platform === "win32") return;
|
|
223
|
+
const fd = openSync(dirname(this.file), "r");
|
|
224
|
+
try {
|
|
225
|
+
fsyncSync(fd);
|
|
226
|
+
} finally {
|
|
227
|
+
closeSync(fd);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function tokenize(value) {
|
|
232
|
+
return value.normalize("NFKC").toLowerCase().match(/[\p{L}\p{N}_]{3,}/gu) ?? [];
|
|
233
|
+
}
|
|
234
|
+
export {
|
|
235
|
+
MemoryDatabase
|
|
236
|
+
};
|