@jungjaehoon/mama-core 2.4.1 → 4.1.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/README.md +76 -239
- package/db/migrations/030-case-first-memory-substrate.sql +13 -98
- package/db/migrations/034-add-connector-event-scope-columns.sql +9 -13
- package/db/migrations/039-add-connector-event-operator-ingest-seq.sql +9 -95
- package/db/migrations/062-refresh-connector-event-sequences.sql +9 -113
- package/db/migrations/063-refresh-legacy-connector-event-updates.sql +9 -32
- package/db/migrations/067-add-connector-event-source-entity-id.sql +9 -13
- package/db/migrations/069-create-registry-nodes.sql +52 -0
- package/db/migrations/070-record-identity.sql +35 -0
- package/db/migrations/071-service-operation-origins.sql +187 -0
- package/db/migrations/072-observation-versions.sql +29 -0
- package/db/migrations/073-registry-corrections.sql +45 -0
- package/db/migrations/074-work-graph-ref-kinds.sql +39 -0
- package/db/migrations/075-agent-judgments.sql +58 -0
- package/db/migrations/076-scoped-checkpoint-bindings.sql +15 -0
- package/db/migrations/077-legacy-record-kind.sql +6 -0
- package/db/migrations/078-source-ingest-commands.sql +18 -0
- package/db/migrations/079-twin-edge-relations.sql +43 -0
- package/db/migrations/080-restore-duration-days.sql +7 -0
- package/db/migrations/081-mailbox.sql +61 -0
- package/db/migrations/082-commitments-row-id.sql +12 -0
- package/db/migrations/083-observation-is-the-evidence.sql +49 -0
- package/db/migrations/084-scope-kind-is-not-the-cores-to-list.sql +34 -0
- package/db/migrations/085-a-migration-has-a-source.sql +37 -0
- package/db/migrations/086-the-mailbox-is-generic.sql +110 -0
- package/db/migrations/087-mailbox-payload.sql +5 -0
- package/db/migrations/088-native-input-journal.sql +14 -0
- package/db/migrations/089-native-turn-receipt-lookup.sql +3 -0
- package/db/migrations/090-model-run-native-inputs.sql +29 -0
- package/db/migrations/091-native-turn-results.sql +11 -0
- package/db/migrations/092-restore-decision-fts.sql +34 -0
- package/db/migrations/093-native-turn-primary-kind.sql +14 -0
- package/db/migrations/094-commitment-run-provenance.sql +9 -0
- package/db/migrations/095-model-run-native-input-identity.sql +39 -0
- package/db/migrations/096-drop-connector-event-index-fts.sql +9 -0
- package/db/migrations/097-native-input-json-validity.sql +37 -0
- package/db/migrations/098-workflow-memory-kind.sql +105 -0
- package/db/migrations/099-commitment-revision-graph.sql +2 -0
- package/dist/action-contracts.d.ts +212 -0
- package/dist/action-contracts.js +10 -0
- package/dist/api/catalog.d.ts +118 -0
- package/dist/api/catalog.js +1756 -0
- package/dist/api/dispatch.d.ts +76 -0
- package/dist/api/dispatch.js +372 -0
- package/dist/canonicalize.d.ts +2 -2
- package/dist/canonicalize.js +3 -3
- package/dist/client/client.d.ts +33 -0
- package/dist/client/client.js +123 -0
- package/dist/client/ipc.d.ts +81 -0
- package/dist/client/ipc.js +314 -0
- package/dist/db-adapter/base-adapter.d.ts +6 -66
- package/dist/db-adapter/base-adapter.js +6 -6
- package/dist/db-adapter/index.d.ts +5 -5
- package/dist/db-adapter/index.js +2 -5
- package/dist/db-adapter/node-sqlite-adapter.d.ts +85 -15
- package/dist/db-adapter/node-sqlite-adapter.js +1985 -570
- package/dist/db-manager.d.ts +53 -97
- package/dist/db-manager.js +121 -603
- package/dist/embedding/embedder.d.ts +132 -0
- package/dist/embedding/embedder.js +299 -0
- package/dist/identity/principal-repository.d.ts +2 -2
- package/dist/index.d.ts +43 -58
- package/dist/index.js +97 -145
- package/dist/{context-compile/channel-grant.d.ts → knowledge/access.d.ts} +12 -17
- package/dist/knowledge/access.js +663 -0
- package/dist/knowledge/case-errors.d.ts +31 -0
- package/dist/knowledge/case-errors.js +40 -0
- package/dist/{cases/search-rollup.d.ts → knowledge/case-search-rollup.d.ts} +5 -4
- package/dist/{cases/search-rollup.js → knowledge/case-search-rollup.js} +6 -8
- package/dist/{cases/store.d.ts → knowledge/case-store.d.ts} +3 -6
- package/dist/{cases/store.js → knowledge/case-store.js} +7 -42
- package/dist/{cases/timeline-range.d.ts → knowledge/case-timeline-range.d.ts} +3 -3
- package/dist/{cases/timeline-range.js → knowledge/case-timeline-range.js} +47 -25
- package/dist/{cases/types.d.ts → knowledge/case-types.d.ts} +8 -2
- package/dist/{cases/types.js → knowledge/case-types.js} +1 -1
- package/dist/knowledge/commitment-revision-migration.d.ts +4 -0
- package/dist/knowledge/commitment-revision-migration.js +50 -0
- package/dist/knowledge/commitments.d.ts +192 -0
- package/dist/knowledge/commitments.js +386 -0
- package/dist/knowledge/decision-edges.d.ts +70 -0
- package/dist/knowledge/decision-edges.js +123 -0
- package/dist/{agent-graph/types.d.ts → knowledge/graph-query.d.ts} +80 -70
- package/dist/knowledge/graph-query.js +1585 -0
- package/dist/knowledge/identity.d.ts +16 -0
- package/dist/knowledge/identity.js +22 -0
- package/dist/knowledge/index.d.ts +47 -0
- package/dist/knowledge/index.js +90 -0
- package/dist/knowledge/judgment-edge.d.ts +6 -0
- package/dist/knowledge/judgment-edge.js +22 -0
- package/dist/knowledge/judgments.d.ts +118 -0
- package/dist/knowledge/judgments.js +802 -0
- package/dist/knowledge/observations.d.ts +160 -0
- package/dist/knowledge/observations.js +449 -0
- package/dist/knowledge/question-type.d.ts +34 -0
- package/dist/knowledge/question-type.js +70 -0
- package/dist/{search → knowledge}/ranker-features.js +1 -1
- package/dist/{search → knowledge}/search-quality.d.ts +0 -1
- package/dist/knowledge/search.d.ts +38 -0
- package/dist/knowledge/search.js +91 -0
- package/dist/knowledge/source-ingest.d.ts +65 -0
- package/dist/knowledge/source-ingest.js +174 -0
- package/dist/{edges/types.d.ts → knowledge/twin-edge-types.d.ts} +31 -4
- package/dist/{edges/types.js → knowledge/twin-edge-types.js} +13 -2
- package/dist/knowledge/work-dates.d.ts +49 -0
- package/dist/knowledge/work-dates.js +120 -0
- package/dist/mama-api.d.ts +114 -778
- package/dist/mama-api.js +313 -2461
- package/dist/memory/api.d.ts +264 -24
- package/dist/memory/api.js +1633 -843
- package/dist/memory/bootstrap-builder.d.ts +2 -1
- package/dist/memory/bootstrap-builder.js +5 -5
- package/dist/memory/channel-summary-state-store.d.ts +3 -2
- package/dist/memory/channel-summary-state-store.js +53 -51
- package/dist/memory/channel-summary-store.d.ts +5 -3
- package/dist/memory/channel-summary-store.js +3 -8
- package/dist/memory/dashboard-read.d.ts +46 -0
- package/dist/memory/dashboard-read.js +94 -0
- package/dist/memory/event-store.d.ts +3 -3
- package/dist/memory/event-store.js +3 -10
- package/dist/memory/finding-store.d.ts +3 -2
- package/dist/memory/finding-store.js +2 -7
- package/dist/memory/graph-read.d.ts +52 -0
- package/dist/memory/graph-read.js +130 -0
- package/dist/memory/judgment-types.d.ts +394 -0
- package/dist/memory/judgment-types.js +16 -0
- package/dist/memory/provenance-audit.d.ts +5 -2
- package/dist/memory/provenance-audit.js +8 -14
- package/dist/memory/provenance-live.d.ts +141 -0
- package/dist/memory/provenance-live.js +409 -0
- package/dist/memory/provenance-query.d.ts +5 -4
- package/dist/memory/provenance-query.js +21 -29
- package/dist/memory/provenance-resolver.d.ts +191 -0
- package/dist/memory/provenance-resolver.js +155 -0
- package/dist/memory/provenance.d.ts +1 -11
- package/dist/memory/provenance.js +2 -33
- package/dist/memory/recall-sanitize.d.ts +55 -0
- package/dist/memory/recall-sanitize.js +144 -0
- package/dist/memory/secret-filter.d.ts +32 -0
- package/dist/memory/secret-filter.js +126 -0
- package/dist/memory/truth-store.d.ts +2 -1
- package/dist/memory/truth-store.js +1 -4
- package/dist/memory/types.d.ts +74 -24
- package/dist/memory/types.js +75 -5
- package/dist/memory/write-adapters.d.ts +98 -0
- package/dist/memory/write-adapters.js +242 -0
- package/dist/operations/owner-action-effects.d.ts +53 -0
- package/dist/operations/owner-action-effects.js +96 -0
- package/dist/provenance/source-ref.d.ts +1 -1
- package/dist/provenance/source-ref.js +0 -2
- package/dist/registry/corrections.d.ts +31 -0
- package/dist/registry/corrections.js +332 -0
- package/dist/registry/record-identity.d.ts +49 -0
- package/dist/registry/record-identity.js +133 -0
- package/dist/registry/store.d.ts +101 -0
- package/dist/registry/store.js +395 -0
- package/dist/registry/types.d.ts +28 -0
- package/dist/registry/types.js +5 -0
- package/dist/relevance-scorer.js +2 -2
- package/dist/runtime/agent-event-bus.d.ts +50 -0
- package/dist/runtime/agent-event-bus.js +101 -0
- package/dist/runtime/drivers/claude-cli-wrapper.d.ts +152 -0
- package/dist/runtime/drivers/claude-cli-wrapper.js +375 -0
- package/dist/runtime/drivers/claude-effort.d.ts +4 -0
- package/dist/runtime/drivers/claude-effort.js +16 -0
- package/dist/runtime/drivers/cli-arg-redaction.d.ts +2 -0
- package/dist/runtime/drivers/cli-arg-redaction.js +18 -0
- package/dist/runtime/drivers/cli-secret-redaction.d.ts +17 -0
- package/dist/runtime/drivers/cli-secret-redaction.js +126 -0
- package/dist/runtime/drivers/codex-app-server-process.d.ts +277 -0
- package/dist/runtime/drivers/codex-app-server-process.js +2342 -0
- package/dist/runtime/drivers/codex-auxiliary-tools.d.ts +17 -0
- package/dist/runtime/drivers/codex-auxiliary-tools.js +267 -0
- package/dist/runtime/drivers/codex-home.d.ts +20 -0
- package/dist/runtime/drivers/codex-home.js +674 -0
- package/dist/runtime/drivers/codex-thread-registry.d.ts +63 -0
- package/dist/runtime/drivers/codex-thread-registry.js +433 -0
- package/dist/runtime/drivers/persistent-cli-adapter.d.ts +160 -0
- package/dist/runtime/drivers/persistent-cli-adapter.js +396 -0
- package/dist/runtime/drivers/persistent-cli-process.d.ts +425 -0
- package/dist/runtime/drivers/persistent-cli-process.js +1529 -0
- package/dist/runtime/drivers/types.d.ts +456 -0
- package/dist/runtime/drivers/types.js +110 -0
- package/dist/runtime/mailbox.d.ts +223 -0
- package/dist/runtime/mailbox.js +582 -0
- package/dist/runtime/model-run-store.d.ts +28 -0
- package/dist/{model-runs/store.js → runtime/model-run-store.js} +81 -41
- package/dist/{model-runs/types.d.ts → runtime/model-run-types.d.ts} +33 -4
- package/dist/{model-runs/types.js → runtime/model-run-types.js} +1 -1
- package/dist/runtime/native-effect-observer.d.ts +22 -0
- package/dist/runtime/native-effect-observer.js +103 -0
- package/dist/runtime/native-input-journal.d.ts +46 -0
- package/dist/runtime/native-input-journal.js +308 -0
- package/dist/runtime/native-prompt.d.ts +118 -0
- package/dist/runtime/native-prompt.js +496 -0
- package/dist/runtime/native-session.d.ts +10 -0
- package/dist/{agent-graph/index.js → runtime/native-session.js} +9 -6
- package/dist/runtime/native-tool-trace-observer.d.ts +5 -0
- package/dist/runtime/native-tool-trace-observer.js +65 -0
- package/dist/runtime/native-turn.d.ts +272 -0
- package/dist/runtime/native-turn.js +1041 -0
- package/dist/runtime/operations.d.ts +236 -0
- package/dist/runtime/operations.js +565 -0
- package/dist/runtime/post-tool-handler.d.ts +74 -0
- package/dist/runtime/post-tool-handler.js +137 -0
- package/dist/runtime/prompt-layers.d.ts +126 -0
- package/dist/runtime/prompt-layers.js +239 -0
- package/dist/runtime/runtime-process.d.ts +102 -0
- package/dist/runtime/runtime-process.js +206 -0
- package/dist/runtime/runtime.d.ts +278 -0
- package/dist/runtime/runtime.js +563 -0
- package/dist/runtime/session-pool.d.ts +176 -0
- package/dist/runtime/session-pool.js +369 -0
- package/dist/runtime/stimulus-payload.d.ts +5 -0
- package/dist/runtime/stimulus-payload.js +50 -0
- package/dist/runtime/subagent-bridge.d.ts +24 -0
- package/dist/{agent-graph/types.js → runtime/subagent-bridge.js} +1 -1
- package/dist/runtime/text-completion.d.ts +22 -0
- package/dist/{agent-situation/types.js → runtime/text-completion.js} +1 -1
- package/dist/runtime/token-estimator.d.ts +25 -0
- package/dist/runtime/token-estimator.js +75 -0
- package/dist/runtime/tool-trace-store.d.ts +22 -0
- package/dist/{model-runs → runtime}/tool-trace-store.js +46 -27
- package/dist/runtime/trace-summary.d.ts +5 -0
- package/dist/runtime/trace-summary.js +74 -0
- package/dist/runtime/turn-text.d.ts +23 -0
- package/dist/runtime/turn-text.js +41 -0
- package/dist/storage/database.d.ts +67 -0
- package/dist/storage/database.js +158 -0
- package/package.json +20 -34
- package/dist/agent-graph/alias-write.d.ts +0 -3
- package/dist/agent-graph/alias-write.js +0 -234
- package/dist/agent-graph/entity-resolve.d.ts +0 -3
- package/dist/agent-graph/entity-resolve.js +0 -271
- package/dist/agent-graph/errors.d.ts +0 -4
- package/dist/agent-graph/errors.js +0 -11
- package/dist/agent-graph/graph-query.d.ts +0 -5
- package/dist/agent-graph/graph-query.js +0 -360
- package/dist/agent-graph/index.d.ts +0 -6
- package/dist/agent-situation/builder.d.ts +0 -8
- package/dist/agent-situation/builder.js +0 -239
- package/dist/agent-situation/cache-key.d.ts +0 -4
- package/dist/agent-situation/cache-key.js +0 -64
- package/dist/agent-situation/index.d.ts +0 -7
- package/dist/agent-situation/index.js +0 -28
- package/dist/agent-situation/packet-store.d.ts +0 -27
- package/dist/agent-situation/packet-store.js +0 -228
- package/dist/agent-situation/ranking-policy.d.ts +0 -18
- package/dist/agent-situation/ranking-policy.js +0 -75
- package/dist/agent-situation/source-readers.d.ts +0 -52
- package/dist/agent-situation/source-readers.js +0 -300
- package/dist/agent-situation/types.d.ts +0 -195
- package/dist/cases/wiki-page-index.d.ts +0 -37
- package/dist/cases/wiki-page-index.js +0 -201
- package/dist/config-loader.d.ts +0 -68
- package/dist/config-loader.js +0 -239
- package/dist/connectors/event-index.d.ts +0 -24
- package/dist/connectors/event-index.js +0 -245
- package/dist/connectors/raw-query.d.ts +0 -40
- package/dist/connectors/raw-query.js +0 -412
- package/dist/connectors/types.d.ts +0 -117
- package/dist/connectors/types.js +0 -3
- package/dist/context-compile/boundary-defaults.d.ts +0 -12
- package/dist/context-compile/boundary-defaults.js +0 -105
- package/dist/context-compile/channel-grant.js +0 -66
- package/dist/context-compile/compiler-policy.d.ts +0 -39
- package/dist/context-compile/compiler-policy.js +0 -147
- package/dist/context-compile/compiler.d.ts +0 -18
- package/dist/context-compile/compiler.js +0 -404
- package/dist/context-compile/index.d.ts +0 -10
- package/dist/context-compile/index.js +0 -26
- package/dist/context-compile/packet-store.d.ts +0 -8
- package/dist/context-compile/packet-store.js +0 -239
- package/dist/context-compile/ref.d.ts +0 -7
- package/dist/context-compile/ref.js +0 -102
- package/dist/context-compile/source-readers.d.ts +0 -70
- package/dist/context-compile/source-readers.js +0 -840
- package/dist/context-compile/types.d.ts +0 -155
- package/dist/context-compile/types.js +0 -11
- package/dist/context-compile/visibility.d.ts +0 -21
- package/dist/context-compile/visibility.js +0 -224
- package/dist/decision-tracker.d.ts +0 -206
- package/dist/decision-tracker.js +0 -481
- package/dist/edges/ref-validation.d.ts +0 -7
- package/dist/edges/ref-validation.js +0 -321
- package/dist/edges/store.d.ts +0 -8
- package/dist/edges/store.js +0 -129
- package/dist/embedding-client.d.ts +0 -28
- package/dist/embedding-client.js +0 -53
- package/dist/embedding-server/index.d.ts +0 -67
- package/dist/embedding-server/index.js +0 -405
- package/dist/embedding-server/mobile/auth.d.ts +0 -44
- package/dist/embedding-server/mobile/auth.js +0 -150
- package/dist/embedding-server/mobile/daemon.d.ts +0 -129
- package/dist/embedding-server/mobile/daemon.js +0 -313
- package/dist/embedding-server/mobile/output-parser.d.ts +0 -115
- package/dist/embedding-server/mobile/output-parser.js +0 -241
- package/dist/embedding-server/mobile/session-api.d.ts +0 -57
- package/dist/embedding-server/mobile/session-api.js +0 -261
- package/dist/embedding-server/mobile/session-manager.d.ts +0 -138
- package/dist/embedding-server/mobile/session-manager.js +0 -378
- package/dist/embedding-server/mobile/websocket-handler.d.ts +0 -137
- package/dist/embedding-server/mobile/websocket-handler.js +0 -486
- package/dist/embeddings.d.ts +0 -68
- package/dist/embeddings.js +0 -244
- package/dist/entities/audit-metrics.d.ts +0 -68
- package/dist/entities/audit-metrics.js +0 -124
- package/dist/entities/candidate-generator.d.ts +0 -8
- package/dist/entities/candidate-generator.js +0 -24
- package/dist/entities/entity-linked-decision-counts.d.ts +0 -2
- package/dist/entities/entity-linked-decision-counts.js +0 -29
- package/dist/entities/entity-list.d.ts +0 -22
- package/dist/entities/entity-list.js +0 -3
- package/dist/entities/entity-orphan-list.d.ts +0 -17
- package/dist/entities/entity-orphan-list.js +0 -3
- package/dist/entities/entity-search.d.ts +0 -20
- package/dist/entities/entity-search.js +0 -3
- package/dist/entities/errors.d.ts +0 -55
- package/dist/entities/errors.js +0 -100
- package/dist/entities/lineage-store.d.ts +0 -28
- package/dist/entities/lineage-store.js +0 -204
- package/dist/entities/normalization.d.ts +0 -14
- package/dist/entities/normalization.js +0 -63
- package/dist/entities/policy-store.d.ts +0 -8
- package/dist/entities/policy-store.js +0 -75
- package/dist/entities/policy-types.d.ts +0 -72
- package/dist/entities/policy-types.js +0 -59
- package/dist/entities/projection.d.ts +0 -8
- package/dist/entities/projection.js +0 -82
- package/dist/entities/provenance-query.d.ts +0 -32
- package/dist/entities/provenance-query.js +0 -3
- package/dist/entities/read-identity.d.ts +0 -31
- package/dist/entities/read-identity.js +0 -264
- package/dist/entities/recall-bridge.d.ts +0 -5
- package/dist/entities/recall-bridge.js +0 -171
- package/dist/entities/resolution-engine.d.ts +0 -8
- package/dist/entities/resolution-engine.js +0 -3
- package/dist/entities/rollback-preview.d.ts +0 -35
- package/dist/entities/rollback-preview.js +0 -3
- package/dist/entities/source-locator.d.ts +0 -3
- package/dist/entities/source-locator.js +0 -32
- package/dist/entities/store.d.ts +0 -69
- package/dist/entities/store.js +0 -544
- package/dist/entities/types.d.ts +0 -166
- package/dist/entities/types.js +0 -30
- package/dist/memory/extraction-prompt.d.ts +0 -4
- package/dist/memory/extraction-prompt.js +0 -89
- package/dist/memory/scope-backfill.d.ts +0 -14
- package/dist/memory/scope-backfill.js +0 -3
- package/dist/memory/scope-store.d.ts +0 -4
- package/dist/memory/scope-store.js +0 -9
- package/dist/memory-store.d.ts +0 -87
- package/dist/memory-store.js +0 -78
- package/dist/model-runs/store.d.ts +0 -13
- package/dist/model-runs/tool-trace-store.d.ts +0 -8
- package/dist/notification-manager.d.ts +0 -7
- package/dist/notification-manager.js +0 -12
- package/dist/outcome-tracker.d.ts +0 -151
- package/dist/outcome-tracker.js +0 -271
- package/dist/query-intent.d.ts +0 -23
- package/dist/query-intent.js +0 -114
- package/dist/runtime/trusted-provenance.d.ts +0 -3
- package/dist/runtime/trusted-provenance.js +0 -6
- package/dist/search/question-type.d.ts +0 -5
- package/dist/search/question-type.js +0 -41
- package/dist/test-utils.d.ts +0 -80
- package/dist/test-utils.js +0 -218
- package/dist/tier-validator.d.ts +0 -43
- package/dist/tier-validator.js +0 -159
- package/dist/time-formatter.d.ts +0 -25
- package/dist/time-formatter.js +0 -93
- /package/dist/{search → knowledge}/feedback-store.d.ts +0 -0
- /package/dist/{search → knowledge}/feedback-store.js +0 -0
- /package/dist/{search → knowledge}/ranker-features.d.ts +0 -0
- /package/dist/{search → knowledge}/ranker-rescore.d.ts +0 -0
- /package/dist/{search → knowledge}/ranker-rescore.js +0 -0
- /package/dist/{search → knowledge}/ranker-trainer.d.ts +0 -0
- /package/dist/{search → knowledge}/ranker-trainer.js +0 -0
- /package/dist/{search → knowledge}/search-quality.js +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action dispatch — input validation, exactly one exec call, one result shape.
|
|
3
|
+
*
|
|
4
|
+
* §4.2: dispatch never retries, never guesses sameness, and never turns a thrown
|
|
5
|
+
* error into an empty success. Knowledge-boundary errors keep their own code so
|
|
6
|
+
* the caller can tell a bad call from a denied one from a broken store.
|
|
7
|
+
*/
|
|
8
|
+
import type { ActionCall, ActionContract, ActionResult, ActionSchemaObject } from '../action-contracts.js';
|
|
9
|
+
import { type ActionCatalog, type ActionContext } from './catalog.js';
|
|
10
|
+
export type ActionDispatcher = ((call: ActionCall, context: ActionContext) => Promise<ActionResult>) & {
|
|
11
|
+
/**
|
|
12
|
+
* The actions this dispatcher serves, as their contracts. Not a grant: a fact
|
|
13
|
+
* about the catalog it was built from, which an in-process caller with no
|
|
14
|
+
* stated grant of its own stands on. A caller arriving over the socket is a
|
|
15
|
+
* principal and is compared against the grant its credential resolves to.
|
|
16
|
+
*
|
|
17
|
+
* The contracts and not just the names, because a caller that must tell a model
|
|
18
|
+
* what it may call needs to say what each one is for, and deriving that from a
|
|
19
|
+
* second list is how the two stop agreeing.
|
|
20
|
+
*/
|
|
21
|
+
readonly contracts: readonly ActionContract[];
|
|
22
|
+
};
|
|
23
|
+
export interface DispatcherOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Called when a recallable write carries instruction-shaped content that is
|
|
26
|
+
* not secret-shaped enough to refuse. Observation, never a gate: the write
|
|
27
|
+
* proceeds and the host records what it saw. Absent means nobody is
|
|
28
|
+
* watching, which is honest rather than silently safe.
|
|
29
|
+
*/
|
|
30
|
+
observeWriteWarning?: (input: {
|
|
31
|
+
action: string;
|
|
32
|
+
principalId: string;
|
|
33
|
+
warnings: readonly string[];
|
|
34
|
+
}) => void;
|
|
35
|
+
/**
|
|
36
|
+
* The receipt for one call: what was asked, what came back, how long it took.
|
|
37
|
+
*
|
|
38
|
+
* This ran in the gateway tool executor, which meant a call arriving any other
|
|
39
|
+
* way -- a program over the socket, a scheduled run -- left no row at all,
|
|
40
|
+
* while lane verification reads exactly those rows to decide whether a lane
|
|
41
|
+
* did what it claimed. One call, one receipt, written where every caller
|
|
42
|
+
* passes.
|
|
43
|
+
*
|
|
44
|
+
* It may return a reference to the receipt it wrote; dispatch hands that back
|
|
45
|
+
* to the caller as the result's `experienceRef` so an agent can read its own
|
|
46
|
+
* run. A
|
|
47
|
+
* thrown observer never turns a completed call into a failed one -- the
|
|
48
|
+
* receipt is bookkeeping and the answer is the answer -- but it is not
|
|
49
|
+
* swallowed silently either: the host states what it does with it.
|
|
50
|
+
*/
|
|
51
|
+
observeCall?: (input: {
|
|
52
|
+
action: string;
|
|
53
|
+
/** The command id the caller issued, when it issued one. */
|
|
54
|
+
operationId: string | undefined;
|
|
55
|
+
input: unknown;
|
|
56
|
+
result: ActionResult;
|
|
57
|
+
durationMs: number;
|
|
58
|
+
context: ActionContext;
|
|
59
|
+
}) => Promise<string | undefined> | string | undefined;
|
|
60
|
+
}
|
|
61
|
+
export declare function createDispatcher(catalog: ActionCatalog, options?: DispatcherOptions): ActionDispatcher;
|
|
62
|
+
/**
|
|
63
|
+
* Time one call and hand its receipt's reference back on the result.
|
|
64
|
+
*
|
|
65
|
+
* Exported because a caller that composes its own dispatch -- a test standing a
|
|
66
|
+
* spy where the catalog would be -- must observe through the SAME wrapper. Two
|
|
67
|
+
* copies of "when is a receipt written and where does its ref land" is two
|
|
68
|
+
* answers to one question.
|
|
69
|
+
*/
|
|
70
|
+
export declare function withCallReceipt<D extends (call: ActionCall, context: ActionContext) => Promise<ActionResult>>(dispatch: D, observeCall: NonNullable<DispatcherOptions['observeCall']>): D;
|
|
71
|
+
/**
|
|
72
|
+
* Structural validation against the contract schema — the deep semantic rules
|
|
73
|
+
* (ref existence, history/direction legality per view) still belong to exec.
|
|
74
|
+
*/
|
|
75
|
+
export declare function validateInput(schema: ActionSchemaObject, value: unknown, path: string): string | null;
|
|
76
|
+
//# sourceMappingURL=dispatch.d.ts.map
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDispatcher = createDispatcher;
|
|
4
|
+
exports.withCallReceipt = withCallReceipt;
|
|
5
|
+
exports.validateInput = validateInput;
|
|
6
|
+
const judgments_js_1 = require("../knowledge/judgments.js");
|
|
7
|
+
const access_js_1 = require("../knowledge/access.js");
|
|
8
|
+
const graph_query_js_1 = require("../knowledge/graph-query.js");
|
|
9
|
+
const operations_js_1 = require("../runtime/operations.js");
|
|
10
|
+
const store_js_1 = require("../registry/store.js");
|
|
11
|
+
const catalog_js_1 = require("./catalog.js");
|
|
12
|
+
const secret_filter_js_1 = require("../memory/secret-filter.js");
|
|
13
|
+
function createDispatcher(catalog, options = {}) {
|
|
14
|
+
const runCall = async (call, context) => {
|
|
15
|
+
let registration;
|
|
16
|
+
try {
|
|
17
|
+
registration = catalog.entry(call.action);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (error instanceof catalog_js_1.UnknownActionError) {
|
|
21
|
+
return fail(call, 'unknown_action', 'unknown_action', error.message);
|
|
22
|
+
}
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
const invalid = validateInput(registration.contract.inputSchema, call.input, 'input');
|
|
26
|
+
if (invalid !== null) {
|
|
27
|
+
return fail(call, 'invalid_input', 'invalid_input', invalid);
|
|
28
|
+
}
|
|
29
|
+
// Authority is compared here, before exec, so an ungranted action never
|
|
30
|
+
// reaches an action body. The grant is the principal's configured list —
|
|
31
|
+
// the call cannot widen it, and there is no wildcard to widen it with.
|
|
32
|
+
const granted = context.access.actions;
|
|
33
|
+
if (!Array.isArray(granted)) {
|
|
34
|
+
return fail(call, 'denied', 'action_grant_missing', `principal ${context.access.principalId} states no action grant`);
|
|
35
|
+
}
|
|
36
|
+
if (!granted.includes(registration.contract.name)) {
|
|
37
|
+
return fail(call, 'denied', 'action_not_granted', `principal ${context.access.principalId} may not call ${call.action}`);
|
|
38
|
+
}
|
|
39
|
+
const readsConnector = registration.contract.readsConnector;
|
|
40
|
+
if (readsConnector !== undefined) {
|
|
41
|
+
const named = 'fixed' in readsConnector
|
|
42
|
+
? readsConnector.fixed
|
|
43
|
+
: call.input?.[readsConnector.fromInput];
|
|
44
|
+
if (typeof named !== 'string' || named === '') {
|
|
45
|
+
return fail(call, 'invalid_input', 'connector_unnamed', `${call.action} reads a connector but its ${'fromInput' in readsConnector ? readsConnector.fromInput : 'connector'} names none`);
|
|
46
|
+
}
|
|
47
|
+
// Absent is NO connector read, never all of them - the same fail-closed
|
|
48
|
+
// reading allowanceFromAccess gives the memory window below.
|
|
49
|
+
const grantedConnectors = context.access.connectors ?? [];
|
|
50
|
+
if (!grantedConnectors.includes(named)) {
|
|
51
|
+
// Return the caller's own connector grant to that caller, so it can correct
|
|
52
|
+
// the source name without exposing another principal's grant.
|
|
53
|
+
return fail(call, 'denied', 'connector_out_of_scope', `principal ${context.access.principalId} may not read ${named}; readable connectors: ${[...grantedConnectors].sort().join(', ') || 'none'}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (registration.contract.recallableWrite === true) {
|
|
57
|
+
const scan = (0, secret_filter_js_1.scanMemoryWriteInput)((call.input ?? {}));
|
|
58
|
+
if (scan.warnings.length > 0) {
|
|
59
|
+
options.observeWriteWarning?.({
|
|
60
|
+
action: call.action,
|
|
61
|
+
principalId: context.access.principalId,
|
|
62
|
+
warnings: scan.warnings,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (!scan.clean) {
|
|
66
|
+
// The matched pattern NAMES are safe to return; the matched text is not.
|
|
67
|
+
return fail(call, 'invalid_input',
|
|
68
|
+
// The label operators already grep for. The check moved; its name
|
|
69
|
+
// did not.
|
|
70
|
+
'secret_material_refused', `refusing to persist content matching ${scan.matches.join(', ')} — a secret written here comes back through recall`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const data = await registration.exec(call.input, {
|
|
75
|
+
...context,
|
|
76
|
+
readAllowance: readAllowanceFor(context),
|
|
77
|
+
operationId: call.operationId,
|
|
78
|
+
});
|
|
79
|
+
const result = { status: 'completed', data };
|
|
80
|
+
if (call.operationId !== undefined) {
|
|
81
|
+
result.operationId = call.operationId;
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return fail(call, failureKind(error), errorCode(error), errorMessage(error));
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const dispatch = options.observeCall === undefined ? runCall : withCallReceipt(runCall, options.observeCall);
|
|
90
|
+
return Object.assign(dispatch, { contracts: catalog.list() });
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Time one call and hand its receipt's reference back on the result.
|
|
94
|
+
*
|
|
95
|
+
* Exported because a caller that composes its own dispatch -- a test standing a
|
|
96
|
+
* spy where the catalog would be -- must observe through the SAME wrapper. Two
|
|
97
|
+
* copies of "when is a receipt written and where does its ref land" is two
|
|
98
|
+
* answers to one question.
|
|
99
|
+
*/
|
|
100
|
+
function withCallReceipt(dispatch, observeCall) {
|
|
101
|
+
const observed = async (call, context) => {
|
|
102
|
+
const startedAt = Date.now();
|
|
103
|
+
let result;
|
|
104
|
+
try {
|
|
105
|
+
result = await dispatch(call, context);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
// One call, one receipt. A dispatch that throws instead of failing is the
|
|
109
|
+
// call most worth a row, not the one exempt from it, so the throw is
|
|
110
|
+
// written as the failure it is and then continues on its way.
|
|
111
|
+
try {
|
|
112
|
+
await observeCall({
|
|
113
|
+
action: call.action,
|
|
114
|
+
operationId: call.operationId,
|
|
115
|
+
input: call.input,
|
|
116
|
+
result: {
|
|
117
|
+
status: 'failed',
|
|
118
|
+
error: {
|
|
119
|
+
kind: failureKind(error),
|
|
120
|
+
code: thrownCode(error),
|
|
121
|
+
message: errorMessage(error),
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
durationMs: Date.now() - startedAt,
|
|
125
|
+
context,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// The call's own throw is the answer; a receipt that also fails must
|
|
130
|
+
// not replace it with a different cause.
|
|
131
|
+
}
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
const ref = await observeCall({
|
|
135
|
+
action: call.action,
|
|
136
|
+
operationId: call.operationId,
|
|
137
|
+
input: call.input,
|
|
138
|
+
result,
|
|
139
|
+
durationMs: Date.now() - startedAt,
|
|
140
|
+
context,
|
|
141
|
+
});
|
|
142
|
+
// The receipt points at itself, so the agent that made the call can read
|
|
143
|
+
// what its own run recorded. On the envelope, never inside `data`: the
|
|
144
|
+
// action owns that shape, and a refusal -- the call most worth reading back
|
|
145
|
+
// -- has no `data` to put it in.
|
|
146
|
+
return ref === undefined ? result : { ...result, experienceRef: ref };
|
|
147
|
+
};
|
|
148
|
+
// Whatever the wrapped dispatch declares about itself -- `contracts`, which a
|
|
149
|
+
// caller with no grant of its own stands on -- survives the wrapping. A
|
|
150
|
+
// wrapper that drops it silently narrows the caller's authority.
|
|
151
|
+
return Object.assign(observed, dispatch);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The read window a principal's own grant states.
|
|
155
|
+
*
|
|
156
|
+
* A citation must not out-read reading. That rule used to hold only where a host
|
|
157
|
+
* assembled the window from a verified envelope, which meant a call arriving
|
|
158
|
+
* without one -- a program over the socket, a scheduled run -- got no window at
|
|
159
|
+
* all. The grant already says which connectors, channels and projects this
|
|
160
|
+
* principal may read; the same four fields ARE the window.
|
|
161
|
+
*
|
|
162
|
+
* A host that composed a narrower window still wins: this fills in only when the
|
|
163
|
+
* context carries none. And it fails closed the same way -- a principal whose
|
|
164
|
+
* grant names no connectors gets `connectors: []`, which is no raw events.
|
|
165
|
+
*/
|
|
166
|
+
function allowanceFromAccess(access) {
|
|
167
|
+
const connectors = access.connectors ?? [];
|
|
168
|
+
const wideConnectors = (access.connectorWideRead ?? []).filter((name) => connectors.includes(name));
|
|
169
|
+
// An ordinary connector grant without a tenant is not a raw read window.
|
|
170
|
+
// An explicit connector-wide read is different: the owner stored-source
|
|
171
|
+
// reader already admits that connector across its retained channels. Keep
|
|
172
|
+
// only that exact intersection, never an unrelated narrow connector.
|
|
173
|
+
if (connectors.length > 0 && (access.tenantId ?? null) === null && wideConnectors.length === 0) {
|
|
174
|
+
return { connectors: [], tenantId: null };
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
connectors: (access.tenantId ?? null) === null ? wideConnectors : connectors,
|
|
178
|
+
...(wideConnectors.length === 0 ? {} : { wideConnectors }),
|
|
179
|
+
...(access.channels ? { channels: access.channels } : {}),
|
|
180
|
+
...(access.projectRefs === undefined
|
|
181
|
+
? {}
|
|
182
|
+
: { projectIds: access.projectRefs.map((ref) => ref.id) }),
|
|
183
|
+
tenantId: access.tenantId ?? null,
|
|
184
|
+
maxObservedMs: access.maxObservedMs ?? null,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function readAllowanceFor(context) {
|
|
188
|
+
const allowance = context.readAllowance ?? allowanceFromAccess(context.access);
|
|
189
|
+
const ceiling = context.session?.replaySourceEndMs;
|
|
190
|
+
if (ceiling === undefined)
|
|
191
|
+
return allowance;
|
|
192
|
+
if (!Number.isSafeInteger(ceiling) || ceiling < 0) {
|
|
193
|
+
throw new judgments_js_1.JudgmentError('REPLAY_SOURCE_CEILING_INVALID', 'Replay source ceiling must be a nonnegative epoch millisecond integer');
|
|
194
|
+
}
|
|
195
|
+
const stated = allowance.maxSourceMs;
|
|
196
|
+
if (stated === undefined || stated === null) {
|
|
197
|
+
return { ...allowance, maxSourceMs: ceiling };
|
|
198
|
+
}
|
|
199
|
+
if (!Number.isSafeInteger(stated) || stated < 0) {
|
|
200
|
+
throw new judgments_js_1.JudgmentError('REPLAY_SOURCE_CEILING_INVALID', 'Read allowance source ceiling must be a nonnegative epoch millisecond integer');
|
|
201
|
+
}
|
|
202
|
+
return { ...allowance, maxSourceMs: Math.min(stated, ceiling) };
|
|
203
|
+
}
|
|
204
|
+
function fail(call, kind, code, message) {
|
|
205
|
+
const result = {
|
|
206
|
+
status: 'failed',
|
|
207
|
+
error: { kind, code, message },
|
|
208
|
+
};
|
|
209
|
+
if (call.operationId !== undefined) {
|
|
210
|
+
result.operationId = call.operationId;
|
|
211
|
+
}
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
function failureKind(error) {
|
|
215
|
+
if (error instanceof access_js_1.TwinRefNotVisibleError || thrownCode(error) === 'denied') {
|
|
216
|
+
return 'denied';
|
|
217
|
+
}
|
|
218
|
+
if (error instanceof graph_query_js_1.AgentGraphValidationError ||
|
|
219
|
+
error instanceof secret_filter_js_1.SecretMaterialRefusedError ||
|
|
220
|
+
thrownCode(error) === 'invalid_input') {
|
|
221
|
+
return 'invalid_input';
|
|
222
|
+
}
|
|
223
|
+
if (error instanceof judgments_js_1.JudgmentError ||
|
|
224
|
+
error instanceof operations_js_1.OperationError ||
|
|
225
|
+
error instanceof store_js_1.RegistryError) {
|
|
226
|
+
return 'failed';
|
|
227
|
+
}
|
|
228
|
+
return 'internal';
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* A thrown value's own `code`, when it states one.
|
|
232
|
+
*
|
|
233
|
+
* `errorCode` answers with the class NAME for a custom Error, which is what a
|
|
234
|
+
* failure result wants. A receipt wants the cause, and a thrower that carries a
|
|
235
|
+
* structured `code` has already named it.
|
|
236
|
+
*/
|
|
237
|
+
function thrownCode(error) {
|
|
238
|
+
const stated = error?.code;
|
|
239
|
+
return typeof stated === 'string' && stated !== '' ? stated : errorCode(error);
|
|
240
|
+
}
|
|
241
|
+
function errorCode(error) {
|
|
242
|
+
if (error instanceof judgments_js_1.JudgmentError ||
|
|
243
|
+
error instanceof operations_js_1.OperationError ||
|
|
244
|
+
error instanceof store_js_1.RegistryError) {
|
|
245
|
+
return error.code;
|
|
246
|
+
}
|
|
247
|
+
if (error instanceof Error && error.name !== 'Error') {
|
|
248
|
+
return error.name;
|
|
249
|
+
}
|
|
250
|
+
return 'internal_error';
|
|
251
|
+
}
|
|
252
|
+
function errorMessage(error) {
|
|
253
|
+
return error instanceof Error ? error.message : String(error);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Structural validation against the contract schema — the deep semantic rules
|
|
257
|
+
* (ref existence, history/direction legality per view) still belong to exec.
|
|
258
|
+
*/
|
|
259
|
+
function validateInput(schema, value, path) {
|
|
260
|
+
if (schema.const !== undefined && value !== schema.const) {
|
|
261
|
+
return `${path} must be ${JSON.stringify(schema.const)}.`;
|
|
262
|
+
}
|
|
263
|
+
if (schema.enum !== undefined && !schema.enum.includes(value)) {
|
|
264
|
+
return `${path} must be one of ${schema.enum.map(String).join(', ')}.`;
|
|
265
|
+
}
|
|
266
|
+
if (schema.type !== undefined) {
|
|
267
|
+
const mismatch = typeMismatch(schema.type, value);
|
|
268
|
+
if (mismatch !== null) {
|
|
269
|
+
return `${path} ${mismatch}.`;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (schema.oneOf !== undefined) {
|
|
273
|
+
const matches = schema.oneOf.filter((sub) => validateInput(sub, value, path) === null).length;
|
|
274
|
+
if (matches !== 1) {
|
|
275
|
+
return `${path} must match exactly one allowed shape (${matches} matched).`;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (typeof value === 'number') {
|
|
279
|
+
if (schema.minimum !== undefined && value < schema.minimum) {
|
|
280
|
+
return `${path} must be >= ${schema.minimum}.`;
|
|
281
|
+
}
|
|
282
|
+
if (schema.maximum !== undefined && value > schema.maximum) {
|
|
283
|
+
return `${path} must be <= ${schema.maximum}.`;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (typeof value === 'string' &&
|
|
287
|
+
schema.minLength !== undefined &&
|
|
288
|
+
value.length < schema.minLength) {
|
|
289
|
+
return `${path} must be at least ${schema.minLength} characters.`;
|
|
290
|
+
}
|
|
291
|
+
if (typeof value === 'string' &&
|
|
292
|
+
schema.maxLength !== undefined &&
|
|
293
|
+
value.length > schema.maxLength) {
|
|
294
|
+
return `${path} must be at most ${schema.maxLength} characters.`;
|
|
295
|
+
}
|
|
296
|
+
if (typeof value === 'string' &&
|
|
297
|
+
schema.pattern !== undefined &&
|
|
298
|
+
!new RegExp(schema.pattern).test(value)) {
|
|
299
|
+
return `${path} must match ${schema.pattern}.`;
|
|
300
|
+
}
|
|
301
|
+
if (schema.type === 'object' &&
|
|
302
|
+
typeof value === 'object' &&
|
|
303
|
+
value !== null &&
|
|
304
|
+
!Array.isArray(value)) {
|
|
305
|
+
const objectValue = value;
|
|
306
|
+
for (const key of schema.required ?? []) {
|
|
307
|
+
if (objectValue[key] === undefined) {
|
|
308
|
+
return `${path}.${key} is required.`;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const properties = schema.properties ?? {};
|
|
312
|
+
for (const [key, propertyValue] of Object.entries(objectValue)) {
|
|
313
|
+
// undefined is absent — JSON cannot carry it
|
|
314
|
+
if (propertyValue === undefined) {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const propertySchema = properties[key];
|
|
318
|
+
if (propertySchema === undefined) {
|
|
319
|
+
if (schema.additionalProperties === false) {
|
|
320
|
+
return `${path}.${key} is not an allowed property.`;
|
|
321
|
+
}
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
const nested = validateInput(propertySchema, propertyValue, `${path}.${key}`);
|
|
325
|
+
if (nested !== null) {
|
|
326
|
+
return nested;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (schema.type === 'array' &&
|
|
331
|
+
Array.isArray(value) &&
|
|
332
|
+
schema.minItems !== undefined &&
|
|
333
|
+
value.length < schema.minItems) {
|
|
334
|
+
return `${path} must carry at least ${schema.minItems} item(s).`;
|
|
335
|
+
}
|
|
336
|
+
if (schema.type === 'array' &&
|
|
337
|
+
Array.isArray(value) &&
|
|
338
|
+
schema.maxItems !== undefined &&
|
|
339
|
+
value.length > schema.maxItems) {
|
|
340
|
+
return `${path} must carry at most ${schema.maxItems} item(s).`;
|
|
341
|
+
}
|
|
342
|
+
if (schema.type === 'array' && Array.isArray(value) && schema.items !== undefined) {
|
|
343
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
344
|
+
const nested = validateInput(schema.items, value[i], `${path}[${i}]`);
|
|
345
|
+
if (nested !== null) {
|
|
346
|
+
return nested;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
function typeMismatch(type, value) {
|
|
353
|
+
switch (type) {
|
|
354
|
+
case 'object':
|
|
355
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
356
|
+
? null
|
|
357
|
+
: 'must be an object';
|
|
358
|
+
case 'array':
|
|
359
|
+
return Array.isArray(value) ? null : 'must be an array';
|
|
360
|
+
case 'string':
|
|
361
|
+
return typeof value === 'string' ? null : 'must be a string';
|
|
362
|
+
case 'number':
|
|
363
|
+
return typeof value === 'number' && Number.isFinite(value) ? null : 'must be a number';
|
|
364
|
+
case 'integer':
|
|
365
|
+
return typeof value === 'number' && Number.isInteger(value) ? null : 'must be an integer';
|
|
366
|
+
case 'boolean':
|
|
367
|
+
return typeof value === 'boolean' ? null : 'must be a boolean';
|
|
368
|
+
case 'null':
|
|
369
|
+
return value === null ? null : 'must be null';
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
//# sourceMappingURL=dispatch.js.map
|
package/dist/canonicalize.d.ts
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* - serialize normalized value via JSON.stringify (after normalization)
|
|
13
13
|
* - hash canonical JSON string with SHA-256 over UTF-8 bytes → 32-byte Buffer
|
|
14
14
|
*
|
|
15
|
-
* No top-level await. No ESM-only runtime dependency. Safe for CJS
|
|
16
|
-
*
|
|
15
|
+
* No top-level await. No ESM-only runtime dependency. Safe for CJS callers via the
|
|
16
|
+
* package export `./canonicalize`.
|
|
17
17
|
*
|
|
18
18
|
* @module canonicalize
|
|
19
19
|
*/
|
package/dist/canonicalize.js
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* - serialize normalized value via JSON.stringify (after normalization)
|
|
14
14
|
* - hash canonical JSON string with SHA-256 over UTF-8 bytes → 32-byte Buffer
|
|
15
15
|
*
|
|
16
|
-
* No top-level await. No ESM-only runtime dependency. Safe for CJS
|
|
17
|
-
*
|
|
16
|
+
* No top-level await. No ESM-only runtime dependency. Safe for CJS callers via the
|
|
17
|
+
* package export `./canonicalize`.
|
|
18
18
|
*
|
|
19
19
|
* @module canonicalize
|
|
20
20
|
*/
|
|
@@ -62,7 +62,7 @@ function normalize(value, path = '$') {
|
|
|
62
62
|
if (type === 'object') {
|
|
63
63
|
const obj = value;
|
|
64
64
|
const sortedKeys = Object.keys(obj).sort();
|
|
65
|
-
const out =
|
|
65
|
+
const out = Object.create(null);
|
|
66
66
|
for (const key of sortedKeys) {
|
|
67
67
|
out[key] = normalize(obj[key], `${path}.${key}`);
|
|
68
68
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ActionContract, ActionResult, ClientSessionFacts } from '../action-contracts.js';
|
|
2
|
+
export interface ClientOptions {
|
|
3
|
+
/** The private socket the runtime owns. No path guessing — product assembly states it. */
|
|
4
|
+
socketPath: string;
|
|
5
|
+
/**
|
|
6
|
+
* Append-only JSONL journal of issued operations: {at, operationId, action,
|
|
7
|
+
* payloadHash}. Written before send; a failed write fails the call closed,
|
|
8
|
+
* because an operation without a recovery record must not run.
|
|
9
|
+
*/
|
|
10
|
+
journalPath: string;
|
|
11
|
+
/** Opaque session credential; the server resolves it to access. */
|
|
12
|
+
credential?: string;
|
|
13
|
+
/** Optional cap on one request; a timeout after connect reports `unknown`. */
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ClientCall {
|
|
17
|
+
action: string;
|
|
18
|
+
input?: unknown;
|
|
19
|
+
session?: ClientSessionFacts;
|
|
20
|
+
/** Reuse this id only to retransmit the same call. */
|
|
21
|
+
operationId?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface Client {
|
|
24
|
+
/** Every contract the server executes — the whole callable surface. */
|
|
25
|
+
describe(): Promise<ActionContract[]>;
|
|
26
|
+
/** One contract by name or alias. */
|
|
27
|
+
describe(name: string): Promise<ActionContract>;
|
|
28
|
+
call(call: ClientCall): Promise<ActionResult>;
|
|
29
|
+
/** The server's bound record of an issued operation. */
|
|
30
|
+
getOperation(operationId: string): Promise<ActionResult>;
|
|
31
|
+
}
|
|
32
|
+
export declare function createClient(options: ClientOptions): Client;
|
|
33
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createClient = createClient;
|
|
4
|
+
/**
|
|
5
|
+
* Client — the one caller-side library for CLI and MCP.
|
|
6
|
+
*
|
|
7
|
+
* §291: the client issues the operationId for a call and writes it to the
|
|
8
|
+
* journal — action plus payload hash — BEFORE the request is sent, so a retry
|
|
9
|
+
* always names the same id. For knowledge commands that id is the commandId;
|
|
10
|
+
* an input carrying a different commandId conflicts before anything writes.
|
|
11
|
+
*
|
|
12
|
+
* §297: a confirmed failure is `failed`; a transport break after the request
|
|
13
|
+
* may have committed is `unknown` and keeps the original operationId. A
|
|
14
|
+
* disconnected call is never reported as unsaved.
|
|
15
|
+
*/
|
|
16
|
+
const node_crypto_1 = require("node:crypto");
|
|
17
|
+
const node_fs_1 = require("node:fs");
|
|
18
|
+
const node_path_1 = require("node:path");
|
|
19
|
+
const canonicalize_js_1 = require("../canonicalize.js");
|
|
20
|
+
const ipc_js_1 = require("./ipc.js");
|
|
21
|
+
function createClient(options) {
|
|
22
|
+
const journal = (entry) => {
|
|
23
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(options.journalPath), { recursive: true });
|
|
24
|
+
(0, node_fs_1.appendFileSync)(options.journalPath, `${JSON.stringify(entry)}\n`, 'utf8');
|
|
25
|
+
};
|
|
26
|
+
const payloadHash = (call, operationId) => (0, node_crypto_1.createHash)('sha256')
|
|
27
|
+
.update((0, canonicalize_js_1.canonicalizeJSON)(JSON.parse(JSON.stringify({ action: call.action, input: call.input ?? null, operationId }))))
|
|
28
|
+
.digest('hex');
|
|
29
|
+
const call = async (request) => {
|
|
30
|
+
const operationId = request.operationId ?? `op_${(0, node_crypto_1.randomUUID)()}`;
|
|
31
|
+
const inputCommandId = typeof request.input === 'object' && request.input !== null
|
|
32
|
+
? request.input.commandId
|
|
33
|
+
: undefined;
|
|
34
|
+
if (typeof inputCommandId === 'string' && inputCommandId !== operationId) {
|
|
35
|
+
return {
|
|
36
|
+
status: 'failed',
|
|
37
|
+
operationId,
|
|
38
|
+
error: {
|
|
39
|
+
kind: 'invalid_input',
|
|
40
|
+
code: 'COMMAND_CONFLICT',
|
|
41
|
+
message: `input.commandId ${inputCommandId} disagrees with operationId ${operationId}: ` +
|
|
42
|
+
'a call has exactly one command id — retransmit reuses it, a new call gets a new one',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
journal({
|
|
48
|
+
at: new Date().toISOString(),
|
|
49
|
+
operationId,
|
|
50
|
+
action: request.action,
|
|
51
|
+
payloadHash: payloadHash(request, operationId),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return {
|
|
56
|
+
status: 'failed',
|
|
57
|
+
operationId,
|
|
58
|
+
error: {
|
|
59
|
+
kind: 'internal',
|
|
60
|
+
code: 'journal_write_failed',
|
|
61
|
+
message: `operation journal write failed before send: ${error instanceof Error ? error.message : String(error)}`,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
let wire;
|
|
66
|
+
try {
|
|
67
|
+
wire = await (0, ipc_js_1.sendIpcRequest)(options.socketPath, {
|
|
68
|
+
requestId: (0, ipc_js_1.newRequestId)(),
|
|
69
|
+
kind: 'call',
|
|
70
|
+
credential: options.credential,
|
|
71
|
+
action: request.action,
|
|
72
|
+
input: request.input,
|
|
73
|
+
operationId,
|
|
74
|
+
session: request.session,
|
|
75
|
+
}, { timeoutMs: options.timeoutMs });
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (error instanceof ipc_js_1.IpcTransportError && error.phase === 'connect') {
|
|
79
|
+
return {
|
|
80
|
+
status: 'failed',
|
|
81
|
+
operationId,
|
|
82
|
+
error: {
|
|
83
|
+
kind: 'internal',
|
|
84
|
+
code: 'ipc_unavailable',
|
|
85
|
+
message: `runtime socket unreachable: ${error.message}`,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
status: 'unknown',
|
|
91
|
+
operationId,
|
|
92
|
+
error: {
|
|
93
|
+
kind: 'internal',
|
|
94
|
+
code: 'ipc_lost_response',
|
|
95
|
+
message: `the answer was lost after the call may have committed — ` +
|
|
96
|
+
`check operation.get(${operationId}) or resend the same operationId`,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (!wire.ok) {
|
|
101
|
+
return { status: 'failed', operationId, error: wire.error };
|
|
102
|
+
}
|
|
103
|
+
return wire.result;
|
|
104
|
+
};
|
|
105
|
+
const describe = async (name) => {
|
|
106
|
+
const wire = await (0, ipc_js_1.sendIpcRequest)(options.socketPath, {
|
|
107
|
+
requestId: (0, ipc_js_1.newRequestId)(),
|
|
108
|
+
kind: 'describe',
|
|
109
|
+
credential: options.credential,
|
|
110
|
+
name,
|
|
111
|
+
}, { timeoutMs: options.timeoutMs });
|
|
112
|
+
if (!wire.ok) {
|
|
113
|
+
throw new Error(`describe failed: [${wire.error.code}] ${wire.error.message}`);
|
|
114
|
+
}
|
|
115
|
+
return wire.result;
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
describe: describe,
|
|
119
|
+
call,
|
|
120
|
+
getOperation: (operationId) => call({ action: 'operation.get', input: { operationId } }),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=client.js.map
|