@sentropic/h2a-cli 0.74.0 → 0.78.0
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/index.d.ts +1 -36
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -40
- package/dist/index.js.map +1 -1
- package/package.json +4 -15
- package/dist/bin.d.ts +0 -3
- package/dist/bin.d.ts.map +0 -1
- package/dist/bin.js +0 -83
- package/dist/bin.js.map +0 -1
- package/dist/cli-contract.d.ts +0 -62
- package/dist/cli-contract.d.ts.map +0 -1
- package/dist/cli-contract.js +0 -729
- package/dist/cli-contract.js.map +0 -1
- package/dist/cli.d.ts +0 -254
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -4530
- package/dist/cli.js.map +0 -1
- package/dist/hosts/agy.d.ts +0 -14
- package/dist/hosts/agy.d.ts.map +0 -1
- package/dist/hosts/agy.js +0 -47
- package/dist/hosts/agy.js.map +0 -1
- package/dist/hosts/claude.d.ts +0 -12
- package/dist/hosts/claude.d.ts.map +0 -1
- package/dist/hosts/claude.js +0 -46
- package/dist/hosts/claude.js.map +0 -1
- package/dist/hosts/codex.d.ts +0 -65
- package/dist/hosts/codex.d.ts.map +0 -1
- package/dist/hosts/codex.js +0 -45
- package/dist/hosts/codex.js.map +0 -1
- package/dist/hosts/gemini.d.ts +0 -12
- package/dist/hosts/gemini.d.ts.map +0 -1
- package/dist/hosts/gemini.js +0 -46
- package/dist/hosts/gemini.js.map +0 -1
- package/dist/hosts/plugin.d.ts +0 -151
- package/dist/hosts/plugin.d.ts.map +0 -1
- package/dist/hosts/plugin.js +0 -188
- package/dist/hosts/plugin.js.map +0 -1
- package/dist/mcp.d.ts +0 -2
- package/dist/mcp.d.ts.map +0 -1
- package/dist/mcp.js +0 -32
- package/dist/mcp.js.map +0 -1
- package/dist/runtime/blockage/index.d.ts +0 -3
- package/dist/runtime/blockage/index.d.ts.map +0 -1
- package/dist/runtime/blockage/index.js +0 -3
- package/dist/runtime/blockage/index.js.map +0 -1
- package/dist/runtime/blockage/notifiers.d.ts +0 -52
- package/dist/runtime/blockage/notifiers.d.ts.map +0 -1
- package/dist/runtime/blockage/notifiers.js +0 -74
- package/dist/runtime/blockage/notifiers.js.map +0 -1
- package/dist/runtime/blockage/registry.d.ts +0 -30
- package/dist/runtime/blockage/registry.d.ts.map +0 -1
- package/dist/runtime/blockage/registry.js +0 -86
- package/dist/runtime/blockage/registry.js.map +0 -1
- package/dist/runtime/deploy/k8s-sidecar.d.ts +0 -92
- package/dist/runtime/deploy/k8s-sidecar.d.ts.map +0 -1
- package/dist/runtime/deploy/k8s-sidecar.js +0 -166
- package/dist/runtime/deploy/k8s-sidecar.js.map +0 -1
- package/dist/runtime/deploy/k8s-tenant.d.ts +0 -86
- package/dist/runtime/deploy/k8s-tenant.d.ts.map +0 -1
- package/dist/runtime/deploy/k8s-tenant.js +0 -255
- package/dist/runtime/deploy/k8s-tenant.js.map +0 -1
- package/dist/runtime/drive/inbox-wake.d.ts +0 -36
- package/dist/runtime/drive/inbox-wake.d.ts.map +0 -1
- package/dist/runtime/drive/inbox-wake.js +0 -50
- package/dist/runtime/drive/inbox-wake.js.map +0 -1
- package/dist/runtime/drive/index.d.ts +0 -173
- package/dist/runtime/drive/index.d.ts.map +0 -1
- package/dist/runtime/drive/index.js +0 -503
- package/dist/runtime/drive/index.js.map +0 -1
- package/dist/runtime/drumbeat/deciders.d.ts +0 -32
- package/dist/runtime/drumbeat/deciders.d.ts.map +0 -1
- package/dist/runtime/drumbeat/deciders.js +0 -65
- package/dist/runtime/drumbeat/deciders.js.map +0 -1
- package/dist/runtime/drumbeat/decisions.d.ts +0 -19
- package/dist/runtime/drumbeat/decisions.d.ts.map +0 -1
- package/dist/runtime/drumbeat/decisions.js +0 -34
- package/dist/runtime/drumbeat/decisions.js.map +0 -1
- package/dist/runtime/drumbeat/inbox-relance.d.ts +0 -29
- package/dist/runtime/drumbeat/inbox-relance.d.ts.map +0 -1
- package/dist/runtime/drumbeat/inbox-relance.js +0 -101
- package/dist/runtime/drumbeat/inbox-relance.js.map +0 -1
- package/dist/runtime/drumbeat/index.d.ts +0 -8
- package/dist/runtime/drumbeat/index.d.ts.map +0 -1
- package/dist/runtime/drumbeat/index.js +0 -8
- package/dist/runtime/drumbeat/index.js.map +0 -1
- package/dist/runtime/drumbeat/registry.d.ts +0 -60
- package/dist/runtime/drumbeat/registry.d.ts.map +0 -1
- package/dist/runtime/drumbeat/registry.js +0 -115
- package/dist/runtime/drumbeat/registry.js.map +0 -1
- package/dist/runtime/drumbeat/relaunchers.d.ts +0 -111
- package/dist/runtime/drumbeat/relaunchers.d.ts.map +0 -1
- package/dist/runtime/drumbeat/relaunchers.js +0 -242
- package/dist/runtime/drumbeat/relaunchers.js.map +0 -1
- package/dist/runtime/drumbeat/scan.d.ts +0 -36
- package/dist/runtime/drumbeat/scan.d.ts.map +0 -1
- package/dist/runtime/drumbeat/scan.js +0 -41
- package/dist/runtime/drumbeat/scan.js.map +0 -1
- package/dist/runtime/drumbeat/watch.d.ts +0 -74
- package/dist/runtime/drumbeat/watch.d.ts.map +0 -1
- package/dist/runtime/drumbeat/watch.js +0 -216
- package/dist/runtime/drumbeat/watch.js.map +0 -1
- package/dist/runtime/escalation/index.d.ts +0 -2
- package/dist/runtime/escalation/index.d.ts.map +0 -1
- package/dist/runtime/escalation/index.js +0 -2
- package/dist/runtime/escalation/index.js.map +0 -1
- package/dist/runtime/escalation/registry.d.ts +0 -44
- package/dist/runtime/escalation/registry.d.ts.map +0 -1
- package/dist/runtime/escalation/registry.js +0 -79
- package/dist/runtime/escalation/registry.js.map +0 -1
- package/dist/runtime/governance/claims.d.ts +0 -43
- package/dist/runtime/governance/claims.d.ts.map +0 -1
- package/dist/runtime/governance/claims.js +0 -93
- package/dist/runtime/governance/claims.js.map +0 -1
- package/dist/runtime/governance/conductor.d.ts +0 -79
- package/dist/runtime/governance/conductor.d.ts.map +0 -1
- package/dist/runtime/governance/conductor.js +0 -106
- package/dist/runtime/governance/conductor.js.map +0 -1
- package/dist/runtime/governance/index.d.ts +0 -5
- package/dist/runtime/governance/index.d.ts.map +0 -1
- package/dist/runtime/governance/index.js +0 -5
- package/dist/runtime/governance/index.js.map +0 -1
- package/dist/runtime/governance/launch-check.d.ts +0 -93
- package/dist/runtime/governance/launch-check.d.ts.map +0 -1
- package/dist/runtime/governance/launch-check.js +0 -113
- package/dist/runtime/governance/launch-check.js.map +0 -1
- package/dist/runtime/governance/spawns.d.ts +0 -50
- package/dist/runtime/governance/spawns.d.ts.map +0 -1
- package/dist/runtime/governance/spawns.js +0 -91
- package/dist/runtime/governance/spawns.js.map +0 -1
- package/dist/runtime/identity/bindings.d.ts +0 -84
- package/dist/runtime/identity/bindings.d.ts.map +0 -1
- package/dist/runtime/identity/bindings.js +0 -112
- package/dist/runtime/identity/bindings.js.map +0 -1
- package/dist/runtime/identity/index.d.ts +0 -11
- package/dist/runtime/identity/index.d.ts.map +0 -1
- package/dist/runtime/identity/index.js +0 -6
- package/dist/runtime/identity/index.js.map +0 -1
- package/dist/runtime/identity/live.d.ts +0 -26
- package/dist/runtime/identity/live.d.ts.map +0 -1
- package/dist/runtime/identity/live.js +0 -232
- package/dist/runtime/identity/live.js.map +0 -1
- package/dist/runtime/identity/migration.d.ts +0 -55
- package/dist/runtime/identity/migration.d.ts.map +0 -1
- package/dist/runtime/identity/migration.js +0 -100
- package/dist/runtime/identity/migration.js.map +0 -1
- package/dist/runtime/identity/readers.d.ts +0 -42
- package/dist/runtime/identity/readers.d.ts.map +0 -1
- package/dist/runtime/identity/readers.js +0 -248
- package/dist/runtime/identity/readers.js.map +0 -1
- package/dist/runtime/identity/resolver.d.ts +0 -50
- package/dist/runtime/identity/resolver.d.ts.map +0 -1
- package/dist/runtime/identity/resolver.js +0 -61
- package/dist/runtime/identity/resolver.js.map +0 -1
- package/dist/runtime/identity/workspace-id.d.ts +0 -43
- package/dist/runtime/identity/workspace-id.d.ts.map +0 -1
- package/dist/runtime/identity/workspace-id.js +0 -138
- package/dist/runtime/identity/workspace-id.js.map +0 -1
- package/dist/runtime/local-files/index.d.ts +0 -8
- package/dist/runtime/local-files/index.d.ts.map +0 -1
- package/dist/runtime/local-files/index.js +0 -8
- package/dist/runtime/local-files/index.js.map +0 -1
- package/dist/runtime/local-files/lease.d.ts +0 -62
- package/dist/runtime/local-files/lease.d.ts.map +0 -1
- package/dist/runtime/local-files/lease.js +0 -249
- package/dist/runtime/local-files/lease.js.map +0 -1
- package/dist/runtime/local-files/locks.d.ts +0 -43
- package/dist/runtime/local-files/locks.d.ts.map +0 -1
- package/dist/runtime/local-files/locks.js +0 -197
- package/dist/runtime/local-files/locks.js.map +0 -1
- package/dist/runtime/local-files/migrate.d.ts +0 -31
- package/dist/runtime/local-files/migrate.d.ts.map +0 -1
- package/dist/runtime/local-files/migrate.js +0 -104
- package/dist/runtime/local-files/migrate.js.map +0 -1
- package/dist/runtime/local-files/paths.d.ts +0 -146
- package/dist/runtime/local-files/paths.d.ts.map +0 -1
- package/dist/runtime/local-files/paths.js +0 -233
- package/dist/runtime/local-files/paths.js.map +0 -1
- package/dist/runtime/local-files/presence.d.ts +0 -80
- package/dist/runtime/local-files/presence.d.ts.map +0 -1
- package/dist/runtime/local-files/presence.js +0 -250
- package/dist/runtime/local-files/presence.js.map +0 -1
- package/dist/runtime/local-files/schema.d.ts +0 -47
- package/dist/runtime/local-files/schema.d.ts.map +0 -1
- package/dist/runtime/local-files/schema.js +0 -77
- package/dist/runtime/local-files/schema.js.map +0 -1
- package/dist/runtime/local-files/store.d.ts +0 -132
- package/dist/runtime/local-files/store.d.ts.map +0 -1
- package/dist/runtime/local-files/store.js +0 -997
- package/dist/runtime/local-files/store.js.map +0 -1
- package/dist/runtime/mcp/handlers.d.ts +0 -196
- package/dist/runtime/mcp/handlers.d.ts.map +0 -1
- package/dist/runtime/mcp/handlers.js +0 -1025
- package/dist/runtime/mcp/handlers.js.map +0 -1
- package/dist/runtime/mcp/index.d.ts +0 -7
- package/dist/runtime/mcp/index.d.ts.map +0 -1
- package/dist/runtime/mcp/index.js +0 -7
- package/dist/runtime/mcp/index.js.map +0 -1
- package/dist/runtime/mcp/notifications.d.ts +0 -47
- package/dist/runtime/mcp/notifications.d.ts.map +0 -1
- package/dist/runtime/mcp/notifications.js +0 -204
- package/dist/runtime/mcp/notifications.js.map +0 -1
- package/dist/runtime/mcp/server.d.ts +0 -45
- package/dist/runtime/mcp/server.d.ts.map +0 -1
- package/dist/runtime/mcp/server.js +0 -94
- package/dist/runtime/mcp/server.js.map +0 -1
- package/dist/runtime/mcp/sessions.d.ts +0 -68
- package/dist/runtime/mcp/sessions.d.ts.map +0 -1
- package/dist/runtime/mcp/sessions.js +0 -152
- package/dist/runtime/mcp/sessions.js.map +0 -1
- package/dist/runtime/mcp/stdio.d.ts +0 -72
- package/dist/runtime/mcp/stdio.d.ts.map +0 -1
- package/dist/runtime/mcp/stdio.js +0 -274
- package/dist/runtime/mcp/stdio.js.map +0 -1
- package/dist/runtime/mcp/tools.d.ts +0 -19
- package/dist/runtime/mcp/tools.d.ts.map +0 -1
- package/dist/runtime/mcp/tools.js +0 -525
- package/dist/runtime/mcp/tools.js.map +0 -1
- package/dist/runtime/mcp-http/app.d.ts +0 -33
- package/dist/runtime/mcp-http/app.d.ts.map +0 -1
- package/dist/runtime/mcp-http/app.js +0 -162
- package/dist/runtime/mcp-http/app.js.map +0 -1
- package/dist/runtime/mcp-http/hosted-mcp-server.d.ts +0 -14
- package/dist/runtime/mcp-http/hosted-mcp-server.d.ts.map +0 -1
- package/dist/runtime/mcp-http/hosted-mcp-server.js +0 -38
- package/dist/runtime/mcp-http/hosted-mcp-server.js.map +0 -1
- package/dist/runtime/mcp-http/index.d.ts +0 -18
- package/dist/runtime/mcp-http/index.d.ts.map +0 -1
- package/dist/runtime/mcp-http/index.js +0 -18
- package/dist/runtime/mcp-http/index.js.map +0 -1
- package/dist/runtime/mcp-http/main.d.ts +0 -2
- package/dist/runtime/mcp-http/main.d.ts.map +0 -1
- package/dist/runtime/mcp-http/main.js +0 -14
- package/dist/runtime/mcp-http/main.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/broker-login.d.ts +0 -50
- package/dist/runtime/mcp-http/oauth/broker-login.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/broker-login.js +0 -41
- package/dist/runtime/mcp-http/oauth/broker-login.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/broker-routes.d.ts +0 -45
- package/dist/runtime/mcp-http/oauth/broker-routes.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/broker-routes.js +0 -54
- package/dist/runtime/mcp-http/oauth/broker-routes.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/config.d.ts +0 -49
- package/dist/runtime/mcp-http/oauth/config.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/config.js +0 -69
- package/dist/runtime/mcp-http/oauth/config.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/crypto.d.ts +0 -14
- package/dist/runtime/mcp-http/oauth/crypto.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/crypto.js +0 -29
- package/dist/runtime/mcp-http/oauth/crypto.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/file-store.d.ts +0 -48
- package/dist/runtime/mcp-http/oauth/file-store.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/file-store.js +0 -101
- package/dist/runtime/mcp-http/oauth/file-store.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/hono-oauth-router.d.ts +0 -5
- package/dist/runtime/mcp-http/oauth/hono-oauth-router.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/hono-oauth-router.js +0 -92
- package/dist/runtime/mcp-http/oauth/hono-oauth-router.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/oidc-rp.d.ts +0 -56
- package/dist/runtime/mcp-http/oauth/oidc-rp.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/oidc-rp.js +0 -63
- package/dist/runtime/mcp-http/oauth/oidc-rp.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/redirect-uri.d.ts +0 -11
- package/dist/runtime/mcp-http/oauth/redirect-uri.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/redirect-uri.js +0 -29
- package/dist/runtime/mcp-http/oauth/redirect-uri.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/single-tenant-provider.d.ts +0 -70
- package/dist/runtime/mcp-http/oauth/single-tenant-provider.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/single-tenant-provider.js +0 -247
- package/dist/runtime/mcp-http/oauth/single-tenant-provider.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/tenancy.d.ts +0 -3
- package/dist/runtime/mcp-http/oauth/tenancy.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/tenancy.js +0 -18
- package/dist/runtime/mcp-http/oauth/tenancy.js.map +0 -1
- package/dist/runtime/mcp-http/readonly-allowlist.d.ts +0 -24
- package/dist/runtime/mcp-http/readonly-allowlist.d.ts.map +0 -1
- package/dist/runtime/mcp-http/readonly-allowlist.js +0 -43
- package/dist/runtime/mcp-http/readonly-allowlist.js.map +0 -1
- package/dist/runtime/mcp-http/serve.d.ts +0 -39
- package/dist/runtime/mcp-http/serve.d.ts.map +0 -1
- package/dist/runtime/mcp-http/serve.js +0 -100
- package/dist/runtime/mcp-http/serve.js.map +0 -1
- package/dist/runtime/mirror/accept.d.ts +0 -57
- package/dist/runtime/mirror/accept.d.ts.map +0 -1
- package/dist/runtime/mirror/accept.js +0 -70
- package/dist/runtime/mirror/accept.js.map +0 -1
- package/dist/runtime/mirror/build.d.ts +0 -44
- package/dist/runtime/mirror/build.d.ts.map +0 -1
- package/dist/runtime/mirror/build.js +0 -37
- package/dist/runtime/mirror/build.js.map +0 -1
- package/dist/runtime/mirror/index.d.ts +0 -9
- package/dist/runtime/mirror/index.d.ts.map +0 -1
- package/dist/runtime/mirror/index.js +0 -9
- package/dist/runtime/mirror/index.js.map +0 -1
- package/dist/runtime/mirror/serve.d.ts +0 -21
- package/dist/runtime/mirror/serve.d.ts.map +0 -1
- package/dist/runtime/mirror/serve.js +0 -141
- package/dist/runtime/mirror/serve.js.map +0 -1
- package/dist/runtime/nhi.d.ts +0 -15
- package/dist/runtime/nhi.d.ts.map +0 -1
- package/dist/runtime/nhi.js +0 -26
- package/dist/runtime/nhi.js.map +0 -1
- package/dist/runtime/remote/accept.d.ts +0 -50
- package/dist/runtime/remote/accept.d.ts.map +0 -1
- package/dist/runtime/remote/accept.js +0 -54
- package/dist/runtime/remote/accept.js.map +0 -1
- package/dist/runtime/remote/client.d.ts +0 -27
- package/dist/runtime/remote/client.d.ts.map +0 -1
- package/dist/runtime/remote/client.js +0 -34
- package/dist/runtime/remote/client.js.map +0 -1
- package/dist/runtime/remote/index.d.ts +0 -5
- package/dist/runtime/remote/index.d.ts.map +0 -1
- package/dist/runtime/remote/index.js +0 -5
- package/dist/runtime/remote/index.js.map +0 -1
- package/dist/runtime/remote/serve.d.ts +0 -27
- package/dist/runtime/remote/serve.d.ts.map +0 -1
- package/dist/runtime/remote/serve.js +0 -26
- package/dist/runtime/remote/serve.js.map +0 -1
- package/dist/runtime/remote/server.d.ts +0 -36
- package/dist/runtime/remote/server.d.ts.map +0 -1
- package/dist/runtime/remote/server.js +0 -100
- package/dist/runtime/remote/server.js.map +0 -1
- package/dist/runtime/sysml/client.d.ts +0 -44
- package/dist/runtime/sysml/client.d.ts.map +0 -1
- package/dist/runtime/sysml/client.js +0 -52
- package/dist/runtime/sysml/client.js.map +0 -1
- package/dist/runtime/sysml/disclosure.d.ts +0 -23
- package/dist/runtime/sysml/disclosure.d.ts.map +0 -1
- package/dist/runtime/sysml/disclosure.js +0 -53
- package/dist/runtime/sysml/disclosure.js.map +0 -1
- package/dist/runtime/sysml/index.d.ts +0 -4
- package/dist/runtime/sysml/index.d.ts.map +0 -1
- package/dist/runtime/sysml/index.js +0 -4
- package/dist/runtime/sysml/index.js.map +0 -1
- package/dist/runtime/sysml/verify.d.ts +0 -32
- package/dist/runtime/sysml/verify.d.ts.map +0 -1
- package/dist/runtime/sysml/verify.js +0 -67
- package/dist/runtime/sysml/verify.js.map +0 -1
- package/dist/runtime/upgrade/index.d.ts +0 -96
- package/dist/runtime/upgrade/index.d.ts.map +0 -1
- package/dist/runtime/upgrade/index.js +0 -185
- package/dist/runtime/upgrade/index.js.map +0 -1
- package/dist/runtime/version/agent-version.d.ts +0 -15
- package/dist/runtime/version/agent-version.d.ts.map +0 -1
- package/dist/runtime/version/agent-version.js +0 -53
- package/dist/runtime/version/agent-version.js.map +0 -1
- package/skills/h2a/SKILL.md +0 -345
|
@@ -1,997 +0,0 @@
|
|
|
1
|
-
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import { H2A_ARTIFACT_KINDS, H2A_AUTHORITY_MATRIX, H2A_DECLARATION_INTERET_BODY_KIND, appendJournalEntry, assertValidNegotiationState, canSignArtifactKind, computeHash, createJournalEntry, deriveDecisionDossier as deriveDecisionDossierCore, derivePostureConflit, derivePostureConfiance as derivePostureConfianceCore, evaluatePresenterBias, isH2AEnvelope, validateH2AEnvelope, isH2ADeclarationInteret, validateSubagentBinding, verifyCanonical, verifyJournalChain } from "@sentropic/h2a";
|
|
4
|
-
import { withLockSync } from "./locks.js";
|
|
5
|
-
import { withLeaseSync } from "./lease.js";
|
|
6
|
-
import { inboxDir, inboxDirRaw, localStorePaths, negotiationDir, negotiationJournalFile, outboxDir, outboxDirRaw, safePathSegment } from "./paths.js";
|
|
7
|
-
import { H2A_STORE_SCHEMA_FILE, H2A_STORE_SCHEMA_VERSION, StoreSchemaMismatchError, readCliPackageVersion } from "./schema.js";
|
|
8
|
-
import { listIdentityAliases, mergeInboxDedup } from "../identity/migration.js";
|
|
9
|
-
function ensureDir(path) {
|
|
10
|
-
mkdirSync(path, { recursive: true });
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Compute the immutable on-disk target for a stabilized artifact (DEC-031, DEC-033).
|
|
14
|
-
* `CONTRACT`/`POLICY`/`ENGAGEMENT` get their own subtree keyed by `artifact.id`;
|
|
15
|
-
* anything else (AMENDMENT/MANDATE/AUTHORITY/ENFORCEMENT_PLAN/unknown) falls
|
|
16
|
-
* back to `<root>/artifacts/<artifactHash>.json` so write-once semantics still
|
|
17
|
-
* apply (DEC-033).
|
|
18
|
-
*/
|
|
19
|
-
function resolveStabilizedArtifactPath(paths, artifact, artifactHash) {
|
|
20
|
-
const a = (typeof artifact === "object" && artifact !== null
|
|
21
|
-
? artifact
|
|
22
|
-
: {});
|
|
23
|
-
const kind = typeof a.kind === "string" ? a.kind : undefined;
|
|
24
|
-
const id = typeof a.id === "string" ? a.id : undefined;
|
|
25
|
-
// DEC-062: sanitize ids through safePathSegment so Windows mkdir
|
|
26
|
-
// accepts them (`nego:codex` would otherwise be a forbidden path).
|
|
27
|
-
if (kind === "CONTRACT" && id) {
|
|
28
|
-
return join(paths.contracts, safePathSegment(id), "contract.json");
|
|
29
|
-
}
|
|
30
|
-
if (kind === "POLICY" && id) {
|
|
31
|
-
return join(paths.policies, `${safePathSegment(id)}.json`);
|
|
32
|
-
}
|
|
33
|
-
if (kind === "ENGAGEMENT" && id) {
|
|
34
|
-
return join(paths.engagements, safePathSegment(id), "charter.json");
|
|
35
|
-
}
|
|
36
|
-
// Fallback: hash-addressed under <root>/artifacts/. We replace ":" so the
|
|
37
|
-
// canonical "sha256:<hex>" prefix maps to a portable filename.
|
|
38
|
-
const safeHash = artifactHash.replace(/:/g, "_");
|
|
39
|
-
return join(paths.artifacts, `${safeHash}.json`);
|
|
40
|
-
}
|
|
41
|
-
function ensureLayout(paths) {
|
|
42
|
-
ensureDir(paths.root);
|
|
43
|
-
ensureDir(paths.registry);
|
|
44
|
-
ensureDir(paths.contracts);
|
|
45
|
-
ensureDir(paths.policies);
|
|
46
|
-
ensureDir(paths.engagements);
|
|
47
|
-
ensureDir(paths.artifacts);
|
|
48
|
-
ensureDir(paths.negotiations);
|
|
49
|
-
ensureDir(paths.inbox);
|
|
50
|
-
ensureDir(paths.outbox);
|
|
51
|
-
}
|
|
52
|
-
function readJsonl(file) {
|
|
53
|
-
if (!existsSync(file))
|
|
54
|
-
return [];
|
|
55
|
-
const content = readFileSync(file, "utf8");
|
|
56
|
-
if (content.length === 0)
|
|
57
|
-
return [];
|
|
58
|
-
const lines = content.split("\n");
|
|
59
|
-
const out = [];
|
|
60
|
-
for (const line of lines) {
|
|
61
|
-
if (line.length === 0)
|
|
62
|
-
continue;
|
|
63
|
-
out.push(JSON.parse(line));
|
|
64
|
-
}
|
|
65
|
-
return out;
|
|
66
|
-
}
|
|
67
|
-
function appendJsonl(file, value) {
|
|
68
|
-
appendFileSync(file, `${JSON.stringify(value)}\n`, { encoding: "utf8" });
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Initialize or validate the schema sentinel for a store root (DEC-036).
|
|
72
|
-
* Returns silently on success; throws `StoreSchemaMismatchError` if the
|
|
73
|
-
* on-disk version is unknown and `allowVersionMismatch` is false.
|
|
74
|
-
*/
|
|
75
|
-
function ensureSchemaSentinel(root, options) {
|
|
76
|
-
const sentinelPath = join(root, H2A_STORE_SCHEMA_FILE);
|
|
77
|
-
if (!existsSync(sentinelPath)) {
|
|
78
|
-
const payload = {
|
|
79
|
-
version: H2A_STORE_SCHEMA_VERSION,
|
|
80
|
-
createdAt: new Date().toISOString(),
|
|
81
|
-
createdBy: readCliPackageVersion()
|
|
82
|
-
};
|
|
83
|
-
writeFileSync(sentinelPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
let parsed;
|
|
87
|
-
try {
|
|
88
|
-
parsed = JSON.parse(readFileSync(sentinelPath, "utf8"));
|
|
89
|
-
}
|
|
90
|
-
catch (err) {
|
|
91
|
-
throw new StoreSchemaMismatchError("<unparseable>", H2A_STORE_SCHEMA_VERSION, root);
|
|
92
|
-
}
|
|
93
|
-
const found = typeof parsed.version === "string" ? parsed.version : "<missing>";
|
|
94
|
-
if (found === H2A_STORE_SCHEMA_VERSION)
|
|
95
|
-
return;
|
|
96
|
-
if (options.allowVersionMismatch) {
|
|
97
|
-
process.stderr.write(`h2a store: schema version mismatch at ${root} (found "${found}", expected "${H2A_STORE_SCHEMA_VERSION}"); proceeding read-only because allowVersionMismatch=true (DEC-036)\n`);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
throw new StoreSchemaMismatchError(found, H2A_STORE_SCHEMA_VERSION, root);
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Resolve the effective lock strategy (DEC-066 / DEC-067). Explicit option
|
|
104
|
-
* wins; otherwise fall back to `H2A_LOCK_MODE` env so a K8s tenant Deployment
|
|
105
|
-
* (Scenario B) can turn on the lease lock for every store-creation site —
|
|
106
|
-
* `mcp-serve` and the one-shot CLI verbs — without threading a flag through
|
|
107
|
-
* each call. Unknown env values fall through to the `"pid"` default.
|
|
108
|
-
*/
|
|
109
|
-
function resolveLockMode(explicit) {
|
|
110
|
-
if (explicit)
|
|
111
|
-
return explicit;
|
|
112
|
-
const env = process.env.H2A_LOCK_MODE;
|
|
113
|
-
return env === "lease" || env === "pid" ? env : "pid";
|
|
114
|
-
}
|
|
115
|
-
/** Read `H2A_LEASE_MS` env as a positive integer, else undefined (DEC-067). */
|
|
116
|
-
function envLeaseMs() {
|
|
117
|
-
const raw = process.env.H2A_LEASE_MS;
|
|
118
|
-
if (!raw)
|
|
119
|
-
return undefined;
|
|
120
|
-
const n = Number.parseInt(raw, 10);
|
|
121
|
-
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
122
|
-
}
|
|
123
|
-
export function createLocalStore(options) {
|
|
124
|
-
const paths = localStorePaths(options.root);
|
|
125
|
-
ensureLayout(paths);
|
|
126
|
-
ensureSchemaSentinel(paths.root, {
|
|
127
|
-
allowVersionMismatch: options.allowVersionMismatch === true
|
|
128
|
-
});
|
|
129
|
-
if (!existsSync(paths.instances)) {
|
|
130
|
-
writeFileSync(paths.instances, "", { encoding: "utf8" });
|
|
131
|
-
}
|
|
132
|
-
const lockTimeoutMs = options.lockTimeoutMs ?? 5000;
|
|
133
|
-
const lockMode = resolveLockMode(options.lockMode);
|
|
134
|
-
const leaseMs = options.leaseMs ?? envLeaseMs() ?? 30000;
|
|
135
|
-
const lockOpts = lockMode === "lease"
|
|
136
|
-
? { timeoutMs: lockTimeoutMs, leaseMs }
|
|
137
|
-
: { timeoutMs: lockTimeoutMs };
|
|
138
|
-
// DEC-066: route every critical section through the chosen strategy. `pid`
|
|
139
|
-
// is the same-machine default (DEC-036); `lease` is the cross-host primitive
|
|
140
|
-
// (DEC-065) for a shared RWX store. Both share the `(path, fn, opts)` shape;
|
|
141
|
-
// the lease handle arg is simply ignored by the store's sync sections.
|
|
142
|
-
function lock(lockPath, fn, opts) {
|
|
143
|
-
return lockMode === "lease"
|
|
144
|
-
? withLeaseSync(lockPath, fn, opts)
|
|
145
|
-
: withLockSync(lockPath, fn, opts);
|
|
146
|
-
}
|
|
147
|
-
const registryLock = join(paths.registry, ".lock");
|
|
148
|
-
const negotiationLock = (id) => join(negotiationDir(paths, id), ".lock");
|
|
149
|
-
const inboxLock = (actor) => join(inboxDir(paths, actor), ".lock");
|
|
150
|
-
const outboxLock = (actor) => join(outboxDir(paths, actor), ".lock");
|
|
151
|
-
function listInstances() {
|
|
152
|
-
return readJsonl(paths.instances);
|
|
153
|
-
}
|
|
154
|
-
function findInstance(id) {
|
|
155
|
-
return listInstances().find((entry) => entry.id === id);
|
|
156
|
-
}
|
|
157
|
-
function registerInstance(reg) {
|
|
158
|
-
lock(registryLock, () => {
|
|
159
|
-
if (findInstance(reg.id)) {
|
|
160
|
-
throw new Error(`Instance already registered: ${reg.id}`);
|
|
161
|
-
}
|
|
162
|
-
appendJsonl(paths.instances, reg);
|
|
163
|
-
}, lockOpts);
|
|
164
|
-
}
|
|
165
|
-
// DEC-078: the active public keys for an instance = the keys baked into its
|
|
166
|
-
// registration PLUS keys added to the keyring, MINUS any revoked. Derived
|
|
167
|
-
// from append-only sources so a rotation never rewrites a registration.
|
|
168
|
-
function listInstanceKeys(instanceId) {
|
|
169
|
-
const fromRegistration = findInstance(instanceId)?.publicKeys ?? [];
|
|
170
|
-
const events = readJsonl(paths.keys).filter((e) => e.instance === instanceId);
|
|
171
|
-
const revoked = new Set(events.filter((e) => e.type === "revoked").map((e) => e.publicKey));
|
|
172
|
-
const active = new Set();
|
|
173
|
-
for (const pem of fromRegistration)
|
|
174
|
-
active.add(pem);
|
|
175
|
-
for (const e of events) {
|
|
176
|
-
if (e.type === "added")
|
|
177
|
-
active.add(e.publicKey);
|
|
178
|
-
}
|
|
179
|
-
for (const pem of revoked)
|
|
180
|
-
active.delete(pem);
|
|
181
|
-
return [...active];
|
|
182
|
-
}
|
|
183
|
-
// DEC-087: raw keyring events (added/revoked, append-only) for posture
|
|
184
|
-
// derivation — `auditNhiPosture` reads these to age active keys (NHI7).
|
|
185
|
-
function listKeyEvents() {
|
|
186
|
-
return readJsonl(paths.keys);
|
|
187
|
-
}
|
|
188
|
-
function addInstanceKey(instanceId, publicKeyPem) {
|
|
189
|
-
lock(registryLock, () => {
|
|
190
|
-
if (!findInstance(instanceId)) {
|
|
191
|
-
throw new Error(`Instance not registered: ${instanceId}`);
|
|
192
|
-
}
|
|
193
|
-
if (listInstanceKeys(instanceId).includes(publicKeyPem)) {
|
|
194
|
-
throw new Error(`Key already registered for ${instanceId}`);
|
|
195
|
-
}
|
|
196
|
-
appendJsonl(paths.keys, {
|
|
197
|
-
instance: instanceId,
|
|
198
|
-
publicKey: publicKeyPem,
|
|
199
|
-
type: "added",
|
|
200
|
-
at: new Date().toISOString()
|
|
201
|
-
});
|
|
202
|
-
}, lockOpts);
|
|
203
|
-
}
|
|
204
|
-
// DEC-079: revoke a key (rotate-out). Appends a `revoked` event; the
|
|
205
|
-
// `listInstanceKeys` union already subtracts it, so the verifier stops
|
|
206
|
-
// accepting it. Rejects revoking a key that is not currently active.
|
|
207
|
-
function revokeInstanceKey(instanceId, publicKeyPem) {
|
|
208
|
-
lock(registryLock, () => {
|
|
209
|
-
if (!listInstanceKeys(instanceId).includes(publicKeyPem)) {
|
|
210
|
-
throw new Error(`Key not active for ${instanceId}`);
|
|
211
|
-
}
|
|
212
|
-
appendJsonl(paths.keys, {
|
|
213
|
-
instance: instanceId,
|
|
214
|
-
publicKey: publicKeyPem,
|
|
215
|
-
type: "revoked",
|
|
216
|
-
at: new Date().toISOString()
|
|
217
|
-
});
|
|
218
|
-
}, lockOpts);
|
|
219
|
-
}
|
|
220
|
-
// DEC-109: append-only org-membership grants, layered over registrations like
|
|
221
|
-
// the DEC-078 keyring. A grant only augments an already-registered instance
|
|
222
|
-
// (keyed-only); the `effectiveOrgInstances` union surfaces it to diff/discover.
|
|
223
|
-
function listOrgMembership() {
|
|
224
|
-
return readJsonl(paths.orgMembership);
|
|
225
|
-
}
|
|
226
|
-
function grantOrgMembership(grant) {
|
|
227
|
-
lock(registryLock, () => {
|
|
228
|
-
if (!listInstances().some((r) => r.instance === grant.instance)) {
|
|
229
|
-
throw new Error(`Instance not registered: ${grant.instance}`);
|
|
230
|
-
}
|
|
231
|
-
appendJsonl(paths.orgMembership, grant);
|
|
232
|
-
}, lockOpts);
|
|
233
|
-
}
|
|
234
|
-
function listSubagents() {
|
|
235
|
-
return readJsonl(paths.subagents);
|
|
236
|
-
}
|
|
237
|
-
function findSubagent(id) {
|
|
238
|
-
return listSubagents().find((entry) => entry.id === id);
|
|
239
|
-
}
|
|
240
|
-
function listSubagentsOf(parentInstance) {
|
|
241
|
-
return listSubagents().filter((b) => b.parentInstance === parentInstance);
|
|
242
|
-
}
|
|
243
|
-
// DEC-071: append-only per-subagent audit log. Callers append under the
|
|
244
|
-
// registry lock (low-volume, co-located with the binding registry).
|
|
245
|
-
function appendSubagentAudit(event) {
|
|
246
|
-
appendJsonl(paths.subagentAudit, event);
|
|
247
|
-
}
|
|
248
|
-
function readSubagentAudit(subagentId) {
|
|
249
|
-
return readJsonl(paths.subagentAudit).filter((e) => e.subagent === subagentId);
|
|
250
|
-
}
|
|
251
|
-
function readSubagentAuditOf(parentInstance) {
|
|
252
|
-
const ids = new Set(listSubagentsOf(parentInstance).map((b) => b.id));
|
|
253
|
-
return readJsonl(paths.subagentAudit).filter((e) => ids.has(e.subagent));
|
|
254
|
-
}
|
|
255
|
-
// DEC-072: a subagent's lifecycle status is *derived* from the append-only
|
|
256
|
-
// audit log rather than stored mutably — a `revoked` event flips it. This
|
|
257
|
-
// keeps `subagents.jsonl` append-only (no in-place binding edit) and reuses
|
|
258
|
-
// the slice-4 audit as the single source of truth.
|
|
259
|
-
function subagentStatus(subagentId) {
|
|
260
|
-
const events = readSubagentAudit(subagentId);
|
|
261
|
-
return events.some((e) => e.type === "revoked") ? "revoked" : "active";
|
|
262
|
-
}
|
|
263
|
-
// DEC-072: takeover at subagent granularity. Revoking deactivates a subagent
|
|
264
|
-
// (future routing is refused); its pending inbox stays readable by the parent
|
|
265
|
-
// via the fan-in (DEC-070), which is how the parent reclaims its work.
|
|
266
|
-
function revokeSubagent(subagentId, reason) {
|
|
267
|
-
lock(registryLock, () => {
|
|
268
|
-
if (!findSubagent(subagentId)) {
|
|
269
|
-
throw new Error(`Subagent not registered: ${subagentId}`);
|
|
270
|
-
}
|
|
271
|
-
if (subagentStatus(subagentId) === "revoked") {
|
|
272
|
-
throw new Error(`Subagent already revoked: ${subagentId}`);
|
|
273
|
-
}
|
|
274
|
-
appendSubagentAudit({
|
|
275
|
-
subagent: subagentId,
|
|
276
|
-
type: "revoked",
|
|
277
|
-
at: new Date().toISOString(),
|
|
278
|
-
...(reason ? { reason } : {})
|
|
279
|
-
});
|
|
280
|
-
}, lockOpts);
|
|
281
|
-
}
|
|
282
|
-
// DEC-089: coordinated NHI offboarding. Revokes every active key and every
|
|
283
|
-
// active subagent of the instance (each via the already-locked revoke
|
|
284
|
-
// primitives — no nested lock), then appends a tombstone. Re-running is safe:
|
|
285
|
-
// it revokes only what is still active, so a second call records 0/empty.
|
|
286
|
-
function offboardInstance(instanceId, reason) {
|
|
287
|
-
if (!findInstance(instanceId)) {
|
|
288
|
-
throw new Error(`Instance not registered: ${instanceId}`);
|
|
289
|
-
}
|
|
290
|
-
const activeKeys = listInstanceKeys(instanceId);
|
|
291
|
-
for (const pem of activeKeys)
|
|
292
|
-
revokeInstanceKey(instanceId, pem);
|
|
293
|
-
const activeSubs = listSubagentsOf(instanceId).filter((b) => subagentStatus(b.id) === "active");
|
|
294
|
-
for (const b of activeSubs)
|
|
295
|
-
revokeSubagent(b.id, reason ?? "offboard");
|
|
296
|
-
const tombstone = {
|
|
297
|
-
instance: instanceId,
|
|
298
|
-
...(reason ? { reason } : {}),
|
|
299
|
-
revokedKeys: activeKeys.length,
|
|
300
|
-
revokedSubagents: activeSubs.map((b) => b.id),
|
|
301
|
-
at: new Date().toISOString()
|
|
302
|
-
};
|
|
303
|
-
lock(registryLock, () => appendJsonl(paths.offboard, tombstone), lockOpts);
|
|
304
|
-
return tombstone;
|
|
305
|
-
}
|
|
306
|
-
function listOffboards() {
|
|
307
|
-
return readJsonl(paths.offboard);
|
|
308
|
-
}
|
|
309
|
-
// DEC-068: subagent bindings share the registry lock with instance
|
|
310
|
-
// registration — both are low-volume registry appends, so one lock keeps the
|
|
311
|
-
// two files mutually consistent (a binding's parent must already be present).
|
|
312
|
-
function registerSubagent(binding) {
|
|
313
|
-
lock(registryLock, () => {
|
|
314
|
-
const parent = findInstance(binding.parentInstance);
|
|
315
|
-
if (!parent) {
|
|
316
|
-
throw new Error(`Subagent parent not registered: ${binding.parentInstance}`);
|
|
317
|
-
}
|
|
318
|
-
const validation = validateSubagentBinding(binding, parent);
|
|
319
|
-
if (!validation.ok) {
|
|
320
|
-
throw new Error(`Invalid subagent binding (${binding.id}): ${validation.errors.join(", ")}`);
|
|
321
|
-
}
|
|
322
|
-
if (findSubagent(binding.id)) {
|
|
323
|
-
throw new Error(`Subagent already registered: ${binding.id}`);
|
|
324
|
-
}
|
|
325
|
-
appendJsonl(paths.subagents, binding);
|
|
326
|
-
appendSubagentAudit({
|
|
327
|
-
subagent: binding.id,
|
|
328
|
-
type: "registered",
|
|
329
|
-
at: new Date().toISOString()
|
|
330
|
-
});
|
|
331
|
-
}, lockOpts);
|
|
332
|
-
}
|
|
333
|
-
function negotiationStateFile(id) {
|
|
334
|
-
return `${negotiationDir(paths, id)}/state.json`;
|
|
335
|
-
}
|
|
336
|
-
function openNegotiation(record) {
|
|
337
|
-
assertValidNegotiationState(record.status);
|
|
338
|
-
ensureDir(negotiationDir(paths, record.id));
|
|
339
|
-
return lock(negotiationLock(record.id), () => {
|
|
340
|
-
const file = negotiationStateFile(record.id);
|
|
341
|
-
if (existsSync(file)) {
|
|
342
|
-
throw new Error(`Negotiation already open: ${record.id}`);
|
|
343
|
-
}
|
|
344
|
-
writeFileSync(file, `${JSON.stringify(record, null, 2)}\n`, "utf8");
|
|
345
|
-
return record;
|
|
346
|
-
}, lockOpts);
|
|
347
|
-
}
|
|
348
|
-
function readNegotiation(id) {
|
|
349
|
-
const file = negotiationStateFile(id);
|
|
350
|
-
if (!existsSync(file))
|
|
351
|
-
return undefined;
|
|
352
|
-
return JSON.parse(readFileSync(file, "utf8"));
|
|
353
|
-
}
|
|
354
|
-
function updateNegotiationStatus(id, status) {
|
|
355
|
-
assertValidNegotiationState(status);
|
|
356
|
-
ensureDir(negotiationDir(paths, id));
|
|
357
|
-
return lock(negotiationLock(id), () => {
|
|
358
|
-
const current = readNegotiation(id);
|
|
359
|
-
if (!current) {
|
|
360
|
-
throw new Error(`Negotiation not found: ${id}`);
|
|
361
|
-
}
|
|
362
|
-
const updated = { ...current, status };
|
|
363
|
-
writeFileSync(negotiationStateFile(id), `${JSON.stringify(updated, null, 2)}\n`, "utf8");
|
|
364
|
-
return updated;
|
|
365
|
-
}, lockOpts);
|
|
366
|
-
}
|
|
367
|
-
function readNegotiationJournalUnlocked(negotiationId) {
|
|
368
|
-
const file = negotiationJournalFile(paths, negotiationId);
|
|
369
|
-
if (!existsSync(file))
|
|
370
|
-
return [];
|
|
371
|
-
const entries = readJsonl(file);
|
|
372
|
-
const check = verifyJournalChain(entries);
|
|
373
|
-
if (!check.ok) {
|
|
374
|
-
throw new Error(`Negotiation ${negotiationId}: corrupt journal chain at index ${check.index}: ${check.reason}`);
|
|
375
|
-
}
|
|
376
|
-
return entries;
|
|
377
|
-
}
|
|
378
|
-
function readNegotiationJournal(negotiationId) {
|
|
379
|
-
return readNegotiationJournalUnlocked(negotiationId);
|
|
380
|
-
}
|
|
381
|
-
function appendNegotiationEvent(negotiationId, payload) {
|
|
382
|
-
ensureDir(negotiationDir(paths, negotiationId));
|
|
383
|
-
return lock(negotiationLock(negotiationId), () => {
|
|
384
|
-
const file = negotiationJournalFile(paths, negotiationId);
|
|
385
|
-
const existing = readNegotiationJournalUnlocked(negotiationId);
|
|
386
|
-
const previous = existing[existing.length - 1];
|
|
387
|
-
const entry = previous
|
|
388
|
-
? appendJournalEntry(previous, payload)
|
|
389
|
-
: createJournalEntry(payload);
|
|
390
|
-
appendJsonl(file, entry);
|
|
391
|
-
return entry;
|
|
392
|
-
}, lockOpts);
|
|
393
|
-
}
|
|
394
|
-
function asPlainRecord(value) {
|
|
395
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
396
|
-
return undefined;
|
|
397
|
-
}
|
|
398
|
-
return value;
|
|
399
|
-
}
|
|
400
|
-
function addScopeValue(scopes, value) {
|
|
401
|
-
if (typeof value === "string" && value.length > 0) {
|
|
402
|
-
scopes.add(value);
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
if (Array.isArray(value)) {
|
|
406
|
-
for (const item of value)
|
|
407
|
-
addScopeValue(scopes, item);
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
const nested = asPlainRecord(value);
|
|
411
|
-
if (nested)
|
|
412
|
-
addScopeValue(scopes, nested.scope);
|
|
413
|
-
}
|
|
414
|
-
function collectDecisionScopes(record, entries, artifact) {
|
|
415
|
-
const scopes = new Set([record.scope]);
|
|
416
|
-
const addArtifactScopes = (candidate) => {
|
|
417
|
-
const body = asPlainRecord(candidate);
|
|
418
|
-
if (!body)
|
|
419
|
-
return;
|
|
420
|
-
addScopeValue(scopes, body.scope);
|
|
421
|
-
addScopeValue(scopes, body.aval);
|
|
422
|
-
addScopeValue(scopes, body.scopeAval);
|
|
423
|
-
};
|
|
424
|
-
for (const entry of entries) {
|
|
425
|
-
const body = asPlainRecord(entry.body);
|
|
426
|
-
if (body && "artifact" in body)
|
|
427
|
-
addArtifactScopes(body.artifact);
|
|
428
|
-
}
|
|
429
|
-
addArtifactScopes(artifact);
|
|
430
|
-
return [...scopes];
|
|
431
|
-
}
|
|
432
|
-
function collectDeclarations(entries) {
|
|
433
|
-
return entries
|
|
434
|
-
.map((entry) => entry.body)
|
|
435
|
-
.filter(isH2ADeclarationInteret);
|
|
436
|
-
}
|
|
437
|
-
function collectControleFlags(entries) {
|
|
438
|
-
const subjects = new Set();
|
|
439
|
-
for (const entry of entries) {
|
|
440
|
-
if (entry.actor.role !== "CONTROL")
|
|
441
|
-
continue;
|
|
442
|
-
const body = asPlainRecord(entry.body);
|
|
443
|
-
if (!body)
|
|
444
|
-
continue;
|
|
445
|
-
const subject = body.subject;
|
|
446
|
-
if (typeof subject !== "string" || subject.length === 0)
|
|
447
|
-
continue;
|
|
448
|
-
if (body.kind === "postureConflit" ||
|
|
449
|
-
body.kind === "conflitInteret" ||
|
|
450
|
-
body.postureConflit === "conflit-declarable" ||
|
|
451
|
-
body.conflitInteret === true) {
|
|
452
|
-
subjects.add(subject);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
return [...subjects];
|
|
456
|
-
}
|
|
457
|
-
function derivePosturesConflitFor(record, entries, artifact) {
|
|
458
|
-
const declarations = collectDeclarations(entries);
|
|
459
|
-
const controleFlags = collectControleFlags(entries);
|
|
460
|
-
const subjects = [];
|
|
461
|
-
const seen = new Set();
|
|
462
|
-
const addSubject = (subject) => {
|
|
463
|
-
if (seen.has(subject))
|
|
464
|
-
return;
|
|
465
|
-
seen.add(subject);
|
|
466
|
-
subjects.push(subject);
|
|
467
|
-
};
|
|
468
|
-
for (const signer of record.requiredSigners ?? [])
|
|
469
|
-
addSubject(signer);
|
|
470
|
-
for (const declaration of declarations)
|
|
471
|
-
addSubject(declaration.subject);
|
|
472
|
-
for (const subject of controleFlags)
|
|
473
|
-
addSubject(subject);
|
|
474
|
-
const decisionScopes = collectDecisionScopes(record, entries, artifact);
|
|
475
|
-
return subjects.map((subject) => derivePostureConflit(subject, {
|
|
476
|
-
declarations,
|
|
477
|
-
ownScopes: findInstance(subject)?.scopes ?? [],
|
|
478
|
-
decisionScopes,
|
|
479
|
-
signers: record.requiredSigners ?? [],
|
|
480
|
-
controleFlags
|
|
481
|
-
}));
|
|
482
|
-
}
|
|
483
|
-
function findInstanceBySubject(subject) {
|
|
484
|
-
return listInstances().find((entry) => entry.id === subject || entry.instance === subject);
|
|
485
|
-
}
|
|
486
|
-
function subjectScopesBySubject() {
|
|
487
|
-
const out = {};
|
|
488
|
-
for (const registration of listInstances()) {
|
|
489
|
-
const scopes = registration.scopes ?? [];
|
|
490
|
-
out[registration.id] = scopes;
|
|
491
|
-
out[registration.instance] = scopes;
|
|
492
|
-
}
|
|
493
|
-
return out;
|
|
494
|
-
}
|
|
495
|
-
function publicKeysBySubject() {
|
|
496
|
-
const out = {};
|
|
497
|
-
for (const registration of listInstances()) {
|
|
498
|
-
const keys = listInstanceKeys(registration.id);
|
|
499
|
-
out[registration.id] = keys;
|
|
500
|
-
out[registration.instance] = keys;
|
|
501
|
-
}
|
|
502
|
-
return out;
|
|
503
|
-
}
|
|
504
|
-
function deriveDecisionDossierFor(record, entries, artifact) {
|
|
505
|
-
const recordForDossier = artifact === undefined
|
|
506
|
-
? record
|
|
507
|
-
: { ...record, currentArtifactHash: computeHash(artifact) };
|
|
508
|
-
return deriveDecisionDossierCore({
|
|
509
|
-
record: recordForDossier,
|
|
510
|
-
journal: entries,
|
|
511
|
-
subjectScopes: subjectScopesBySubject(),
|
|
512
|
-
controleFlags: collectControleFlags(entries)
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
function derivePostureConfianceFor(record, entries, artifact) {
|
|
516
|
-
return derivePostureConfianceCore({
|
|
517
|
-
record,
|
|
518
|
-
journal: entries,
|
|
519
|
-
dossier: deriveDecisionDossierFor(record, entries, artifact),
|
|
520
|
-
publicKeys: publicKeysBySubject(),
|
|
521
|
-
subjectScopes: subjectScopesBySubject(),
|
|
522
|
-
decisionScopes: collectDecisionScopes(record, entries, artifact)
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
function deriveDecisionDossier(negotiationId, options = {}) {
|
|
526
|
-
const record = readNegotiation(negotiationId);
|
|
527
|
-
if (!record) {
|
|
528
|
-
throw new Error(`Negotiation not found: ${negotiationId}`);
|
|
529
|
-
}
|
|
530
|
-
const entries = readNegotiationJournalUnlocked(negotiationId);
|
|
531
|
-
const declarations = collectDeclarations(entries);
|
|
532
|
-
const controleFlags = collectControleFlags(entries);
|
|
533
|
-
const subjectScopes = subjectScopesBySubject();
|
|
534
|
-
const dossier = deriveDecisionDossierCore({
|
|
535
|
-
record,
|
|
536
|
-
journal: entries,
|
|
537
|
-
subjectScopes,
|
|
538
|
-
controleFlags
|
|
539
|
-
});
|
|
540
|
-
const result = {
|
|
541
|
-
negotiationId,
|
|
542
|
-
dossier,
|
|
543
|
-
dossierHash: computeHash(dossier)
|
|
544
|
-
};
|
|
545
|
-
if (!options.presenter) {
|
|
546
|
-
return result;
|
|
547
|
-
}
|
|
548
|
-
const presenterRegistration = findInstanceBySubject(options.presenter);
|
|
549
|
-
const presenterBias = evaluatePresenterBias(options.presenter, {
|
|
550
|
-
declarations,
|
|
551
|
-
ownScopes: presenterRegistration?.scopes ?? [],
|
|
552
|
-
decisionScopes: collectDecisionScopes(record, entries),
|
|
553
|
-
signers: record.requiredSigners ?? [],
|
|
554
|
-
controleFlags
|
|
555
|
-
});
|
|
556
|
-
if (!presenterBias.biased || options.advisoryGate !== true) {
|
|
557
|
-
return { ...result, presenterBias };
|
|
558
|
-
}
|
|
559
|
-
const advisoryEvent = appendNegotiationEvent(negotiationId, {
|
|
560
|
-
id: options.eventId ?? `evt-presenterBias-${Date.now().toString(36)}`,
|
|
561
|
-
type: "escalate",
|
|
562
|
-
actor: { instance: "h2a-cli", role: "MANDATAIRE", scope: record.scope },
|
|
563
|
-
body: {
|
|
564
|
-
kind: "escalation",
|
|
565
|
-
channel: "alert",
|
|
566
|
-
payload: {
|
|
567
|
-
kind: "presenterBias",
|
|
568
|
-
subject: presenterBias.subject,
|
|
569
|
-
postureConflit: presenterBias.posture.postureConflit,
|
|
570
|
-
criteres: presenterBias.posture.criteres,
|
|
571
|
-
disclosureMode: presenterBias.posture.disclosureMode,
|
|
572
|
-
masqueImpactCollectif: presenterBias.posture.masqueImpactCollectif,
|
|
573
|
-
requiredBodyKind: H2A_DECLARATION_INTERET_BODY_KIND,
|
|
574
|
-
disposition: "route-escalate"
|
|
575
|
-
}
|
|
576
|
-
},
|
|
577
|
-
createdAt: new Date().toISOString()
|
|
578
|
-
});
|
|
579
|
-
return { ...result, presenterBias, advisoryEvent };
|
|
580
|
-
}
|
|
581
|
-
function derivePostureConfiance(negotiationId) {
|
|
582
|
-
const record = readNegotiation(negotiationId);
|
|
583
|
-
if (!record) {
|
|
584
|
-
throw new Error(`Negotiation not found: ${negotiationId}`);
|
|
585
|
-
}
|
|
586
|
-
return derivePostureConfianceFor(record, readNegotiationJournalUnlocked(negotiationId));
|
|
587
|
-
}
|
|
588
|
-
function declareConflitInteret(negotiationId, declaration, options = {}) {
|
|
589
|
-
if (!isH2ADeclarationInteret(declaration)) {
|
|
590
|
-
throw new Error(`${H2A_DECLARATION_INTERET_BODY_KIND}: invalid declaration body`);
|
|
591
|
-
}
|
|
592
|
-
const record = readNegotiation(negotiationId);
|
|
593
|
-
if (!record) {
|
|
594
|
-
throw new Error(`Negotiation not found: ${negotiationId}`);
|
|
595
|
-
}
|
|
596
|
-
const registration = findInstance(declaration.subject);
|
|
597
|
-
const role = (registration?.roles?.[0] ?? "CONDUCTOR");
|
|
598
|
-
const scope = registration?.scopes?.[0] ?? record.scope;
|
|
599
|
-
return appendNegotiationEvent(negotiationId, {
|
|
600
|
-
id: options.eventId ?? `evt-declaration-interet-${Date.now().toString(36)}`,
|
|
601
|
-
type: "event",
|
|
602
|
-
actor: { instance: declaration.subject, role, scope },
|
|
603
|
-
body: declaration,
|
|
604
|
-
createdAt: declaration.at
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
|
-
function derivePosturesConflit(negotiationId) {
|
|
608
|
-
const record = readNegotiation(negotiationId);
|
|
609
|
-
if (!record) {
|
|
610
|
-
throw new Error(`Negotiation not found: ${negotiationId}`);
|
|
611
|
-
}
|
|
612
|
-
return derivePosturesConflitFor(record, readNegotiationJournalUnlocked(negotiationId));
|
|
613
|
-
}
|
|
614
|
-
function stabilizeNegotiation(negotiationId, options = {}) {
|
|
615
|
-
ensureDir(negotiationDir(paths, negotiationId));
|
|
616
|
-
return lock(negotiationLock(negotiationId), () => {
|
|
617
|
-
const record = readNegotiation(negotiationId);
|
|
618
|
-
if (!record) {
|
|
619
|
-
throw new Error(`Negotiation not found: ${negotiationId}`);
|
|
620
|
-
}
|
|
621
|
-
if (record.status === "stabilized") {
|
|
622
|
-
throw new Error(`Negotiation already stabilized: ${negotiationId}`);
|
|
623
|
-
}
|
|
624
|
-
if (!Array.isArray(record.requiredSigners) || record.requiredSigners.length === 0) {
|
|
625
|
-
throw new Error(`Negotiation ${negotiationId} has no requiredSigners`);
|
|
626
|
-
}
|
|
627
|
-
const entries = readNegotiationJournalUnlocked(negotiationId);
|
|
628
|
-
const byHash = new Map();
|
|
629
|
-
for (const entry of entries) {
|
|
630
|
-
const body = entry.body;
|
|
631
|
-
if (!body || body.kind !== "signature")
|
|
632
|
-
continue;
|
|
633
|
-
if (typeof body.artifactHash !== "string" || !body.signature)
|
|
634
|
-
continue;
|
|
635
|
-
const signer = entry.actor.instance;
|
|
636
|
-
const sig = body.signature;
|
|
637
|
-
if (sig.by !== signer) {
|
|
638
|
-
throw new Error(`Negotiation ${negotiationId}: signature by ${sig.by} does not match actor.instance ${signer}`);
|
|
639
|
-
}
|
|
640
|
-
const registration = findInstance(signer);
|
|
641
|
-
if (!registration) {
|
|
642
|
-
throw new Error(`Negotiation ${negotiationId}: signer ${signer} is not registered`);
|
|
643
|
-
}
|
|
644
|
-
const keys = registration.publicKeys ?? [];
|
|
645
|
-
if (keys.length === 0) {
|
|
646
|
-
throw new Error(`Negotiation ${negotiationId}: signer ${signer} has no publicKeys in the registry`);
|
|
647
|
-
}
|
|
648
|
-
const verified = keys.some((pem) => verifyCanonical({ artifactHash: body.artifactHash }, sig, pem));
|
|
649
|
-
if (!verified) {
|
|
650
|
-
throw new Error(`Negotiation ${negotiationId}: signature by ${signer} fails verification against registered keys`);
|
|
651
|
-
}
|
|
652
|
-
const bucket = byHash.get(body.artifactHash) ?? new Map();
|
|
653
|
-
bucket.set(signer, sig);
|
|
654
|
-
byHash.set(body.artifactHash, bucket);
|
|
655
|
-
}
|
|
656
|
-
const required = new Set(record.requiredSigners);
|
|
657
|
-
let winningHash;
|
|
658
|
-
for (const [hash, bucket] of byHash) {
|
|
659
|
-
const have = new Set(bucket.keys());
|
|
660
|
-
const allPresent = [...required].every((id) => have.has(id));
|
|
661
|
-
if (allPresent) {
|
|
662
|
-
winningHash = hash;
|
|
663
|
-
break;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
if (!winningHash) {
|
|
667
|
-
const summary = [...byHash].map(([hash, bucket]) => `${hash} signed by ${[...bucket.keys()].join(",")}`);
|
|
668
|
-
throw new Error(`Negotiation ${negotiationId}: no artifactHash has the full quorum (${record.requiredSigners.join(",")}). Collected: ${summary.join(" | ")}`);
|
|
669
|
-
}
|
|
670
|
-
// Walk back through the journal to find the offer/counter event whose body.artifact
|
|
671
|
-
// hashes to the winning artifactHash. We then persist that artifact JSON to its
|
|
672
|
-
// immutable target file (DEC-031 + DEC-033).
|
|
673
|
-
let winningArtifact;
|
|
674
|
-
for (const entry of entries) {
|
|
675
|
-
const body = entry.body;
|
|
676
|
-
if (!body || body.artifact === undefined)
|
|
677
|
-
continue;
|
|
678
|
-
if (computeHash(body.artifact) === winningHash) {
|
|
679
|
-
winningArtifact = body.artifact;
|
|
680
|
-
break;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
if (winningArtifact === undefined) {
|
|
684
|
-
throw new Error(`stabilizeNegotiation: no offer/counter event matches the winning artifactHash ${winningHash}`);
|
|
685
|
-
}
|
|
686
|
-
// Authority check (DEC-035): every signer of the winning artifactHash
|
|
687
|
-
// must hold at least one role allowed by H2A_AUTHORITY_MATRIX for the
|
|
688
|
-
// artifact's declared kind. Unknown/missing kinds emit a stderr warning
|
|
689
|
-
// and skip the check (V1 permissive on extension).
|
|
690
|
-
const winningKind = (typeof winningArtifact === "object" && winningArtifact !== null
|
|
691
|
-
? winningArtifact.kind
|
|
692
|
-
: undefined);
|
|
693
|
-
const knownKind = typeof winningKind === "string" &&
|
|
694
|
-
H2A_ARTIFACT_KINDS.includes(winningKind)
|
|
695
|
-
? winningKind
|
|
696
|
-
: undefined;
|
|
697
|
-
if (knownKind) {
|
|
698
|
-
const signersBucket = byHash.get(winningHash);
|
|
699
|
-
if (signersBucket) {
|
|
700
|
-
for (const signer of signersBucket.keys()) {
|
|
701
|
-
const reg = findInstance(signer);
|
|
702
|
-
const roles = (reg?.roles ?? []);
|
|
703
|
-
const allowed = roles.some((role) => canSignArtifactKind(role, knownKind));
|
|
704
|
-
if (!allowed) {
|
|
705
|
-
throw new Error(`Negotiation ${negotiationId}: signer ${signer} is not authorized to sign artifact kind ${knownKind} (roles: [${roles.join(",")}]); allowed roles: [${H2A_AUTHORITY_MATRIX[knownKind].roles.join(",")}]`);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
else {
|
|
711
|
-
process.stderr.write(`h2a stabilize: negotiation ${negotiationId} artifact ${winningHash} has no recognizable kind; skipping authority check (DEC-035)\n`);
|
|
712
|
-
}
|
|
713
|
-
const artifactPath = resolveStabilizedArtifactPath(paths, winningArtifact, winningHash);
|
|
714
|
-
ensureDir(dirname(artifactPath));
|
|
715
|
-
try {
|
|
716
|
-
writeFileSync(artifactPath, `${JSON.stringify(winningArtifact, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
|
|
717
|
-
}
|
|
718
|
-
catch (err) {
|
|
719
|
-
const e = err;
|
|
720
|
-
if (e.code === "EEXIST") {
|
|
721
|
-
throw new Error(`stabilizeNegotiation: stabilized artifact already on disk at ${artifactPath}`);
|
|
722
|
-
}
|
|
723
|
-
throw err;
|
|
724
|
-
}
|
|
725
|
-
// Inline append + status update (we already hold the per-negotiation
|
|
726
|
-
// lock, so we cannot recurse into the wrapped helpers).
|
|
727
|
-
const journalFile = negotiationJournalFile(paths, negotiationId);
|
|
728
|
-
let entriesForAppend = readNegotiationJournalUnlocked(negotiationId);
|
|
729
|
-
const advisoryEvents = [];
|
|
730
|
-
const posturesConflit = derivePosturesConflitFor(record, entriesForAppend, winningArtifact).filter((posture) => posture.postureConflit === "conflit-declarable" &&
|
|
731
|
-
record.requiredSigners.includes(posture.subject));
|
|
732
|
-
for (const [index, posture] of posturesConflit.entries()) {
|
|
733
|
-
const previousAdvisory = entriesForAppend[entriesForAppend.length - 1];
|
|
734
|
-
const advisoryPayload = {
|
|
735
|
-
id: `evt-postureConflit-${index}-${Date.now().toString(36)}`,
|
|
736
|
-
type: "escalate",
|
|
737
|
-
actor: { instance: "h2a-cli", role: "MANDATAIRE", scope: record.scope },
|
|
738
|
-
body: {
|
|
739
|
-
kind: "escalation",
|
|
740
|
-
channel: "alert",
|
|
741
|
-
payload: {
|
|
742
|
-
kind: "postureConflit",
|
|
743
|
-
subject: posture.subject,
|
|
744
|
-
postureConflit: posture.postureConflit,
|
|
745
|
-
criteres: posture.criteres,
|
|
746
|
-
disclosureMode: posture.disclosureMode,
|
|
747
|
-
masqueImpactCollectif: posture.masqueImpactCollectif,
|
|
748
|
-
requiredBodyKind: H2A_DECLARATION_INTERET_BODY_KIND,
|
|
749
|
-
disposition: "route-escalate"
|
|
750
|
-
}
|
|
751
|
-
},
|
|
752
|
-
createdAt: new Date().toISOString()
|
|
753
|
-
};
|
|
754
|
-
const advisoryEvent = previousAdvisory
|
|
755
|
-
? appendJournalEntry(previousAdvisory, advisoryPayload)
|
|
756
|
-
: createJournalEntry(advisoryPayload);
|
|
757
|
-
appendJsonl(journalFile, advisoryEvent);
|
|
758
|
-
advisoryEvents.push(advisoryEvent);
|
|
759
|
-
entriesForAppend = [...entriesForAppend, advisoryEvent];
|
|
760
|
-
}
|
|
761
|
-
const postureConfiance = derivePostureConfianceFor(record, entriesForAppend, winningArtifact);
|
|
762
|
-
if (postureConfiance.postureConfiance !== "etablie") {
|
|
763
|
-
const previousAdvisory = entriesForAppend[entriesForAppend.length - 1];
|
|
764
|
-
const advisoryPayload = {
|
|
765
|
-
id: `evt-postureConfiance-${Date.now().toString(36)}`,
|
|
766
|
-
type: "escalate",
|
|
767
|
-
actor: { instance: "h2a-cli", role: "MANDATAIRE", scope: record.scope },
|
|
768
|
-
body: {
|
|
769
|
-
kind: "escalation",
|
|
770
|
-
channel: "alert",
|
|
771
|
-
payload: {
|
|
772
|
-
kind: "postureConfiance",
|
|
773
|
-
postureConfiance: postureConfiance.postureConfiance,
|
|
774
|
-
dossierHash: postureConfiance.dossierHash,
|
|
775
|
-
attentionAttested: postureConfiance.attentionAttested,
|
|
776
|
-
noUndisclosedCollectiveConflict: postureConfiance.noUndisclosedCollectiveConflict,
|
|
777
|
-
missingAttestations: postureConfiance.missingAttestations,
|
|
778
|
-
staleAttestations: postureConfiance.staleAttestations,
|
|
779
|
-
undisclosedConflicts: postureConfiance.undisclosedConflicts,
|
|
780
|
-
disclosedConflicts: postureConfiance.disclosedConflicts,
|
|
781
|
-
reasons: postureConfiance.reasons,
|
|
782
|
-
disposition: "advisory-only"
|
|
783
|
-
}
|
|
784
|
-
},
|
|
785
|
-
createdAt: new Date().toISOString()
|
|
786
|
-
};
|
|
787
|
-
const advisoryEvent = previousAdvisory
|
|
788
|
-
? appendJournalEntry(previousAdvisory, advisoryPayload)
|
|
789
|
-
: createJournalEntry(advisoryPayload);
|
|
790
|
-
appendJsonl(journalFile, advisoryEvent);
|
|
791
|
-
advisoryEvents.push(advisoryEvent);
|
|
792
|
-
entriesForAppend = [...entriesForAppend, advisoryEvent];
|
|
793
|
-
}
|
|
794
|
-
const previous = entriesForAppend[entriesForAppend.length - 1];
|
|
795
|
-
const finalPayload = {
|
|
796
|
-
id: options.eventId ?? `evt-stabilize-${Date.now().toString(36)}`,
|
|
797
|
-
type: "event",
|
|
798
|
-
actor: { instance: "h2a-cli", role: "MANDATAIRE", scope: record.scope },
|
|
799
|
-
body: {
|
|
800
|
-
kind: "stabilized",
|
|
801
|
-
artifactHash: winningHash,
|
|
802
|
-
signers: record.requiredSigners,
|
|
803
|
-
artifactPath
|
|
804
|
-
},
|
|
805
|
-
createdAt: new Date().toISOString()
|
|
806
|
-
};
|
|
807
|
-
const finalEvent = previous
|
|
808
|
-
? appendJournalEntry(previous, finalPayload)
|
|
809
|
-
: createJournalEntry(finalPayload);
|
|
810
|
-
appendJsonl(journalFile, finalEvent);
|
|
811
|
-
const updated = {
|
|
812
|
-
...record,
|
|
813
|
-
status: "stabilized",
|
|
814
|
-
currentArtifactHash: winningHash
|
|
815
|
-
};
|
|
816
|
-
writeFileSync(negotiationStateFile(negotiationId), `${JSON.stringify(updated, null, 2)}\n`, "utf8");
|
|
817
|
-
return {
|
|
818
|
-
record: updated,
|
|
819
|
-
artifactHash: winningHash,
|
|
820
|
-
signers: record.requiredSigners,
|
|
821
|
-
finalEvent,
|
|
822
|
-
advisoryEvents,
|
|
823
|
-
artifactPath
|
|
824
|
-
};
|
|
825
|
-
}, lockOpts);
|
|
826
|
-
}
|
|
827
|
-
function envelopeFile(dir, envelopeId) {
|
|
828
|
-
// DEC-062: envelope ids can contain `:` (e.g. `env:hello`), sanitize.
|
|
829
|
-
return join(dir, `${safePathSegment(envelopeId)}.json`);
|
|
830
|
-
}
|
|
831
|
-
function readEnvelopesFrom(dir) {
|
|
832
|
-
if (!existsSync(dir))
|
|
833
|
-
return [];
|
|
834
|
-
const files = readdirSync(dir).filter((f) => f.endsWith(".json")).sort();
|
|
835
|
-
const out = [];
|
|
836
|
-
for (const file of files) {
|
|
837
|
-
const parsed = JSON.parse(readFileSync(join(dir, file), "utf8"));
|
|
838
|
-
if (isH2AEnvelope(parsed)) {
|
|
839
|
-
out.push(parsed);
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
return out;
|
|
843
|
-
}
|
|
844
|
-
function putInboxMessage(actor, envelope) {
|
|
845
|
-
const validation = validateH2AEnvelope(envelope);
|
|
846
|
-
if (!validation.ok) {
|
|
847
|
-
throw new Error("putInboxMessage: invalid H2A envelope — " + validation.errors.join("; "));
|
|
848
|
-
}
|
|
849
|
-
const dir = inboxDir(paths, actor);
|
|
850
|
-
ensureDir(dir);
|
|
851
|
-
lock(inboxLock(actor), () => {
|
|
852
|
-
writeFileSync(envelopeFile(dir, envelope.id), JSON.stringify(envelope, null, 2), "utf8");
|
|
853
|
-
}, lockOpts);
|
|
854
|
-
}
|
|
855
|
-
// Every dir an actor's inbox may live in: its canonical (case-folded) dir plus
|
|
856
|
-
// the raw pre-fix dir, for the actor and each legacy identity alias. Reading
|
|
857
|
-
// the raw form recovers envelopes deposited before the case-fold fix; writes
|
|
858
|
-
// always target the canonical dir, so the raw dirs drain as messages are popped.
|
|
859
|
-
function inboxSourceDirs(actor) {
|
|
860
|
-
// A legacy alias (`host:<cwd-leaf>`) is shared by every agent that ever
|
|
861
|
-
// connected in that workspace, so a later (de-collisioned) peer must NOT
|
|
862
|
-
// read the OWNER's legacy inbox. Owner = the FIRST claimant of that
|
|
863
|
-
// legacyInstance (earliest alias `at`). `adoptedKeyring` can't gate this — a
|
|
864
|
-
// fresh first connect has adoptedKeyring:false too. This keeps the legit
|
|
865
|
-
// "read your own migrated inbox" while closing the cross-read.
|
|
866
|
-
const allAliases = listIdentityAliases(paths.root);
|
|
867
|
-
const ownedLegacy = allAliases
|
|
868
|
-
.filter((a) => a.instance === actor)
|
|
869
|
-
.filter((a) => {
|
|
870
|
-
const claimants = allAliases.filter((x) => x.legacyInstance === a.legacyInstance);
|
|
871
|
-
const earliest = claimants.reduce((m, x) => (x.at < m.at ? x : m), claimants[0]);
|
|
872
|
-
return earliest.instance === actor;
|
|
873
|
-
})
|
|
874
|
-
.map((a) => a.legacyInstance);
|
|
875
|
-
const dirs = new Set();
|
|
876
|
-
for (const handle of [actor, ...ownedLegacy]) {
|
|
877
|
-
dirs.add(inboxDir(paths, handle));
|
|
878
|
-
dirs.add(inboxDirRaw(paths, handle));
|
|
879
|
-
}
|
|
880
|
-
return [...dirs];
|
|
881
|
-
}
|
|
882
|
-
function readInbox(actor) {
|
|
883
|
-
return mergeInboxDedup(inboxSourceDirs(actor).map((dir) => readEnvelopesFrom(dir)));
|
|
884
|
-
}
|
|
885
|
-
function popInboxMessage(actor, envelopeId) {
|
|
886
|
-
const dirs = inboxSourceDirs(actor);
|
|
887
|
-
for (const dir of dirs)
|
|
888
|
-
ensureDir(dir);
|
|
889
|
-
return lock(inboxLock(actor), () => {
|
|
890
|
-
// Delete the id from EVERY source dir (canonical + raw + alias), not just
|
|
891
|
-
// the first: the same envelope can sit in the canonical and the raw/legacy
|
|
892
|
-
// dir at once, and a single-delete leaves the copy to resurface on the
|
|
893
|
-
// next read. Return the first copy found.
|
|
894
|
-
let found;
|
|
895
|
-
for (const dir of dirs) {
|
|
896
|
-
const file = envelopeFile(dir, envelopeId);
|
|
897
|
-
if (!existsSync(file))
|
|
898
|
-
continue;
|
|
899
|
-
if (found === undefined) {
|
|
900
|
-
found = JSON.parse(readFileSync(file, "utf8"));
|
|
901
|
-
}
|
|
902
|
-
unlinkSync(file);
|
|
903
|
-
}
|
|
904
|
-
return found;
|
|
905
|
-
}, lockOpts);
|
|
906
|
-
}
|
|
907
|
-
function putOutboxMessage(actor, envelope) {
|
|
908
|
-
const validation = validateH2AEnvelope(envelope);
|
|
909
|
-
if (!validation.ok) {
|
|
910
|
-
throw new Error("putOutboxMessage: invalid H2A envelope — " + validation.errors.join("; "));
|
|
911
|
-
}
|
|
912
|
-
const dir = outboxDir(paths, actor);
|
|
913
|
-
ensureDir(dir);
|
|
914
|
-
lock(outboxLock(actor), () => {
|
|
915
|
-
writeFileSync(envelopeFile(dir, envelope.id), JSON.stringify(envelope, null, 2), "utf8");
|
|
916
|
-
}, lockOpts);
|
|
917
|
-
}
|
|
918
|
-
function readOutbox(actor) {
|
|
919
|
-
// canonical + raw fallback (like the inbox) so pre-case-fold outbox copies
|
|
920
|
-
// stay visible; deduped by envelope id.
|
|
921
|
-
const dirs = new Set([outboxDir(paths, actor), outboxDirRaw(paths, actor)]);
|
|
922
|
-
return mergeInboxDedup([...dirs].map((dir) => readEnvelopesFrom(dir)));
|
|
923
|
-
}
|
|
924
|
-
// DEC-070: validated routing to a subagent address. Unlike the raw mailbox
|
|
925
|
-
// put (which accepts any actor string), this asserts the subagent binding is
|
|
926
|
-
// registered before delivery, so an envelope can never be routed to a
|
|
927
|
-
// subagent that does not exist. Mailbox dirs are already safePathSegment-safe
|
|
928
|
-
// for the `~` separator (DEC-062/068), so the address routes unchanged.
|
|
929
|
-
function routeToSubagent(subagentId, envelope, mailbox = "inbox") {
|
|
930
|
-
if (!findSubagent(subagentId)) {
|
|
931
|
-
throw new Error(`Subagent not registered: ${subagentId}`);
|
|
932
|
-
}
|
|
933
|
-
if (subagentStatus(subagentId) === "revoked") {
|
|
934
|
-
throw new Error(`Subagent revoked: ${subagentId}`);
|
|
935
|
-
}
|
|
936
|
-
if (mailbox === "outbox")
|
|
937
|
-
putOutboxMessage(subagentId, envelope);
|
|
938
|
-
else
|
|
939
|
-
putInboxMessage(subagentId, envelope);
|
|
940
|
-
lock(registryLock, () => appendSubagentAudit({
|
|
941
|
-
subagent: subagentId,
|
|
942
|
-
type: "routed",
|
|
943
|
-
at: new Date().toISOString(),
|
|
944
|
-
envelopeId: envelope.id,
|
|
945
|
-
mailbox
|
|
946
|
-
}), lockOpts);
|
|
947
|
-
}
|
|
948
|
-
// DEC-070: parent fan-in — every registered subagent of a parent plus its
|
|
949
|
-
// inbox, so the parent (or an auditor) sees what was routed to each subagent.
|
|
950
|
-
// This is the "individually auditable" half of DEC-008's V2 goal.
|
|
951
|
-
function readSubagentInboxes(parentInstance) {
|
|
952
|
-
return listSubagentsOf(parentInstance).map((b) => ({
|
|
953
|
-
subagent: b.id,
|
|
954
|
-
envelopes: readInbox(b.id)
|
|
955
|
-
}));
|
|
956
|
-
}
|
|
957
|
-
return {
|
|
958
|
-
paths,
|
|
959
|
-
registerInstance,
|
|
960
|
-
listInstances,
|
|
961
|
-
findInstance,
|
|
962
|
-
addInstanceKey,
|
|
963
|
-
listInstanceKeys,
|
|
964
|
-
listKeyEvents,
|
|
965
|
-
revokeInstanceKey,
|
|
966
|
-
grantOrgMembership,
|
|
967
|
-
listOrgMembership,
|
|
968
|
-
offboardInstance,
|
|
969
|
-
listOffboards,
|
|
970
|
-
registerSubagent,
|
|
971
|
-
listSubagents,
|
|
972
|
-
findSubagent,
|
|
973
|
-
listSubagentsOf,
|
|
974
|
-
openNegotiation,
|
|
975
|
-
readNegotiation,
|
|
976
|
-
updateNegotiationStatus,
|
|
977
|
-
appendNegotiationEvent,
|
|
978
|
-
readNegotiationJournal,
|
|
979
|
-
declareConflitInteret,
|
|
980
|
-
derivePosturesConflit,
|
|
981
|
-
deriveDecisionDossier,
|
|
982
|
-
derivePostureConfiance,
|
|
983
|
-
stabilizeNegotiation,
|
|
984
|
-
putInboxMessage,
|
|
985
|
-
readInbox,
|
|
986
|
-
popInboxMessage,
|
|
987
|
-
putOutboxMessage,
|
|
988
|
-
readOutbox,
|
|
989
|
-
routeToSubagent,
|
|
990
|
-
readSubagentInboxes,
|
|
991
|
-
readSubagentAudit,
|
|
992
|
-
readSubagentAuditOf,
|
|
993
|
-
subagentStatus,
|
|
994
|
-
revokeSubagent
|
|
995
|
-
};
|
|
996
|
-
}
|
|
997
|
-
//# sourceMappingURL=store.js.map
|