@mnmal-ai/cortext 1.0.0-alpha.10 → 1.0.0-alpha.12
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/dist/bootstrap.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/kernel/layout.d.ts +14 -8
- package/dist/kernel/migrations/0001_baseline.d.ts +4 -0
- package/dist/kernel/migrations/0002_drop_engine_owned_indexes.d.ts +2 -1
- package/dist/kernel/substrate-contract.d.ts +1 -1
- package/dist/kernel-bootstrap.js +1 -1
- package/dist/kernel.js +1 -1
- package/dist/namespace.d.ts +2 -1
- package/dist/participants.d.ts +9 -1
- package/dist/{plugin-CujP_pvT.js → plugin-BtBVkZpZ.js} +21 -13
- package/dist/{src-DAeiNhDw.js → src-4GYqbw6S.js} +226 -143
- package/package.json +3 -3
package/dist/bootstrap.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as CORTEXT_NAMESPACE, O as ARCHITECTURE_NAMESPACE } from "./plugin-
|
|
1
|
+
import { D as isMonotonicAuth, T as DEFAULT_AUTH, t as createCortextPlugin } from "./src-4GYqbw6S.js";
|
|
2
|
+
import { A as CORTEXT_NAMESPACE, O as ARCHITECTURE_NAMESPACE } from "./plugin-BtBVkZpZ.js";
|
|
3
3
|
import "./kernel.js";
|
|
4
4
|
import { defineBootstrap } from "@mnmal-ai/substrate/sdk";
|
|
5
5
|
import { z } from "zod";
|
package/dist/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ import type { CortextPluginConfig } from './config.js';
|
|
|
26
26
|
export { buildAuthPolicies, DEFAULT_AUTH, isMonotonicAuth } from './auth-policy.js';
|
|
27
27
|
export { type CompactResult, registerCompactRoute, runCompact } from './compact.js';
|
|
28
28
|
export type { CortextAuthConfig, CortextAuthMode, CortextPluginConfig } from './config.js';
|
|
29
|
-
export { AGENT_MESSAGE_STATUS_VOCAB, AGENT_RULE_CATEGORY_VOCAB, AGENT_RULE_SEVERITY_ALIASES, AGENT_RULE_SEVERITY_VOCAB, ARCHITECTURE_TYPE_NAMES, buildArchitectureNamespace, buildClaudeNamespace, CLAUDE_TYPE_NAMES, DECISION_STATUS_ALIASES, DECISION_STATUS_VOCAB, HOST_INSTALL_CHECK_STATES, HOST_INSTALL_FRAGMENT_STATES, TODO_RESOLUTION_ALIASES, TODO_STATUS_ALIASES, TODO_STATUS_IMPLIES } from './namespace.js';
|
|
29
|
+
export { AGENT_MESSAGE_STATUS_VOCAB, AGENT_RULE_CATEGORY_VOCAB, AGENT_RULE_SEVERITY_ALIASES, AGENT_RULE_SEVERITY_VOCAB, ARCHITECTURE_TYPE_NAMES, buildArchitectureNamespace, buildClaudeNamespace, CLAUDE_TYPE_NAMES, DECISION_STATUS_ALIASES, DECISION_STATUS_VOCAB, HOST_INSTALL_CHECK_STATES, HOST_INSTALL_FRAGMENT_STATES, TASK_STATUS_VOCAB, TODO_RESOLUTION_ALIASES, TODO_STATUS_ALIASES, TODO_STATUS_IMPLIES } from './namespace.js';
|
|
30
30
|
export { registerOnboardRoute } from './onboard.js';
|
|
31
31
|
export { buildReleaseSurface, bumpClass, classifySurfaceChange, classSatisfies, type ReleaseClass, type ReleaseSurface, type SurfaceChange, type SurfaceField } from './release-surface.js';
|
|
32
32
|
export { type ClosedTodoText, inferTodoResolution, type TodoResolution } from './todo-resolution.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as
|
|
2
|
-
import { _ as AGENT_MESSAGE_STATUS_VOCAB } from "./plugin-
|
|
1
|
+
import { C as registerCompactRoute, D as isMonotonicAuth, E as buildAuthPolicies, S as buildClaudeNamespace, T as DEFAULT_AUTH, _ as TASK_STATUS_VOCAB, a as classSatisfies, b as TODO_STATUS_IMPLIES, c as AGENT_RULE_CATEGORY_VOCAB, d as ARCHITECTURE_TYPE_NAMES, f as CLAUDE_TYPE_NAMES, g as HOST_INSTALL_FRAGMENT_STATES, h as HOST_INSTALL_CHECK_STATES, i as bumpClass, l as AGENT_RULE_SEVERITY_ALIASES, m as DECISION_STATUS_VOCAB, n as inferTodoResolution, o as classifySurfaceChange, p as DECISION_STATUS_ALIASES, r as buildReleaseSurface, s as registerOnboardRoute, t as createCortextPlugin, u as AGENT_RULE_SEVERITY_VOCAB, v as TODO_RESOLUTION_ALIASES, w as runCompact, x as buildArchitectureNamespace, y as TODO_STATUS_ALIASES } from "./src-4GYqbw6S.js";
|
|
2
|
+
import { _ as AGENT_MESSAGE_STATUS_VOCAB } from "./plugin-BtBVkZpZ.js";
|
|
3
3
|
import "./kernel.js";
|
|
4
4
|
|
|
5
|
-
export { AGENT_MESSAGE_STATUS_VOCAB, AGENT_RULE_CATEGORY_VOCAB, AGENT_RULE_SEVERITY_ALIASES, AGENT_RULE_SEVERITY_VOCAB, ARCHITECTURE_TYPE_NAMES, CLAUDE_TYPE_NAMES, DECISION_STATUS_ALIASES, DECISION_STATUS_VOCAB, DEFAULT_AUTH, HOST_INSTALL_CHECK_STATES, HOST_INSTALL_FRAGMENT_STATES, TODO_RESOLUTION_ALIASES, TODO_STATUS_ALIASES, TODO_STATUS_IMPLIES, buildArchitectureNamespace, buildAuthPolicies, buildClaudeNamespace, buildReleaseSurface, bumpClass, classSatisfies, classifySurfaceChange, createCortextPlugin, inferTodoResolution, isMonotonicAuth, registerCompactRoute, registerOnboardRoute, runCompact };
|
|
5
|
+
export { AGENT_MESSAGE_STATUS_VOCAB, AGENT_RULE_CATEGORY_VOCAB, AGENT_RULE_SEVERITY_ALIASES, AGENT_RULE_SEVERITY_VOCAB, ARCHITECTURE_TYPE_NAMES, CLAUDE_TYPE_NAMES, DECISION_STATUS_ALIASES, DECISION_STATUS_VOCAB, DEFAULT_AUTH, HOST_INSTALL_CHECK_STATES, HOST_INSTALL_FRAGMENT_STATES, TASK_STATUS_VOCAB, TODO_RESOLUTION_ALIASES, TODO_STATUS_ALIASES, TODO_STATUS_IMPLIES, buildArchitectureNamespace, buildAuthPolicies, buildClaudeNamespace, buildReleaseSurface, bumpClass, classSatisfies, classifySurfaceChange, createCortextPlugin, inferTodoResolution, isMonotonicAuth, registerCompactRoute, registerOnboardRoute, runCompact };
|
package/dist/kernel/layout.d.ts
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* partitioned by namespace and type (`partitioned`)
|
|
2
|
+
* How the store keeps rows: one shared `rows` table (`legacy`), `rows`
|
|
3
|
+
* partitioned by namespace and type (`partitioned`), or one table per type
|
|
4
|
+
* listed in `substrate_type_tables` (`tables`, the only layout substrate
|
|
5
|
+
* 1.0.0-alpha.12 and later boots).
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* `
|
|
9
|
-
* indexes per
|
|
7
|
+
* cortext's raw DDL targets `rows` on `legacy` only. On `partitioned`,
|
|
8
|
+
* Postgres refuses a UNIQUE index that lacks the partition keys and clones
|
|
9
|
+
* every parent-level index and CHECK onto other plugins' partitions; on
|
|
10
|
+
* `tables` there is no `rows` at all. On both, the engine builds cortext's
|
|
11
|
+
* declared indexes, `indexed: true` fields and enum CHECKs per type.
|
|
10
12
|
*/
|
|
11
|
-
export type StoreLayout = 'legacy' | 'partitioned';
|
|
13
|
+
export type StoreLayout = 'legacy' | 'partitioned' | 'tables';
|
|
14
|
+
/** Whether `layout` has a shared `rows` table for cortext's raw DDL to target. */
|
|
15
|
+
export declare const hasRowsTable: (layout: unknown) => boolean;
|
|
12
16
|
/**
|
|
13
17
|
* The layout as the catalog reports it. Migrations read `ctx.layout` instead;
|
|
14
18
|
* this is for boot-time code, whose lifecycle context does not carry it.
|
|
19
|
+
* A schema with neither table reads as `legacy`, whose audit then reports
|
|
20
|
+
* every object missing.
|
|
15
21
|
*/
|
|
16
22
|
export declare function storeLayout(sql: unknown): Promise<StoreLayout>;
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
* body (see `CORTEXT_PROMOTED_COLUMNS`), their indexes, and the invariants
|
|
4
4
|
* on cortext's own types.
|
|
5
5
|
*
|
|
6
|
+
* Only a store with a shared `rows` table gets any of it; on per-type tables
|
|
7
|
+
* the engine builds the columns, indexes and enum CHECKs from cortext's
|
|
8
|
+
* namespace definition, so both `apply` and `rollback` return first.
|
|
9
|
+
*
|
|
6
10
|
* The kernel runner does not wrap `apply` in a transaction, so each statement
|
|
7
11
|
* commits on its own and a run that dies part-way re-applies cleanly: every
|
|
8
12
|
* statement is idempotent.
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* the engine serves the same reads: the derived recipient index for the
|
|
4
4
|
* inbox, and the `indexed: true` indexes on project and status.
|
|
5
5
|
*
|
|
6
|
-
* A partitioned `rows` never had them
|
|
6
|
+
* A partitioned `rows` never had them, and a per-type tables store has no
|
|
7
|
+
* `rows` at all. Idempotent, like 0001: the runner does
|
|
7
8
|
* not wrap `apply` in a transaction.
|
|
8
9
|
*/
|
|
9
10
|
import type { KernelMigration } from '@mnmal-ai/substrate/sdk';
|
|
@@ -16,4 +16,4 @@
|
|
|
16
16
|
* by reflex: widen it only after reading what the new major changed.
|
|
17
17
|
* `substrate-contract-parity.test.ts` holds the manifest to this literal.
|
|
18
18
|
*/
|
|
19
|
-
export declare const SUBSTRATE_CONTRACT_RANGE = ">=1.0.0-alpha.
|
|
19
|
+
export declare const SUBSTRATE_CONTRACT_RANGE = ">=1.0.0-alpha.12 <2.0.0";
|
package/dist/kernel-bootstrap.js
CHANGED
package/dist/kernel.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as CORTEXT_NAMESPACE, C as auditObjects, D as scopedNamespace, E as resolveCortextNamespaces, O as ARCHITECTURE_NAMESPACE, S as auditNamespaceCoverage, T as reportInvariantAudit, _ as AGENT_MESSAGE_STATUS_VOCAB, a as repairInvariants, b as TODO_RESOLUTION_VOCAB, c as ENGINE_OWNED_INDEXES, d as VOCAB_CONSTRAINTS, f as countDuplicateHostInstallKeys, g as rescopeVocabConstraint, h as rescopeKidUniqueIndex, i as repairIfRequested, k as CORTEXT_COORDINATOR_NAME, l as cortextDropEngineOwnedIndexesMigration, m as rescopeHostInstallKeyIndex, n as SUBSTRATE_CONTRACT_RANGE, o as repairOutcomeMessage, p as countDuplicateKids, r as REPAIR_ENV_VAR, s as repairRequested, t as createCortextKernelPlugin, u as cortextBaselineMigration, v as TODO_LIFECYCLE_VOCAB, w as findingMessage, x as INVARIANT_OBJECTS, y as TODO_PRIORITY_VOCAB } from "./plugin-
|
|
1
|
+
import { A as CORTEXT_NAMESPACE, C as auditObjects, D as scopedNamespace, E as resolveCortextNamespaces, O as ARCHITECTURE_NAMESPACE, S as auditNamespaceCoverage, T as reportInvariantAudit, _ as AGENT_MESSAGE_STATUS_VOCAB, a as repairInvariants, b as TODO_RESOLUTION_VOCAB, c as ENGINE_OWNED_INDEXES, d as VOCAB_CONSTRAINTS, f as countDuplicateHostInstallKeys, g as rescopeVocabConstraint, h as rescopeKidUniqueIndex, i as repairIfRequested, k as CORTEXT_COORDINATOR_NAME, l as cortextDropEngineOwnedIndexesMigration, m as rescopeHostInstallKeyIndex, n as SUBSTRATE_CONTRACT_RANGE, o as repairOutcomeMessage, p as countDuplicateKids, r as REPAIR_ENV_VAR, s as repairRequested, t as createCortextKernelPlugin, u as cortextBaselineMigration, v as TODO_LIFECYCLE_VOCAB, w as findingMessage, x as INVARIANT_OBJECTS, y as TODO_PRIORITY_VOCAB } from "./plugin-BtBVkZpZ.js";
|
|
2
2
|
|
|
3
3
|
export { AGENT_MESSAGE_STATUS_VOCAB, ARCHITECTURE_NAMESPACE, CORTEXT_COORDINATOR_NAME, CORTEXT_NAMESPACE, ENGINE_OWNED_INDEXES, INVARIANT_OBJECTS, REPAIR_ENV_VAR, SUBSTRATE_CONTRACT_RANGE, TODO_LIFECYCLE_VOCAB, TODO_PRIORITY_VOCAB, TODO_RESOLUTION_VOCAB, VOCAB_CONSTRAINTS, auditNamespaceCoverage, auditObjects, cortextBaselineMigration, cortextDropEngineOwnedIndexesMigration, countDuplicateHostInstallKeys, countDuplicateKids, createCortextKernelPlugin, findingMessage, repairIfRequested, repairInvariants, repairOutcomeMessage, repairRequested, reportInvariantAudit, rescopeHostInstallKeyIndex, rescopeKidUniqueIndex, rescopeVocabConstraint, resolveCortextNamespaces, scopedNamespace };
|
package/dist/namespace.d.ts
CHANGED
|
@@ -141,9 +141,10 @@ export { AGENT_MESSAGE_STATUS_VOCAB } from './kernel/index.js';
|
|
|
141
141
|
export declare const HOST_INSTALL_FRAGMENT_STATES: readonly ['current', 'stale', 'missing', 'undigested', 'uncomparable', 'source', 'absent'];
|
|
142
142
|
/** HostInstall's per-check verdicts: the worst finding each check produced. */
|
|
143
143
|
export declare const HOST_INSTALL_CHECK_STATES: readonly ['ok', 'warn', 'unknown'];
|
|
144
|
+
export declare const TASK_STATUS_VOCAB: readonly ['active', 'paused', 'done'];
|
|
144
145
|
export declare function buildClaudeNamespace(ns: string): NamespaceDefinition;
|
|
145
146
|
/** The canonical ordered list of type names registered by this namespace. */
|
|
146
|
-
export declare const CLAUDE_TYPE_NAMES: readonly ['SessionLog', 'Decision', 'Todo', 'AgentRule', 'CouncilSession', 'CouncilAdvisor', 'Memory', 'AnchorIntent', 'Concept', 'ConceptLink', 'Skill', 'AgentIdentity', 'AgentMessage', 'HostInstall'];
|
|
147
|
+
export declare const CLAUDE_TYPE_NAMES: readonly ['SessionLog', 'Decision', 'Todo', 'AgentRule', 'CouncilSession', 'CouncilAdvisor', 'Memory', 'AnchorIntent', 'Concept', 'ConceptLink', 'Skill', 'AgentIdentity', 'AgentMessage', 'HostInstall', 'Task', 'ExecutionCheckpoint'];
|
|
147
148
|
/**
|
|
148
149
|
* Architecture namespace — Claude-lineage code-review findings. Kept separate
|
|
149
150
|
* from the claude context namespace so lifecycle (compaction, retention,
|
package/dist/participants.d.ts
CHANGED
|
@@ -11,7 +11,15 @@
|
|
|
11
11
|
* An operator is an identity granted `admin` (or `*`) in this namespace.
|
|
12
12
|
*/
|
|
13
13
|
import type { NamespaceInstallContext, RegistryEntryResolver, RequestContext } from '@mnmal-ai/substrate/sdk';
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Whether `identity` is an operator: it holds `admin` (or `*`) in `namespace`,
|
|
16
|
+
* or the deployment admin capability (`SUBSTRATE_ADMIN_CAPABILITY`, default
|
|
17
|
+
* `admin`) in `_system`, as substrate's own admin gate reads it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isOperatorIdentity(identity: RequestContext['identity'], namespace: string, env?: {
|
|
20
|
+
SUBSTRATE_ADMIN_CAPABILITY?: string;
|
|
21
|
+
}): boolean;
|
|
22
|
+
/** Whether the caller is an operator in the namespace it is calling. */
|
|
15
23
|
export declare function isOperator(ctx: RequestContext): boolean;
|
|
16
24
|
/**
|
|
17
25
|
* AgentMessage update/delete: the sender, the recipient, or an operator.
|
|
@@ -98,15 +98,22 @@ const CORTEXT_PROMOTED_COLUMNS = {
|
|
|
98
98
|
|
|
99
99
|
//#endregion
|
|
100
100
|
//#region src/kernel/layout.ts
|
|
101
|
+
/** Whether `layout` has a shared `rows` table for cortext's raw DDL to target. */
|
|
102
|
+
const hasRowsTable = (layout) => layout === "legacy" || layout === "partitioned";
|
|
101
103
|
/**
|
|
102
104
|
* The layout as the catalog reports it. Migrations read `ctx.layout` instead;
|
|
103
105
|
* this is for boot-time code, whose lifecycle context does not carry it.
|
|
106
|
+
* A schema with neither table reads as `legacy`, whose audit then reports
|
|
107
|
+
* every object missing.
|
|
104
108
|
*/
|
|
105
109
|
async function storeLayout(sql) {
|
|
106
110
|
const [row] = await sql`
|
|
107
|
-
SELECT c.relkind
|
|
111
|
+
SELECT (SELECT c.relkind FROM pg_class c WHERE c.oid = to_regclass('rows')) AS kind,
|
|
112
|
+
to_regclass('substrate_type_tables') IS NOT NULL AS tables
|
|
108
113
|
`;
|
|
109
|
-
|
|
114
|
+
if (row?.kind === "p") return "partitioned";
|
|
115
|
+
if (row?.kind == null && row?.tables === true) return "tables";
|
|
116
|
+
return "legacy";
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
//#endregion
|
|
@@ -360,11 +367,11 @@ function findingMessage(finding) {
|
|
|
360
367
|
* structured channel this belongs in.
|
|
361
368
|
*/
|
|
362
369
|
async function reportInvariantAudit(ctx, warn = console.warn) {
|
|
363
|
-
let findings = [...auditNamespaceCoverage(ctx.coordinatorNamespaces, ctx.coordinatorNamespacesByPlugin)];
|
|
364
|
-
const target = ctx.coordinatorNamespaces.includes("cortext") ? CORTEXT_NAMESPACE : resolveCortextNamespaces(ctx.coordinatorNamespacesByPlugin)?.[0] ?? "cortext";
|
|
365
370
|
const catalogRead = ctx.sql !== null && ctx.sql !== void 0;
|
|
366
|
-
let objectsChecked = 0;
|
|
367
371
|
const layout = catalogRead ? await storeLayout(ctx.sql).catch(() => "legacy") : null;
|
|
372
|
+
let findings = layout !== null && layout !== "legacy" ? [] : auditNamespaceCoverage(ctx.coordinatorNamespaces, ctx.coordinatorNamespacesByPlugin);
|
|
373
|
+
const target = ctx.coordinatorNamespaces.includes("cortext") ? CORTEXT_NAMESPACE : resolveCortextNamespaces(ctx.coordinatorNamespacesByPlugin)?.[0] ?? "cortext";
|
|
374
|
+
let objectsChecked = 0;
|
|
368
375
|
if (catalogRead && layout === "legacy") try {
|
|
369
376
|
const result = await auditObjects(ctx.sql, target);
|
|
370
377
|
if (target !== "cortext" && result.findings.length === 0) findings = findings.filter((f) => f.kind !== "namespace_drifted");
|
|
@@ -602,7 +609,7 @@ const cortextBaselineMigration = {
|
|
|
602
609
|
id: "0001_baseline",
|
|
603
610
|
apply: async (ctx) => {
|
|
604
611
|
const { sql, metric } = ctx;
|
|
605
|
-
if (sql === null) return;
|
|
612
|
+
if (sql === null || !hasRowsTable(ctx.layout)) return;
|
|
606
613
|
const s = sql;
|
|
607
614
|
await s.unsafe(`
|
|
608
615
|
ALTER TABLE rows
|
|
@@ -620,8 +627,8 @@ const cortextBaselineMigration = {
|
|
|
620
627
|
await rescopeKidUniqueIndex(s, CORTEXT_NAMESPACE);
|
|
621
628
|
await rescopeHostInstallKeyIndex(s, CORTEXT_NAMESPACE);
|
|
622
629
|
},
|
|
623
|
-
rollback: async ({ sql }) => {
|
|
624
|
-
if (sql === null) return;
|
|
630
|
+
rollback: async ({ sql, layout }) => {
|
|
631
|
+
if (sql === null || !hasRowsTable(layout)) return;
|
|
625
632
|
const s = sql;
|
|
626
633
|
for (const { constraint } of VOCAB_CONSTRAINTS) await s.unsafe(`ALTER TABLE rows DROP CONSTRAINT IF EXISTS ${constraint}`);
|
|
627
634
|
await s.unsafe("DROP INDEX IF EXISTS rows_agent_identity_kid_unique, rows_host_install_key_unique");
|
|
@@ -641,7 +648,7 @@ const ENGINE_OWNED_INDEXES = [
|
|
|
641
648
|
const cortextDropEngineOwnedIndexesMigration = {
|
|
642
649
|
id: "0002_drop_engine_owned_indexes",
|
|
643
650
|
apply: async ({ sql, layout }) => {
|
|
644
|
-
if (sql === null || layout
|
|
651
|
+
if (sql === null || layout !== "legacy") return;
|
|
645
652
|
await sql.unsafe(`DROP INDEX IF EXISTS ${ENGINE_OWNED_INDEXES.join(", ")}`);
|
|
646
653
|
},
|
|
647
654
|
rollback: async () => {}
|
|
@@ -740,9 +747,10 @@ async function repairIfRequested(ctx, warn = console.warn) {
|
|
|
740
747
|
ctx.metric("cortext_kernel.repair_declined", { reason: "target_unknown" });
|
|
741
748
|
return null;
|
|
742
749
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
750
|
+
const layout = await storeLayout(ctx.sql).catch(() => "legacy");
|
|
751
|
+
if (layout !== "legacy") {
|
|
752
|
+
warn(`[cortext-kernel] ${REPAIR_ENV_VAR} is set, but the store layout is ${layout}: the engine owns cortext's indexes and CHECKs there, and there are no objects on rows to repair. Nothing to do.`);
|
|
753
|
+
ctx.metric("cortext_kernel.repair_declined", { reason: layout });
|
|
746
754
|
return null;
|
|
747
755
|
}
|
|
748
756
|
warn(`[cortext-kernel] ${REPAIR_ENV_VAR} is set — re-scoping kernel invariants to '${ctx.namespace}'. This takes ACCESS EXCLUSIVE on rows and briefly blocks writes.` + (ctx.namespace === "cortext" ? " Target matches the built-in scope literal, so this is a no-op rebuild." : ""));
|
|
@@ -789,7 +797,7 @@ async function repairIfRequested(ctx, warn = console.warn) {
|
|
|
789
797
|
* by reflex: widen it only after reading what the new major changed.
|
|
790
798
|
* `substrate-contract-parity.test.ts` holds the manifest to this literal.
|
|
791
799
|
*/
|
|
792
|
-
const SUBSTRATE_CONTRACT_RANGE = ">=1.0.0-alpha.
|
|
800
|
+
const SUBSTRATE_CONTRACT_RANGE = ">=1.0.0-alpha.12 <2.0.0";
|
|
793
801
|
|
|
794
802
|
//#endregion
|
|
795
803
|
//#region src/kernel/plugin.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as CORTEXT_NAMESPACE, O as ARCHITECTURE_NAMESPACE, _ as AGENT_MESSAGE_STATUS_VOCAB, b as TODO_RESOLUTION_VOCAB, j as CORTEXT_PROMOTED_COLUMNS, k as CORTEXT_COORDINATOR_NAME, n as SUBSTRATE_CONTRACT_RANGE, v as TODO_LIFECYCLE_VOCAB, y as TODO_PRIORITY_VOCAB } from "./plugin-
|
|
2
|
-
import { JSON_HEADERS, RESERVED_AUTOCRUD_FIELDS, ResolverFrameError, and, createResolverWithEdges, defineNamespace, deleteResolver, denyAll, denyVerbs, field, formatQualifiedKey, getResolver, grantOf, grantedIn, jsonResponse, listResolver, makeCtx, or, queryAll, requireIdentity, resolveEntityRow, resolveIdentity, selectAll, updateResolver } from "@mnmal-ai/substrate/sdk";
|
|
1
|
+
import { A as CORTEXT_NAMESPACE, O as ARCHITECTURE_NAMESPACE, _ as AGENT_MESSAGE_STATUS_VOCAB, b as TODO_RESOLUTION_VOCAB, j as CORTEXT_PROMOTED_COLUMNS, k as CORTEXT_COORDINATOR_NAME, n as SUBSTRATE_CONTRACT_RANGE, v as TODO_LIFECYCLE_VOCAB, y as TODO_PRIORITY_VOCAB } from "./plugin-BtBVkZpZ.js";
|
|
2
|
+
import { JSON_HEADERS, RESERVED_AUTOCRUD_FIELDS, ResolverFrameError, SYSTEM_NAMESPACE, and, createResolverWithEdges, defineNamespace, deleteResolver, denyAll, denyVerbs, field, formatQualifiedKey, getResolver, grantOf, grantedIn, jsonResponse, listResolver, makeCtx, or, queryAll, requireIdentity, resolveEntityRow, resolveIdentity, selectAll, updateResolver } from "@mnmal-ai/substrate/sdk";
|
|
3
3
|
|
|
4
4
|
//#region src/auth-policy.ts
|
|
5
5
|
/**
|
|
@@ -40,6 +40,154 @@ function buildAuthPolicies(auth) {
|
|
|
40
40
|
return [];
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/participants.ts
|
|
45
|
+
/**
|
|
46
|
+
* Whether `identity` is an operator: it holds `admin` (or `*`) in `namespace`,
|
|
47
|
+
* or the deployment admin capability (`SUBSTRATE_ADMIN_CAPABILITY`, default
|
|
48
|
+
* `admin`) in `_system`, as substrate's own admin gate reads it.
|
|
49
|
+
*/
|
|
50
|
+
function isOperatorIdentity(identity, namespace, env = process.env) {
|
|
51
|
+
const grant = grantOf(identity);
|
|
52
|
+
const caps = grantedIn(grant, namespace);
|
|
53
|
+
if (caps.includes("admin") || caps.includes("*")) return true;
|
|
54
|
+
const adminCap = env.SUBSTRATE_ADMIN_CAPABILITY || "admin";
|
|
55
|
+
const system = grantedIn(grant, SYSTEM_NAMESPACE);
|
|
56
|
+
return system.includes(adminCap) || system.includes("*");
|
|
57
|
+
}
|
|
58
|
+
/** Whether the caller is an operator in the namespace it is calling. */
|
|
59
|
+
function isOperator(ctx) {
|
|
60
|
+
return isOperatorIdentity(ctx.identity, ctx.namespace);
|
|
61
|
+
}
|
|
62
|
+
function rowId(args) {
|
|
63
|
+
const raw = args.params?.id;
|
|
64
|
+
return typeof raw === "string" || typeof raw === "number" ? raw : void 0;
|
|
65
|
+
}
|
|
66
|
+
function callerKid(ctx) {
|
|
67
|
+
const id = ctx.identity?.id;
|
|
68
|
+
return typeof id === "string" && id !== "" ? id : null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* AgentMessage update/delete: the sender, the recipient, or an operator.
|
|
72
|
+
* The sender is the key that signed the send (`_metadata.createdBy`); the
|
|
73
|
+
* recipient is the kid on the addressed directory row. Archiving someone
|
|
74
|
+
* else's mail hides it from their inbox, and deleting it destroys it, so
|
|
75
|
+
* neither is a bystander's call.
|
|
76
|
+
*/
|
|
77
|
+
function messageParticipantsOnly(installCtx, name, inner) {
|
|
78
|
+
return async (ctx, args) => {
|
|
79
|
+
const id = rowId(args);
|
|
80
|
+
if (id === void 0 || isOperator(ctx)) return inner(ctx, args);
|
|
81
|
+
const ds = args.dataset ?? installCtx.dataset;
|
|
82
|
+
const message = await resolveEntityRow(ds, "AgentMessage", id);
|
|
83
|
+
if (message === null) return inner(ctx, args);
|
|
84
|
+
const caller = callerKid(ctx);
|
|
85
|
+
const sender = message._metadata?.createdBy?.id;
|
|
86
|
+
let recipient = null;
|
|
87
|
+
if (typeof message.recipient === "string" && message.recipient !== "") {
|
|
88
|
+
const { rows } = await ds.read("AgentIdentity", { id: message.recipient });
|
|
89
|
+
recipient = rows[0]?.kid ?? null;
|
|
90
|
+
}
|
|
91
|
+
if (caller !== null && (caller === sender || caller === recipient)) return inner(ctx, args);
|
|
92
|
+
throw new ResolverFrameError("not_participant", `${name}: only this message's sender ('${String(sender ?? "unknown")}') or recipient ('${String(recipient ?? "unknown")}'), or an operator, may change or delete it`, { id: message.id });
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Whether `host` names the machine in `kid`'s `@host` part, ignoring case.
|
|
97
|
+
* A kid with no `@` names no machine, so it may carry no host.
|
|
98
|
+
*/
|
|
99
|
+
function hostMatchesKid(host, kid) {
|
|
100
|
+
if (host === void 0 || host === null) return true;
|
|
101
|
+
if (typeof kid !== "string" || !kid.includes("@")) return false;
|
|
102
|
+
return String(host).toLowerCase() === kid.slice(kid.lastIndexOf("@") + 1).toLowerCase();
|
|
103
|
+
}
|
|
104
|
+
function refuseForeignHost(name, host, kid) {
|
|
105
|
+
if (hostMatchesKid(host, kid)) return;
|
|
106
|
+
throw new ResolverFrameError("operator_only", `${name}: 'host' must be the machine in the kid ('${String(kid)}'); only an operator may set another`, { field: "host" });
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* AgentIdentity create: a non-operator may not file a row whose `host` is a
|
|
110
|
+
* machine other than the one its kid names. The fleet audit trusts `host`
|
|
111
|
+
* (and `roaming` on that host) when it decides who may write a host's
|
|
112
|
+
* HostInstall rows.
|
|
113
|
+
*/
|
|
114
|
+
function identityCreateGuard(installCtx, name, inner) {
|
|
115
|
+
return async (ctx, args) => {
|
|
116
|
+
const params = args.params ?? {};
|
|
117
|
+
if (!isOperator(ctx)) refuseForeignHost(name, params.host, params.kid);
|
|
118
|
+
if (typeof params.kid === "string") {
|
|
119
|
+
const { rows } = await (args.dataset ?? installCtx.dataset).read("AgentIdentity", {
|
|
120
|
+
where: { kid: { eq: params.kid } },
|
|
121
|
+
limit: 1
|
|
122
|
+
});
|
|
123
|
+
if (rows.length > 0) throw new ResolverFrameError("duplicate_kid", `${name}: an AgentIdentity row for '${params.kid}' already exists; update it instead`, { field: "kid" });
|
|
124
|
+
}
|
|
125
|
+
return inner(ctx, args);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* AgentIdentity update: the identity itself (the caller's kid is the row's
|
|
130
|
+
* kid) or an operator. `kid` is the row's identity, so changing it is an
|
|
131
|
+
* operator act even on your own row: otherwise a row could be renamed onto a
|
|
132
|
+
* kid whose mail it would then receive. `host` must stay the kid's machine,
|
|
133
|
+
* and reactivating a retired row is an operator act.
|
|
134
|
+
*/
|
|
135
|
+
function identitySelfOnly(installCtx, name, inner) {
|
|
136
|
+
return async (ctx, args) => {
|
|
137
|
+
const id = rowId(args);
|
|
138
|
+
if (id === void 0 || isOperator(ctx)) return inner(ctx, args);
|
|
139
|
+
const ds = args.dataset ?? installCtx.dataset;
|
|
140
|
+
const row = await resolveEntityRow(ds, "AgentIdentity", id);
|
|
141
|
+
if (row === null) return inner(ctx, args);
|
|
142
|
+
const params = args.params ?? {};
|
|
143
|
+
if ("kid" in params && params.kid !== row.kid) throw new ResolverFrameError("operator_only", `${name}: 'kid' is the row's identity and only an operator may change it`, { field: "kid" });
|
|
144
|
+
if (callerKid(ctx) === row.kid) {
|
|
145
|
+
if ("host" in params || "roaming" in params || "project" in params) refuseForeignHost(name, "host" in params ? params.host : row.host, row.kid);
|
|
146
|
+
if (params.active === true && row.active === false) throw new ResolverFrameError("operator_only", `${name}: a retired identity may only be reactivated by an operator`, { field: "active" });
|
|
147
|
+
return inner(ctx, args);
|
|
148
|
+
}
|
|
149
|
+
throw new ResolverFrameError("not_self", `${name}: only '${String(row.kid)}' itself, or an operator, may edit this identity row`, { id: row.id });
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Todo create/update: `resolution` says why a CLOSED Todo stopped, so a live
|
|
154
|
+
* Todo carries none. A resolution with a live resulting status is refused,
|
|
155
|
+
* and moving a Todo back to a live status clears the one it had.
|
|
156
|
+
*/
|
|
157
|
+
function todoResolutionGuard(installCtx, name, inner, statusAliases) {
|
|
158
|
+
const canon = (v) => {
|
|
159
|
+
const s = String(v).trim().toLowerCase();
|
|
160
|
+
return statusAliases[s] ?? s;
|
|
161
|
+
};
|
|
162
|
+
return async (ctx, args) => {
|
|
163
|
+
const params = { ...args.params ?? {} };
|
|
164
|
+
const statusGiven = params.status !== void 0 && params.status !== null;
|
|
165
|
+
let resulting = statusGiven ? canon(params.status) : null;
|
|
166
|
+
let current = null;
|
|
167
|
+
const id = rowId(args);
|
|
168
|
+
if (id !== void 0) {
|
|
169
|
+
const ds = args.dataset ?? installCtx.dataset;
|
|
170
|
+
current = await resolveEntityRow(ds, "Todo", id);
|
|
171
|
+
if (resulting === null && current !== null) resulting = canon(current.status);
|
|
172
|
+
}
|
|
173
|
+
if (resulting === null) resulting = "open";
|
|
174
|
+
if (resulting === "closed") return inner(ctx, args);
|
|
175
|
+
if (params.resolution !== void 0 && params.resolution !== null) throw new ResolverFrameError("resolution_on_live_todo", `${name}: 'resolution' is for closed Todos; this one would be '${resulting}'`, { field: "resolution" });
|
|
176
|
+
if (statusGiven && current !== null && current.resolution != null && !("resolution" in params)) params.resolution = null;
|
|
177
|
+
return inner(ctx, {
|
|
178
|
+
...args,
|
|
179
|
+
params
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/** AgentIdentity delete: operators only. Mail hangs off these rows. */
|
|
184
|
+
function operatorOnly(name, inner) {
|
|
185
|
+
return async (ctx, args) => {
|
|
186
|
+
if (isOperator(ctx)) return inner(ctx, args);
|
|
187
|
+
throw new ResolverFrameError("operator_only", `${name}: only an operator may delete an identity row; retire it with active: false instead`, {});
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
43
191
|
//#endregion
|
|
44
192
|
//#region src/compact.ts
|
|
45
193
|
/**
|
|
@@ -173,10 +321,9 @@ function parseBody(req) {
|
|
|
173
321
|
function registerCompactRoute(router, ctx, retention, namespace, auth = DEFAULT_AUTH) {
|
|
174
322
|
router.post("/compact", async (req) => {
|
|
175
323
|
if (auth.writes === "identity") {
|
|
176
|
-
|
|
177
|
-
if (!caps.includes("admin") && !caps.includes("*")) return jsonResponse(403, {
|
|
324
|
+
if (!isOperatorIdentity(req.identity ?? null, namespace)) return jsonResponse(403, {
|
|
178
325
|
code: "operator_only",
|
|
179
|
-
message: `POST /compact archives across every project and needs an operator (admin in '${namespace}')`
|
|
326
|
+
message: `POST /compact archives across every project and needs an operator (admin in '${namespace}', or the deployment admin)`
|
|
180
327
|
});
|
|
181
328
|
}
|
|
182
329
|
const parsed = parseBody(req);
|
|
@@ -258,142 +405,6 @@ function reportUncoveredNamespaces(cfg, metric, warn = console.warn) {
|
|
|
258
405
|
return uncovered;
|
|
259
406
|
}
|
|
260
407
|
|
|
261
|
-
//#endregion
|
|
262
|
-
//#region src/participants.ts
|
|
263
|
-
/** Whether the caller holds `admin` (or `*`) in the namespace it is calling. */
|
|
264
|
-
function isOperator(ctx) {
|
|
265
|
-
const caps = grantedIn(grantOf(ctx.identity), ctx.namespace);
|
|
266
|
-
return caps.includes("admin") || caps.includes("*");
|
|
267
|
-
}
|
|
268
|
-
function rowId(args) {
|
|
269
|
-
const raw = args.params?.id;
|
|
270
|
-
return typeof raw === "string" || typeof raw === "number" ? raw : void 0;
|
|
271
|
-
}
|
|
272
|
-
function callerKid(ctx) {
|
|
273
|
-
const id = ctx.identity?.id;
|
|
274
|
-
return typeof id === "string" && id !== "" ? id : null;
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* AgentMessage update/delete: the sender, the recipient, or an operator.
|
|
278
|
-
* The sender is the key that signed the send (`_metadata.createdBy`); the
|
|
279
|
-
* recipient is the kid on the addressed directory row. Archiving someone
|
|
280
|
-
* else's mail hides it from their inbox, and deleting it destroys it, so
|
|
281
|
-
* neither is a bystander's call.
|
|
282
|
-
*/
|
|
283
|
-
function messageParticipantsOnly(installCtx, name, inner) {
|
|
284
|
-
return async (ctx, args) => {
|
|
285
|
-
const id = rowId(args);
|
|
286
|
-
if (id === void 0 || isOperator(ctx)) return inner(ctx, args);
|
|
287
|
-
const ds = args.dataset ?? installCtx.dataset;
|
|
288
|
-
const message = await resolveEntityRow(ds, "AgentMessage", id);
|
|
289
|
-
if (message === null) return inner(ctx, args);
|
|
290
|
-
const caller = callerKid(ctx);
|
|
291
|
-
const sender = message._metadata?.createdBy?.id;
|
|
292
|
-
let recipient = null;
|
|
293
|
-
if (typeof message.recipient === "string" && message.recipient !== "") {
|
|
294
|
-
const { rows } = await ds.read("AgentIdentity", { id: message.recipient });
|
|
295
|
-
recipient = rows[0]?.kid ?? null;
|
|
296
|
-
}
|
|
297
|
-
if (caller !== null && (caller === sender || caller === recipient)) return inner(ctx, args);
|
|
298
|
-
throw new ResolverFrameError("not_participant", `${name}: only this message's sender ('${String(sender ?? "unknown")}') or recipient ('${String(recipient ?? "unknown")}'), or an operator, may change or delete it`, { id: message.id });
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Whether `host` names the machine in `kid`'s `@host` part, ignoring case.
|
|
303
|
-
* A kid with no `@` names no machine, so it may carry no host.
|
|
304
|
-
*/
|
|
305
|
-
function hostMatchesKid(host, kid) {
|
|
306
|
-
if (host === void 0 || host === null) return true;
|
|
307
|
-
if (typeof kid !== "string" || !kid.includes("@")) return false;
|
|
308
|
-
return String(host).toLowerCase() === kid.slice(kid.lastIndexOf("@") + 1).toLowerCase();
|
|
309
|
-
}
|
|
310
|
-
function refuseForeignHost(name, host, kid) {
|
|
311
|
-
if (hostMatchesKid(host, kid)) return;
|
|
312
|
-
throw new ResolverFrameError("operator_only", `${name}: 'host' must be the machine in the kid ('${String(kid)}'); only an operator may set another`, { field: "host" });
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* AgentIdentity create: a non-operator may not file a row whose `host` is a
|
|
316
|
-
* machine other than the one its kid names. The fleet audit trusts `host`
|
|
317
|
-
* (and `roaming` on that host) when it decides who may write a host's
|
|
318
|
-
* HostInstall rows.
|
|
319
|
-
*/
|
|
320
|
-
function identityCreateGuard(installCtx, name, inner) {
|
|
321
|
-
return async (ctx, args) => {
|
|
322
|
-
const params = args.params ?? {};
|
|
323
|
-
if (!isOperator(ctx)) refuseForeignHost(name, params.host, params.kid);
|
|
324
|
-
if (typeof params.kid === "string") {
|
|
325
|
-
const { rows } = await (args.dataset ?? installCtx.dataset).read("AgentIdentity", {
|
|
326
|
-
where: { kid: { eq: params.kid } },
|
|
327
|
-
limit: 1
|
|
328
|
-
});
|
|
329
|
-
if (rows.length > 0) throw new ResolverFrameError("duplicate_kid", `${name}: an AgentIdentity row for '${params.kid}' already exists; update it instead`, { field: "kid" });
|
|
330
|
-
}
|
|
331
|
-
return inner(ctx, args);
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* AgentIdentity update: the identity itself (the caller's kid is the row's
|
|
336
|
-
* kid) or an operator. `kid` is the row's identity, so changing it is an
|
|
337
|
-
* operator act even on your own row: otherwise a row could be renamed onto a
|
|
338
|
-
* kid whose mail it would then receive. `host` must stay the kid's machine,
|
|
339
|
-
* and reactivating a retired row is an operator act.
|
|
340
|
-
*/
|
|
341
|
-
function identitySelfOnly(installCtx, name, inner) {
|
|
342
|
-
return async (ctx, args) => {
|
|
343
|
-
const id = rowId(args);
|
|
344
|
-
if (id === void 0 || isOperator(ctx)) return inner(ctx, args);
|
|
345
|
-
const ds = args.dataset ?? installCtx.dataset;
|
|
346
|
-
const row = await resolveEntityRow(ds, "AgentIdentity", id);
|
|
347
|
-
if (row === null) return inner(ctx, args);
|
|
348
|
-
const params = args.params ?? {};
|
|
349
|
-
if ("kid" in params && params.kid !== row.kid) throw new ResolverFrameError("operator_only", `${name}: 'kid' is the row's identity and only an operator may change it`, { field: "kid" });
|
|
350
|
-
if (callerKid(ctx) === row.kid) {
|
|
351
|
-
if ("host" in params || "roaming" in params || "project" in params) refuseForeignHost(name, "host" in params ? params.host : row.host, row.kid);
|
|
352
|
-
if (params.active === true && row.active === false) throw new ResolverFrameError("operator_only", `${name}: a retired identity may only be reactivated by an operator`, { field: "active" });
|
|
353
|
-
return inner(ctx, args);
|
|
354
|
-
}
|
|
355
|
-
throw new ResolverFrameError("not_self", `${name}: only '${String(row.kid)}' itself, or an operator, may edit this identity row`, { id: row.id });
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Todo create/update: `resolution` says why a CLOSED Todo stopped, so a live
|
|
360
|
-
* Todo carries none. A resolution with a live resulting status is refused,
|
|
361
|
-
* and moving a Todo back to a live status clears the one it had.
|
|
362
|
-
*/
|
|
363
|
-
function todoResolutionGuard(installCtx, name, inner, statusAliases) {
|
|
364
|
-
const canon = (v) => {
|
|
365
|
-
const s = String(v).trim().toLowerCase();
|
|
366
|
-
return statusAliases[s] ?? s;
|
|
367
|
-
};
|
|
368
|
-
return async (ctx, args) => {
|
|
369
|
-
const params = { ...args.params ?? {} };
|
|
370
|
-
const statusGiven = params.status !== void 0 && params.status !== null;
|
|
371
|
-
let resulting = statusGiven ? canon(params.status) : null;
|
|
372
|
-
let current = null;
|
|
373
|
-
const id = rowId(args);
|
|
374
|
-
if (id !== void 0) {
|
|
375
|
-
const ds = args.dataset ?? installCtx.dataset;
|
|
376
|
-
current = await resolveEntityRow(ds, "Todo", id);
|
|
377
|
-
if (resulting === null && current !== null) resulting = canon(current.status);
|
|
378
|
-
}
|
|
379
|
-
if (resulting === null) resulting = "open";
|
|
380
|
-
if (resulting === "closed") return inner(ctx, args);
|
|
381
|
-
if (params.resolution !== void 0 && params.resolution !== null) throw new ResolverFrameError("resolution_on_live_todo", `${name}: 'resolution' is for closed Todos; this one would be '${resulting}'`, { field: "resolution" });
|
|
382
|
-
if (statusGiven && current !== null && current.resolution != null && !("resolution" in params)) params.resolution = null;
|
|
383
|
-
return inner(ctx, {
|
|
384
|
-
...args,
|
|
385
|
-
params
|
|
386
|
-
});
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
/** AgentIdentity delete: operators only. Mail hangs off these rows. */
|
|
390
|
-
function operatorOnly(name, inner) {
|
|
391
|
-
return async (ctx, args) => {
|
|
392
|
-
if (isOperator(ctx)) return inner(ctx, args);
|
|
393
|
-
throw new ResolverFrameError("operator_only", `${name}: only an operator may delete an identity row; retire it with active: false instead`, {});
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
|
|
397
408
|
//#endregion
|
|
398
409
|
//#region src/mailbox.ts
|
|
399
410
|
const TYPE = "AgentMessage";
|
|
@@ -932,6 +943,11 @@ const HOST_INSTALL_CHECK_STATES = [
|
|
|
932
943
|
"warn",
|
|
933
944
|
"unknown"
|
|
934
945
|
];
|
|
946
|
+
const TASK_STATUS_VOCAB = [
|
|
947
|
+
"active",
|
|
948
|
+
"paused",
|
|
949
|
+
"done"
|
|
950
|
+
];
|
|
935
951
|
const CONCEPT_NAME_TTL_MS = 6e4;
|
|
936
952
|
/** Long-form length past which `embed.source` appends the long form to the synopsis. */
|
|
937
953
|
const LONGFORM_EMBED_CHARS = 1200;
|
|
@@ -1349,6 +1365,45 @@ function buildClaudeNamespace(ns) {
|
|
|
1349
1365
|
where: "live",
|
|
1350
1366
|
name: "rows_host_install_key_unique"
|
|
1351
1367
|
}]
|
|
1368
|
+
},
|
|
1369
|
+
Task: { fields: {
|
|
1370
|
+
title: field.string(),
|
|
1371
|
+
status: field.enum(TASK_STATUS_VOCAB, { default: "active" }),
|
|
1372
|
+
todo: field.crossRef(ns, "Todo", {
|
|
1373
|
+
nullable: true,
|
|
1374
|
+
onDelete: "set_null"
|
|
1375
|
+
}),
|
|
1376
|
+
archived: field.boolean({ default: false }),
|
|
1377
|
+
project: field.string({
|
|
1378
|
+
nullable: true,
|
|
1379
|
+
indexed: true
|
|
1380
|
+
})
|
|
1381
|
+
} },
|
|
1382
|
+
ExecutionCheckpoint: {
|
|
1383
|
+
fields: {
|
|
1384
|
+
task: field.crossRef(ns, "Task", { onDelete: "cascade" }),
|
|
1385
|
+
revision: field.number(),
|
|
1386
|
+
completed: field.string({ indexed: false }),
|
|
1387
|
+
nextAction: field.string({ indexed: false }),
|
|
1388
|
+
blockers: field.string({
|
|
1389
|
+
nullable: true,
|
|
1390
|
+
indexed: false
|
|
1391
|
+
}),
|
|
1392
|
+
refs: field.list(field.string(), { nullable: true }),
|
|
1393
|
+
project: field.string({
|
|
1394
|
+
nullable: true,
|
|
1395
|
+
indexed: true
|
|
1396
|
+
})
|
|
1397
|
+
},
|
|
1398
|
+
indexes: [{
|
|
1399
|
+
fields: ["task", {
|
|
1400
|
+
field: "revision",
|
|
1401
|
+
dir: "desc"
|
|
1402
|
+
}],
|
|
1403
|
+
unique: true,
|
|
1404
|
+
where: "live",
|
|
1405
|
+
name: "rows_execution_checkpoint_task_revision_unique"
|
|
1406
|
+
}]
|
|
1352
1407
|
}
|
|
1353
1408
|
};
|
|
1354
1409
|
const definition = defineNamespace(ns, types, {
|
|
@@ -1393,7 +1448,9 @@ const CLAUDE_TYPE_NAMES = [
|
|
|
1393
1448
|
"Skill",
|
|
1394
1449
|
"AgentIdentity",
|
|
1395
1450
|
"AgentMessage",
|
|
1396
|
-
"HostInstall"
|
|
1451
|
+
"HostInstall",
|
|
1452
|
+
"Task",
|
|
1453
|
+
"ExecutionCheckpoint"
|
|
1397
1454
|
];
|
|
1398
1455
|
/**
|
|
1399
1456
|
* Architecture namespace — Claude-lineage code-review findings. Kept separate
|
|
@@ -1690,6 +1747,32 @@ function buildBody(namespace, retention) {
|
|
|
1690
1747
|
settingsEnv: "json — allowlisted non-secret switch values, plus whether the identity keys are set (nullable)"
|
|
1691
1748
|
},
|
|
1692
1749
|
writeWhen: ["Written by the cold-start of every session with an agent identity; agents read it, they do not write it"]
|
|
1750
|
+
},
|
|
1751
|
+
Task: {
|
|
1752
|
+
purpose: "A unit of work in flight: the stable identity a fresh session resumes by after a reset, independent of session id. Not a backlog ticket; link one via `todo`",
|
|
1753
|
+
fields: {
|
|
1754
|
+
id: "auto-generated",
|
|
1755
|
+
title: "what the work is",
|
|
1756
|
+
status: `${vocabUnion(TASK_STATUS_VOCAB)} (default active)`,
|
|
1757
|
+
todo: "UUID crossRef to the Todo this work implements, set_null on delete (nullable)",
|
|
1758
|
+
archived: "true if retired",
|
|
1759
|
+
project: "repo basename; null = cross-cutting"
|
|
1760
|
+
},
|
|
1761
|
+
writeWhen: ["When starting work that may outlive the context window", "Flip status to done when the work is finished, paused when set aside"]
|
|
1762
|
+
},
|
|
1763
|
+
ExecutionCheckpoint: {
|
|
1764
|
+
purpose: "One acknowledged step of a Task: what was completed and the next action. The latest is the Task's highest revision",
|
|
1765
|
+
fields: {
|
|
1766
|
+
id: "auto-generated",
|
|
1767
|
+
task: "UUID crossRef to the Task, cascade on delete",
|
|
1768
|
+
revision: "one higher than the Task's latest; unique per Task, so a second write at the same revision is refused",
|
|
1769
|
+
completed: "what this step finished (not indexed)",
|
|
1770
|
+
nextAction: "the exact next operation (not indexed)",
|
|
1771
|
+
blockers: "what stands in the way (not indexed, nullable)",
|
|
1772
|
+
refs: "file paths, #shortIds, PR or commit references (nullable)",
|
|
1773
|
+
project: "repo basename; null = cross-cutting"
|
|
1774
|
+
},
|
|
1775
|
+
writeWhen: ["After a completed subtask, a blocker, or before a reset; point at files and rows, never retell them", "Treat the step as done only once the write is acknowledged; on a refused revision, re-read the latest and retry"]
|
|
1693
1776
|
}
|
|
1694
1777
|
},
|
|
1695
1778
|
mutations: buildMutations(),
|
|
@@ -2069,4 +2152,4 @@ function createCortextPlugin(cfg) {
|
|
|
2069
2152
|
}
|
|
2070
2153
|
|
|
2071
2154
|
//#endregion
|
|
2072
|
-
export {
|
|
2155
|
+
export { registerCompactRoute as C, isMonotonicAuth as D, buildAuthPolicies as E, buildClaudeNamespace as S, DEFAULT_AUTH as T, TASK_STATUS_VOCAB as _, classSatisfies as a, TODO_STATUS_IMPLIES as b, AGENT_RULE_CATEGORY_VOCAB as c, ARCHITECTURE_TYPE_NAMES as d, CLAUDE_TYPE_NAMES as f, HOST_INSTALL_FRAGMENT_STATES as g, HOST_INSTALL_CHECK_STATES as h, bumpClass as i, AGENT_RULE_SEVERITY_ALIASES as l, DECISION_STATUS_VOCAB as m, inferTodoResolution as n, classifySurfaceChange as o, DECISION_STATUS_ALIASES as p, buildReleaseSurface as r, registerOnboardRoute as s, createCortextPlugin as t, AGENT_RULE_SEVERITY_VOCAB as u, TODO_RESOLUTION_ALIASES as v, runCompact as w, buildArchitectureNamespace as x, TODO_STATUS_ALIASES as y };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mnmal-ai/cortext",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.12",
|
|
4
4
|
"description": "Cortext: the agent-neutral coordination context store for Substrate (Todos, Decisions, SessionLogs, AgentRules, Memory, Concepts, Councils, …) plus architecture findings. One package carrying two plugins: the coordinator that registers the namespaces, and the kernel plugin that owns their Postgres invariants.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -62,10 +62,10 @@
|
|
|
62
62
|
"zod": "^4.6.5"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
|
-
"@mnmal-ai/substrate": ">=1.0.0-alpha.
|
|
65
|
+
"@mnmal-ai/substrate": ">=1.0.0-alpha.12 <2.0.0"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@mnmal-ai/substrate": "1.0.0-alpha.
|
|
68
|
+
"@mnmal-ai/substrate": "1.0.0-alpha.12",
|
|
69
69
|
"@types/node": "^26.6.4",
|
|
70
70
|
"tsdown": "^0.23.0",
|
|
71
71
|
"typescript": "^7.0.2",
|