@sema-agent/sdk 9.8.0 → 9.9.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 +68 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -0
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +6 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +25 -5
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/resources/memory.d.ts +151 -4
- package/dist/resources/memory.d.ts.map +1 -1
- package/dist/resources/memory.js +177 -0
- package/dist/resources/memory.js.map +1 -1
- package/dist/types.d.ts +354 -0
- package/dist/types.d.ts.map +1 -1
- package/openapi.yaml +811 -4
- package/package.json +1 -1
package/openapi.yaml
CHANGED
|
@@ -4517,6 +4517,359 @@ paths:
|
|
|
4517
4517
|
'422': { description: 'errorCode "memory_sync_invalid_body" — the request failed the typed shape gate.' }
|
|
4518
4518
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
4519
4519
|
|
|
4520
|
+
/v1/memory/entries/{entryId}/provenance:
|
|
4521
|
+
parameters:
|
|
4522
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
4523
|
+
- in: path
|
|
4524
|
+
name: entryId
|
|
4525
|
+
required: true
|
|
4526
|
+
schema: { type: string }
|
|
4527
|
+
description: 'The memory entry id, percent-encoded. A malformed escape → 400 `request.id_invalid`.'
|
|
4528
|
+
get:
|
|
4529
|
+
tags: [memory]
|
|
4530
|
+
operationId: memoryEntryProvenance
|
|
4531
|
+
x-status: live # design/316 item ③ (server >= 7.46.0); route is always mounted and answers 501 itself. SDK memory.provenance().
|
|
4532
|
+
summary: One entry's provenance account (operator lane).
|
|
4533
|
+
description: >
|
|
4534
|
+
design/316 item ③ — the machine-readable answer to "who wrote this in, what state is it in, has it been
|
|
4535
|
+
moved" for an auditor holding an entry id. AUDIENCE IS OPERATOR / COMPLIANCE, not a shell knob: the
|
|
4536
|
+
owner-facing self-service faces are `GET /v1/memory/export` and `POST /v1/sessions/{id}/memory/erase`.
|
|
4537
|
+
|
|
4538
|
+
GATE ORDER: identity (401 `auth.principal_required`) → authorization (403 `auth.operator_only`) →
|
|
4539
|
+
capability (501 `capability.memory_engine_required`) → shape (400). AUTHORIZATION BEFORE CAPABILITY, on
|
|
4540
|
+
purpose: a caller who can reach nothing must not be able to read the deployment's shape off it.
|
|
4541
|
+
Capability bit: `memoryCompliance` — mounted AND `OPERATOR_PRINCIPALS` non-empty. It is NOT a synonym of
|
|
4542
|
+
`memoryBundle` (that one asks about the backend's v2-c composite face; this one asks whether the backend
|
|
4543
|
+
OWNS THE ENGINE CONTROL PLANE, `controlPlaneRoot` — the file memory backend does, pg/tidb do not).
|
|
4544
|
+
⚠️ The bit does NOT include "credentials ready": this route sits behind the deployment-wide rewrite gate,
|
|
4545
|
+
so a worker with no service credential (and without an explicit `ALLOW_UNAUTHED_WRITES`) answers 503
|
|
4546
|
+
`auth.service_token_required` BEFORE routing while the bit still reads `true`.
|
|
4547
|
+
|
|
4548
|
+
The 200 body is core's `EntryProvenanceAccount` ITSELF — not wrapped, not projected, not padded. The
|
|
4549
|
+
account form is an OPEN discriminated shape, so writing a whitelist in the middle would restate a
|
|
4550
|
+
structure that drifts with core. This is DELIBERATELY THE OPPOSITE of the import report (an explicit
|
|
4551
|
+
whitelist there): the test is "is a new key silently reaching the wire dangerous?" — in a disposition
|
|
4552
|
+
list, yes; in a STATEMENT OF FACT, withholding a field is the dangerous half.
|
|
4553
|
+
responses:
|
|
4554
|
+
'200':
|
|
4555
|
+
description: The entry's assembled provenance account (core's, verbatim).
|
|
4556
|
+
content:
|
|
4557
|
+
application/json:
|
|
4558
|
+
schema: { $ref: '#/components/schemas/EntryProvenanceAccount' }
|
|
4559
|
+
'400': { description: 'errorCode "request.id_invalid" — the entry id could not be decoded.' }
|
|
4560
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
4561
|
+
'403': { description: 'errorCode "auth.operator_only" — provenance and erasure are operator-only deployment actions.' }
|
|
4562
|
+
'500':
|
|
4563
|
+
description: >
|
|
4564
|
+
errorCode "internal.memory_control_plane_corrupt" — core's FAIL-CLOSED refusal: the control plane
|
|
4565
|
+
cannot be read soundly and it will not assemble an answer over an account it cannot vouch for.
|
|
4566
|
+
core's diagnostic text rides VERBATIM (it names which ledger is damaged) — show it to the operator in
|
|
4567
|
+
full. 🔴 The code is TWO-MEANINGED and core carries NO discriminator for it today, so DO NOT sniff the
|
|
4568
|
+
message to tell them apart: (1) BEFORE the operation — nothing happened, repair/rebuild the ledger;
|
|
4569
|
+
(2) AFTER it landed (ERASE ONLY) — the deletions and their evidence anchor ARE COMMITTED and only the
|
|
4570
|
+
residue enumeration failed, so the action is to replay with the SAME `requestId`. Never read (2) as
|
|
4571
|
+
"nothing was deleted". ("internal.error" is the unclassified 500; its text is not echoed.)
|
|
4572
|
+
content:
|
|
4573
|
+
application/json:
|
|
4574
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
4575
|
+
'501': { description: 'errorCode "capability.memory_engine_required" — this deployment has no memory engine whose backend owns the engine control plane. Change the deployment shape; retrying will not help.' }
|
|
4576
|
+
|
|
4577
|
+
/v1/memory/erase:
|
|
4578
|
+
parameters:
|
|
4579
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
4580
|
+
post:
|
|
4581
|
+
tags: [memory]
|
|
4582
|
+
operationId: memoryErase
|
|
4583
|
+
x-status: live # design/316 item ③ (server >= 7.46.0). SDK memory.erase().
|
|
4584
|
+
summary: Explicitly authorized compliance erasure (operator lane).
|
|
4585
|
+
description: >
|
|
4586
|
+
design/316 item ③ — the deliverable a compliance caller archives. Gate order, capability bit and the
|
|
4587
|
+
rewrite-gate caveat are VERBATIM those of `GET /v1/memory/entries/{entryId}/provenance`.
|
|
4588
|
+
|
|
4589
|
+
The request body is handed to the engine AS SENT: the server adds, drops and rewrites nothing. In
|
|
4590
|
+
particular `allowUnevidenced` is NEVER INJECTED — the degraded lane is a HUMAN authorization statement,
|
|
4591
|
+
and a middle layer ticking it is that layer signing for the caller. The selector's whole judgment
|
|
4592
|
+
(exactly one of `ids`/`scope`/`sessionId`, de-duplication, types) belongs to core, so a
|
|
4593
|
+
form-right-meaning-wrong body such as `{"ids": [...], "scope": "..."}` reaches the engine and comes back
|
|
4594
|
+
400 `config.memory_erasure_request`.
|
|
4595
|
+
|
|
4596
|
+
🔴 IDEMPOTENCY HAS TWO LEGS WITH DIFFERENT CONTRACTS — read `evidenceCapability` BEFORE deciding whether a
|
|
4597
|
+
retry is safe. `"journal"`: `requestId` is the idempotency identity; a resend READS BACK the ids pinned by
|
|
4598
|
+
the anchor row and never re-resolves, and reusing an id under a DIFFERENT selector is refused loudly (409
|
|
4599
|
+
`memory.erasure_selector_mismatch`). `"none"` (the explicit `allowUnevidenced` degradation): core's words
|
|
4600
|
+
are "same requestId = a NEW request (re-resolved — replay convergence is not promised)" — A RESEND IS A
|
|
4601
|
+
SECOND REAL DELETE. 🔴 DO NOT put an automatic retry policy on this endpoint.
|
|
4602
|
+
|
|
4603
|
+
⚠️ THE MASS-DELETION FUSE DOES NOT APPLY: a whole-scope / whole-session selector is LEGAL here (the fuse
|
|
4604
|
+
guards "files quietly vanished during harvest"; an explicitly authorized erasure vouches for itself by
|
|
4605
|
+
enumerating every id in the attestation). `residuals.propagation` is core's self-declared boundary:
|
|
4606
|
+
deletion is a THIS-STORE fact, remote-peer convergence is the sync deployment's half to prove.
|
|
4607
|
+
requestBody:
|
|
4608
|
+
required: true
|
|
4609
|
+
content:
|
|
4610
|
+
application/json:
|
|
4611
|
+
schema: { $ref: '#/components/schemas/MemoryErasureRequest' }
|
|
4612
|
+
responses:
|
|
4613
|
+
'200':
|
|
4614
|
+
description: The erasure attestation (core's, verbatim).
|
|
4615
|
+
content:
|
|
4616
|
+
application/json:
|
|
4617
|
+
schema: { $ref: '#/components/schemas/MemoryErasureAttestation' }
|
|
4618
|
+
'400':
|
|
4619
|
+
description: >
|
|
4620
|
+
Three codes, three different operator actions — deliberately not folded into one.
|
|
4621
|
+
"request.request_id_required": `requestId` missing / empty / non-string. It is THIS request's
|
|
4622
|
+
idempotency identity and the server never mints one for you, so a RETRY MUST CARRY THE SAME ID (a lost
|
|
4623
|
+
ack must not open a second real delete). "request.body_shape": the top-level JSON shape is wrong, or a
|
|
4624
|
+
key that rewrites an object's prototype was smuggled in as a selector key. "config.memory_erasure_request":
|
|
4625
|
+
core's own verdict — the shape passed, the SELECTOR does not (not exactly one of ids/scope/sessionId,
|
|
4626
|
+
empty/duplicated ids, …).
|
|
4627
|
+
content:
|
|
4628
|
+
application/json:
|
|
4629
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
4630
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
4631
|
+
'403': { description: 'errorCode "auth.operator_only" — provenance and erasure are operator-only deployment actions.' }
|
|
4632
|
+
'409':
|
|
4633
|
+
description: >
|
|
4634
|
+
Three core-minted codes, passed through verbatim; branch on `errorCode` (the status cannot tell them
|
|
4635
|
+
apart). "memory.erasure_evidence_unavailable": this backend has NO evidence face and the caller did not
|
|
4636
|
+
opt into the degraded lane — deliberately NOT 501, because the caller holds a real choice
|
|
4637
|
+
(`allowUnevidenced: true`) and a 501 would send someone off to change a deployment that is fine.
|
|
4638
|
+
"memory.erasure_selector_mismatch": this `requestId` was first executed under a DIFFERENT selector —
|
|
4639
|
+
mint a new id. "memory.erasure_census_incomplete": the store-wide projection census could not be read in
|
|
4640
|
+
full, so the attestation refuses to claim a clean sweep — core states nothing was deleted and nothing
|
|
4641
|
+
recorded; repair the filesystem fault and retry.
|
|
4642
|
+
content:
|
|
4643
|
+
application/json:
|
|
4644
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
4645
|
+
'500':
|
|
4646
|
+
description: 'errorCode "internal.memory_control_plane_corrupt" — see the same response on `GET /v1/memory/entries/{entryId}/provenance`; meaning (2) of that two-meaninged code (deletions COMMITTED, residue enumeration failed) occurs on THIS endpoint only.'
|
|
4647
|
+
content:
|
|
4648
|
+
application/json:
|
|
4649
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
4650
|
+
'501': { description: 'errorCode "capability.memory_engine_required" — this deployment has no memory engine whose backend owns the engine control plane.' }
|
|
4651
|
+
|
|
4652
|
+
/v1/memory/origin/external:
|
|
4653
|
+
parameters:
|
|
4654
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
4655
|
+
- in: query
|
|
4656
|
+
name: scopes
|
|
4657
|
+
required: true
|
|
4658
|
+
schema: { type: string }
|
|
4659
|
+
description: >
|
|
4660
|
+
One or more memory scope keys. Comma-separated (`?scopes=a,b`) and/or REPEATED (`?scopes=a&scopes=b`) —
|
|
4661
|
+
both forms are merged in full, then trimmed per segment, emptied segments dropped, de-duplicated in
|
|
4662
|
+
order. Scope keys carry `:` / `@` / `/`, so every key is percent-encoded before the separators are
|
|
4663
|
+
joined. ⚠️ A scope key containing a LITERAL COMMA is not expressible on this query (the server decodes
|
|
4664
|
+
before it splits).
|
|
4665
|
+
- in: query
|
|
4666
|
+
name: originAware
|
|
4667
|
+
required: false
|
|
4668
|
+
schema: { type: string, enum: ['1'] }
|
|
4669
|
+
description: >
|
|
4670
|
+
design/383 S-6 / S-50 §3 (F3) — the ORIGIN FENCE capability declaration. The server has no per-client
|
|
4671
|
+
version visibility, so an absent flag is read as "an old client", and an old client's strict/strip parse
|
|
4672
|
+
SILENTLY DROPS the `origin` marker — flattening a "known external" entry into unmarked text that the
|
|
4673
|
+
consumer then quotes or writes back as clean content, laundering the provenance away.
|
|
4674
|
+
🔴 EVERY row of THIS face carries a marker, so WITHOUT `originAware=1` the whole list is withheld and the
|
|
4675
|
+
response carries `withheldOriginTagged: <n>` instead (minted only when n > 0). That is the fence working,
|
|
4676
|
+
not a bug: an operator tool must declare it knows the origin key family before it can see them.
|
|
4677
|
+
Only the literal `1` is accepted (no loose parsing of `true`/`yes`).
|
|
4678
|
+
get:
|
|
4679
|
+
tags: [memory]
|
|
4680
|
+
operationId: memoryOriginExternal
|
|
4681
|
+
x-status: live # design/316 item ② (server >= 7.46.0). SDK memory.originExternal() (which always declares originAware=1).
|
|
4682
|
+
summary: Every externally-marked entry in the named scopes, with its provenance account (operator lane).
|
|
4683
|
+
description: >
|
|
4684
|
+
design/316 item ② — a memory entry may carry an EXTERNAL-ORIGIN marker: its content came from material the
|
|
4685
|
+
model READ (a web page, a third-party doc, someone else's repo) rather than from first-hand facts in the
|
|
4686
|
+
session. The marker does not mean "poisoned", it means "VERIFY BEFORE USE", and it rides along into every
|
|
4687
|
+
mount. Gate order, capability caveat and the rewrite gate are verbatim those of item ③; the bit here is
|
|
4688
|
+
`memoryOrigin` (same predicate as `memoryCompliance` TODAY, but deliberately a SECOND bit — they are two
|
|
4689
|
+
product faces and a deployment may one day open only one of them; do not cache them as one).
|
|
4690
|
+
|
|
4691
|
+
🔴 THE ANSWER COVERS ONLY THE SCOPES YOU NAMED, AND AN EMPTY RESULT MUST NEVER BE READ AS "THE STORE IS
|
|
4692
|
+
CLEAN". This server generation has no scope-enumeration read face, so scope names are supplied by the
|
|
4693
|
+
caller and INCOMPLETENESS IS SILENT (an empty array and "these scopes are clean" are the same bytes on the
|
|
4694
|
+
wire). An auditing script must guarantee its own list is complete (the keys are the same ones
|
|
4695
|
+
`GET /v1/memory/export?scope=` takes). The echoed `scopes` is THE SET ACTUALLY AUDITED after
|
|
4696
|
+
normalization — it is returned so the coverage itself is observable rather than inferred from what the
|
|
4697
|
+
caller remembers sending.
|
|
4698
|
+
|
|
4699
|
+
Each row's `provenance` IS the `EntryProvenanceAccount` of item ③ (same discriminated form, same
|
|
4700
|
+
pass-through discipline). An entry that is also CHALLENGED is still listed here — the exclusion rides
|
|
4701
|
+
inside the provenance account, and a human must SEE it in order to adjudicate it.
|
|
4702
|
+
responses:
|
|
4703
|
+
'200':
|
|
4704
|
+
description: The audited scopes (normalized, echoed) and one row per marked entry.
|
|
4705
|
+
content:
|
|
4706
|
+
application/json:
|
|
4707
|
+
schema: { $ref: '#/components/schemas/MemoryOriginExternalResult' }
|
|
4708
|
+
'400': { description: 'errorCode "request.query_invalid" — `scopes` is absent or normalizes to an empty list. The refusal text also names where scope keys come from and repeats that an empty answer is not a clean store.' }
|
|
4709
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
4710
|
+
'403': { description: 'errorCode "auth.operator_only" — origin marking and clearance are operator-only deployment actions.' }
|
|
4711
|
+
'500':
|
|
4712
|
+
description: 'errorCode "internal.memory_control_plane_corrupt" (meaning (1) only on this read face) or "internal.error" (unclassified, text not echoed).'
|
|
4713
|
+
content:
|
|
4714
|
+
application/json:
|
|
4715
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
4716
|
+
'501': { description: 'errorCode "capability.memory_engine_required" — no memory engine whose backend owns the engine control plane; the clearance ledger and its write-ahead custody rows live in that plane.' }
|
|
4717
|
+
|
|
4718
|
+
/v1/memory/origin/clearances:
|
|
4719
|
+
parameters:
|
|
4720
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
4721
|
+
get:
|
|
4722
|
+
tags: [memory]
|
|
4723
|
+
operationId: memoryOriginClearances
|
|
4724
|
+
x-status: live # design/316 item ② (server >= 7.46.0). SDK memory.originClearances().
|
|
4725
|
+
summary: The origin-clearance audit ledger (operator lane, explicit whitelist projection).
|
|
4726
|
+
description: >
|
|
4727
|
+
design/316 item ② — who un-marked what, when, why, and whether the clearance finished. Gate order and
|
|
4728
|
+
capability bit are those of `GET /v1/memory/origin/external`.
|
|
4729
|
+
|
|
4730
|
+
Each row is an EXPLICIT WHITELIST PROJECTION of core's `OriginClearanceRow`, which is DELIBERATELY THE
|
|
4731
|
+
OPPOSITE posture from the two pass-through accounts above — the test is the same one ("is a new key
|
|
4732
|
+
silently reaching the wire dangerous?") and the answer flips, because this is a CUSTODY face rather than a
|
|
4733
|
+
statement of fact.
|
|
4734
|
+
🔴 `entryText` — the full text of the cleared entry — NEVER reaches the wire, but its presence is disclosed
|
|
4735
|
+
honestly as `custodyBytes`. That field is the CRASH-RECOVERY SEAT: if the process dies between the
|
|
4736
|
+
tombstone batch and the re-record batch, the row holds the only copy of that memory; the recovery is TO
|
|
4737
|
+
CALL `POST /v1/memory/origin/entries/{entryId}/clear` AGAIN (the engine replays from the row), not to read
|
|
4738
|
+
the bytes out and paste them back. Putting a whole memory's body on an audit LIST face would attach a full
|
|
4739
|
+
text export to every "let me look at the clearance records". So it is stripped — and STRIPPING IS
|
|
4740
|
+
DISCLOSED: a `pending` row with `custodyBytes > 0` means the recovery seat is still holding a memory and A
|
|
4741
|
+
HUMAN MUST ACT.
|
|
4742
|
+
🔴 THE WHITELIST IS DEEP: the nested `origin` is projected key by key too, because the ledger read returns
|
|
4743
|
+
the RAW ON-DISK OBJECT (core validates it without rewriting it), so version skew, a future core key, or
|
|
4744
|
+
core's own documented BY-HAND recovery path could otherwise ride an extra member straight through — one
|
|
4745
|
+
`origin.entryText` would bypass the single most load-bearing exclusion on this face. Members outside the
|
|
4746
|
+
documented three are never serialized; their KEY NAMES are disclosed under `originUnknownKeys` (names
|
|
4747
|
+
only, never values).
|
|
4748
|
+
🔴 Attribution on this ledger is SELF-DECLARED: `requestId` is filled in by the caller and recorded
|
|
4749
|
+
verbatim, so any operator who can reach the clear valve can book an irreversible clearance under someone
|
|
4750
|
+
else's name. The deployment-side counterpart is a server log line (`memory_origin_cleared`) carrying the
|
|
4751
|
+
VERIFIED principal beside the CLAIMED requestId and the `clearanceId` to join on — DO NOT draw a compliance
|
|
4752
|
+
conclusion from the ledger alone.
|
|
4753
|
+
responses:
|
|
4754
|
+
'200':
|
|
4755
|
+
description: The clearance ledger (whitelist projection, newest-first not guaranteed).
|
|
4756
|
+
content:
|
|
4757
|
+
application/json:
|
|
4758
|
+
schema: { $ref: '#/components/schemas/MemoryOriginClearancesResult' }
|
|
4759
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
4760
|
+
'403': { description: 'errorCode "auth.operator_only" — origin marking and clearance are operator-only deployment actions.' }
|
|
4761
|
+
'500':
|
|
4762
|
+
description: 'errorCode "internal.memory_control_plane_corrupt" (the ledger cannot be read soundly) or "internal.error".'
|
|
4763
|
+
content:
|
|
4764
|
+
application/json:
|
|
4765
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
4766
|
+
'501': { description: 'errorCode "capability.memory_engine_required" — no memory engine whose backend owns the engine control plane.' }
|
|
4767
|
+
|
|
4768
|
+
/v1/memory/origin/entries/{entryId}/clear:
|
|
4769
|
+
parameters:
|
|
4770
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
4771
|
+
- in: path
|
|
4772
|
+
name: entryId
|
|
4773
|
+
required: true
|
|
4774
|
+
schema: { type: string }
|
|
4775
|
+
description: 'The memory entry id, percent-encoded. A malformed escape → 400 `request.id_invalid`.'
|
|
4776
|
+
post:
|
|
4777
|
+
tags: [memory]
|
|
4778
|
+
operationId: memoryOriginClear
|
|
4779
|
+
x-status: live # design/316 item ② (server >= 7.46.0). SDK memory.originClear().
|
|
4780
|
+
summary: The audited UN-MARK valve (operator lane).
|
|
4781
|
+
description: >
|
|
4782
|
+
design/316 item ② — this vouches AWAY the disclosure "this memory came from outside, verify before use",
|
|
4783
|
+
ON BEHALF OF THE HOST. The signer must be the deployment's operator, never a tenant. Gate order and
|
|
4784
|
+
capability bit are those of `GET /v1/memory/origin/external`.
|
|
4785
|
+
|
|
4786
|
+
The clear rides the backend immutability law's ONE legal exit: a write-ahead clearance row (custody = the
|
|
4787
|
+
full entry text) → a COMMITTED tombstone (CAS on the judged rev) → a later-batch re-record of THE SAME ID
|
|
4788
|
+
with the marker stripped. The same id is on purpose: links, usage accounts and the clearance join all keep
|
|
4789
|
+
their key. Every failure leaves the custody row standing, and CALLING THE SAME ENDPOINT AGAIN IS THE
|
|
4790
|
+
RESUME (idempotent).
|
|
4791
|
+
|
|
4792
|
+
🔴 `requestId` IS THE AUDIT ATTRIBUTION, NOT AN IDEMPOTENCY KEY — the opposite of the same-named field on
|
|
4793
|
+
`POST /v1/memory/erase`. Here it answers "WHO cleared this", recorded verbatim on the row and on the
|
|
4794
|
+
terminal event; passing a NEW id when resuming a pending row is legal and common (the later resumer is a
|
|
4795
|
+
different person). No length cap (the body-size limit bounds it).
|
|
4796
|
+
🔴 `reason` is shape-checked only (must be a string); WHETHER AN EMPTY REASON IS ACCEPTABLE IS THE ENGINE'S
|
|
4797
|
+
JUDGMENT (422 `memory.origin_clear_invalid`) — restating a `min(1)` in the HTTP layer would be a second
|
|
4798
|
+
source of truth and would permanently keep a code core deliberately minted off the wire.
|
|
4799
|
+
🔴 THE 200 CARRIES NO "THIS WAS A RESUME" DISCRIMINATOR, and the resume leg DOES NOT LOOK AT YOUR `reason`:
|
|
4800
|
+
the engine resumes the existing pending row, the ledger keeps the OPENER's reason and requestId, and only
|
|
4801
|
+
the terminal EVENT carries yours. So operator B, who resent after a 409 `memory.origin_clear_pending` as
|
|
4802
|
+
instructed and got a 200, may have completed operator A's row. ⇒ AFTER A 200, READ
|
|
4803
|
+
`GET /v1/memory/origin/clearances` BACK and check `reason` / `requestId`; `clearanceId` is that row's key.
|
|
4804
|
+
requestBody:
|
|
4805
|
+
required: true
|
|
4806
|
+
content:
|
|
4807
|
+
application/json:
|
|
4808
|
+
schema: { $ref: '#/components/schemas/MemoryOriginClearRequest' }
|
|
4809
|
+
responses:
|
|
4810
|
+
'200':
|
|
4811
|
+
description: The clearance receipt.
|
|
4812
|
+
content:
|
|
4813
|
+
application/json:
|
|
4814
|
+
schema: { $ref: '#/components/schemas/MemoryOriginClearReceipt' }
|
|
4815
|
+
'400':
|
|
4816
|
+
description: >
|
|
4817
|
+
"request.id_invalid" (the entry id could not be decoded) / "request.request_id_required" (`requestId`
|
|
4818
|
+
missing / empty / non-string — mint a stable id and resend; unlike the erase face, a retry here MAY
|
|
4819
|
+
carry a new one) / "request.body_shape" (the top-level JSON shape is wrong).
|
|
4820
|
+
content:
|
|
4821
|
+
application/json:
|
|
4822
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
4823
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
4824
|
+
'403': { description: 'errorCode "auth.operator_only" — origin marking and clearance are operator-only deployment actions.' }
|
|
4825
|
+
'409':
|
|
4826
|
+
description: >
|
|
4827
|
+
Six core-minted codes, passed through verbatim; BRANCH ON `errorCode` — the six operator actions all
|
|
4828
|
+
differ and folding them into one cell would make people guess.
|
|
4829
|
+
"memory.origin_clear_unknown" — TWO MEANINGS: (1) no committed entry under this id (check the id);
|
|
4830
|
+
(2) a RESUMED clearance row whose entry is gone while this clearance never recorded a tombstone, so
|
|
4831
|
+
core refuses to resurrect a deletion it did not make and the custody bytes stay on the terminal row
|
|
4832
|
+
for a human. Deliberately NOT 404: (2) is not "wrong address", and a generic retry arm would read it
|
|
4833
|
+
as one.
|
|
4834
|
+
"memory.origin_clear_not_marked" — the entry is there but was never marked; use
|
|
4835
|
+
`GET /v1/memory/origin/external` to see who really is.
|
|
4836
|
+
"memory.origin_clear_challenged" — the entry is challenged/latched. THE CLEAR VALVE IS NOT A CHALLENGE
|
|
4837
|
+
EXIT: adjudicate the challenge first (laundering an exclusion through a weaker credential is exactly
|
|
4838
|
+
what this refusal stops).
|
|
4839
|
+
"memory.origin_clear_conflict" — the judgment basis moved since the row was opened (rev changed,
|
|
4840
|
+
projection slug changed, unadopted on-disk changes, tombstone leg refused). Action: call again to
|
|
4841
|
+
re-judge; BYTES ON BOTH SIDES ARE UNCHANGED.
|
|
4842
|
+
"memory.origin_clear_failed" — STOPPED HALFWAY AND RESUMABLE (the re-record leg was refused, or the
|
|
4843
|
+
custody bytes failed their self-check). The row stays `pending` with the bytes in it ⇒ deliberately NOT
|
|
4844
|
+
a 500: the server did not break. Action: fix, then call the same endpoint again to recover. ⚠️ Do not
|
|
4845
|
+
read it as "nothing happened" — the tombstone may ALREADY be committed, and that row's `custodyBytes`
|
|
4846
|
+
on the clearances ledger is the notice.
|
|
4847
|
+
"memory.origin_clear_pending" — there is ALREADY a pending row on this entry (core's words: the pending
|
|
4848
|
+
row is a resume seat, not a queue). This is CONCURRENCY, not a fault ⇒ resend, and the next call walks
|
|
4849
|
+
into the resume leg. ⚠️ See the 200's note: that resume completes SOMEONE ELSE'S row.
|
|
4850
|
+
content:
|
|
4851
|
+
application/json:
|
|
4852
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
4853
|
+
'422':
|
|
4854
|
+
description: >
|
|
4855
|
+
Two core-minted SEMANTIC refusals (the body is valid JSON and the shape is right; what cannot be
|
|
4856
|
+
produced is MEANING). "memory.origin_clear_unattributed" — an empty `requestId` (core's words: refused,
|
|
4857
|
+
never defaulted; a clearance is the host signing for a foreign memory and a signature needs a name).
|
|
4858
|
+
⚠️ Through this route it is UNREACHABLE today (the route's own shape gate answers 400
|
|
4859
|
+
`request.request_id_required` first); it is documented because core owns the refusal, and the day that
|
|
4860
|
+
gate is loosened it must land as a 422 rather than as "the server broke".
|
|
4861
|
+
"memory.origin_clear_invalid" — an empty `reason`; THIS one really does reach the wire, because the
|
|
4862
|
+
HTTP layer deliberately does not restate core's `min(1)`.
|
|
4863
|
+
content:
|
|
4864
|
+
application/json:
|
|
4865
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
4866
|
+
'500':
|
|
4867
|
+
description: 'errorCode "internal.memory_control_plane_corrupt" or "internal.error" (unclassified, text not echoed).'
|
|
4868
|
+
content:
|
|
4869
|
+
application/json:
|
|
4870
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
4871
|
+
'501': { description: 'errorCode "capability.memory_engine_required" — no memory engine whose backend owns the engine control plane.' }
|
|
4872
|
+
|
|
4520
4873
|
/v1/outcomes:
|
|
4521
4874
|
parameters:
|
|
4522
4875
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
@@ -6257,6 +6610,30 @@ components:
|
|
|
6257
6610
|
type: array
|
|
6258
6611
|
items: { type: string }
|
|
6259
6612
|
description: Tool names to withhold from this task's roster.
|
|
6613
|
+
excludeAllTools:
|
|
6614
|
+
type: boolean
|
|
6615
|
+
enum: [true]
|
|
6616
|
+
description: >
|
|
6617
|
+
(server >= 7.90.0, contract G.9b / acceptance G-40) UNMOUNT THE WHOLE TOOL SURFACE for this run —
|
|
6618
|
+
caller tools, scaffolding, hands (Bash/Edit/Write…), MCP, A2A and skills are all left off, the model
|
|
6619
|
+
has nothing to call, and the outcome is necessarily plain text. On the wire: the tail's first
|
|
6620
|
+
`wiring_manifest` has `tools.count === 0` with an empty `entries` (disclosed ONCE, one frame per leg),
|
|
6621
|
+
and the stream carries zero `tool_start` / `tool_end` / `tool_disclosure` frames.
|
|
6622
|
+
🔴 DELIBERATELY NOT FOLDED INTO `excludeTools`: `"*"` is a LEGAL caller tool name, so "unmount
|
|
6623
|
+
everything" cannot be spelled `excludeTools:["*"]` without collapsing it with "unmount the one tool
|
|
6624
|
+
called `*`". Both keys may ride together and each takes effect — no conflict, no 400.
|
|
6625
|
+
🔴 `true` IS THE ONLY VALUE THAT MEANS ANYTHING: the server writes only a literal `true` onto the
|
|
6626
|
+
engine's TaskSpec, because sending `false` merely restates a default the ENGINE owns (absent /
|
|
6627
|
+
`false` ⇒ roster unchanged). A non-boolean is a 400 `request.field_invalid` on the synchronous submit
|
|
6628
|
+
leg; the DURABLE resume leg does not reject it (replaying a stored body — a 4xx there would brick an
|
|
6629
|
+
already-parked run) and degrades it to "key absent".
|
|
6630
|
+
🔴 PROBED BY THE KEY CLOSED SET, NOT BY A CAPABILITY BIT: a server that has not wired it (<= 7.89.x)
|
|
6631
|
+
treats it as an unknown top-level key — 400 `request.body_shape` naming it verbatim under
|
|
6632
|
+
`unknownKeys`, so trial-by-400 IS the probe (one key, one probe path; a second one is deliberately
|
|
6633
|
+
not grown).
|
|
6634
|
+
⚠️ SCOPE: the A2A inbound face (`message/send`) does NOT carry this key — that leg's submit body is
|
|
6635
|
+
rebuilt server-side from the protocol message as a CLOSED four-field set, so putting the key in an
|
|
6636
|
+
A2A message is INERT and does not error. An A2A peer that wants a zero-tool run routes by skill.
|
|
6260
6637
|
deferTools:
|
|
6261
6638
|
type: array
|
|
6262
6639
|
items: { type: string }
|
|
@@ -8319,6 +8696,436 @@ components:
|
|
|
8319
8696
|
updatedAtMs: { type: integer }
|
|
8320
8697
|
pullTruncated: { type: boolean, enum: [true] }
|
|
8321
8698
|
|
|
8699
|
+
MemoryTransferEvidence:
|
|
8700
|
+
type: object
|
|
8701
|
+
description: >
|
|
8702
|
+
core `TransferEvidence` — one row of the public `transfers.jsonl` audit face, as an OPEN discriminated
|
|
8703
|
+
form: the three base keys are the contract, everything else is variant payload.
|
|
8704
|
+
🔴 CONSUMER OBLIGATION: tolerate unknown `channel` values (report / skip / display raw) — NEVER an
|
|
8705
|
+
exhaustive `switch` that throws. The channel vocabulary is set by whichever binary wrote the chain, and a
|
|
8706
|
+
chain written by a newer engine must still read here.
|
|
8707
|
+
required: [ev, channel, at]
|
|
8708
|
+
additionalProperties: true
|
|
8709
|
+
properties:
|
|
8710
|
+
ev: { type: string, description: 'Unique event id (the evidence chain''s append-idempotency key).' }
|
|
8711
|
+
channel: { type: string, description: 'The event kind — OPEN ON PURPOSE (see the consumer obligation above).' }
|
|
8712
|
+
at: { type: integer, description: 'ms epoch of the event.' }
|
|
8713
|
+
|
|
8714
|
+
MemoryCommittedBinding:
|
|
8715
|
+
description: >
|
|
8716
|
+
core `CommittedBinding` — the committed-account BINDING half of one ledger row, as ONE tagged definition
|
|
8717
|
+
(never a look-alike copy: an untagged `{scope, slug}` beside `{state: "unbound"}` can be satisfied by a
|
|
8718
|
+
single hybrid object under both readings). `"unbound"` is a LEGAL transitional state (a v1→v2 migration
|
|
8719
|
+
row whose projection could not be derived), never a refusal.
|
|
8720
|
+
oneOf:
|
|
8721
|
+
- type: object
|
|
8722
|
+
required: [state, scope, slug]
|
|
8723
|
+
additionalProperties: true
|
|
8724
|
+
properties:
|
|
8725
|
+
state: { type: string, enum: [bound] }
|
|
8726
|
+
scope: { type: string }
|
|
8727
|
+
slug: { type: string }
|
|
8728
|
+
at: { type: integer }
|
|
8729
|
+
prev:
|
|
8730
|
+
type: object
|
|
8731
|
+
required: [scope, slug, at]
|
|
8732
|
+
additionalProperties: true
|
|
8733
|
+
properties:
|
|
8734
|
+
scope: { type: string }
|
|
8735
|
+
slug: { type: string }
|
|
8736
|
+
at: { type: integer }
|
|
8737
|
+
- type: object
|
|
8738
|
+
required: [state]
|
|
8739
|
+
additionalProperties: true
|
|
8740
|
+
properties:
|
|
8741
|
+
state: { type: string, enum: [unbound] }
|
|
8742
|
+
|
|
8743
|
+
MemoryProvenanceBinding:
|
|
8744
|
+
description: >
|
|
8745
|
+
`EntryProvenanceAccount.binding` — FOUR states without collapse: `bound` / `unbound` (the shared tagged
|
|
8746
|
+
{@link MemoryCommittedBinding}), `absent` (the account has NO row for this id) and `unknown` (the backend
|
|
8747
|
+
has no audit-snapshot capability).
|
|
8748
|
+
🔴 `unknown` ("cannot judge") MUST NEVER be folded into `absent` ("does not exist") — the two lead to
|
|
8749
|
+
opposite compliance conclusions.
|
|
8750
|
+
oneOf:
|
|
8751
|
+
- { $ref: '#/components/schemas/MemoryCommittedBinding' }
|
|
8752
|
+
- type: object
|
|
8753
|
+
required: [state]
|
|
8754
|
+
additionalProperties: true
|
|
8755
|
+
properties:
|
|
8756
|
+
state: { type: string, enum: [absent] }
|
|
8757
|
+
- type: object
|
|
8758
|
+
required: [state, reason]
|
|
8759
|
+
additionalProperties: true
|
|
8760
|
+
properties:
|
|
8761
|
+
state: { type: string, enum: [unknown] }
|
|
8762
|
+
reason: { type: string, enum: [audit-snapshot-capability-absent] }
|
|
8763
|
+
|
|
8764
|
+
EntryProvenanceAccount:
|
|
8765
|
+
type: object
|
|
8766
|
+
description: >
|
|
8767
|
+
200 body of GET /v1/memory/entries/{entryId}/provenance — core `EntryProvenanceAccount` ITSELF (the server
|
|
8768
|
+
neither wraps, projects nor pads it). An ASSEMBLY, not a second account: every field is read from an
|
|
8769
|
+
authority that already exists (lineage / challenges / pollution = the engine's control plane;
|
|
8770
|
+
binding / content / custody = the backend's committed account + evidence chain).
|
|
8771
|
+
🔴 OPEN on purpose, like every other 2xx body of this family — the enforcement is ASYMMETRIC: a REQUEST
|
|
8772
|
+
body is policed by the server's own strict gate (declaring it closed is an honest description), while a
|
|
8773
|
+
RESPONSE body has no enforcer at all, so a consumer that closes one only turns "the producer shipped
|
|
8774
|
+
first" into "this legal response failed validation". Tolerate new keys here; never reject on them.
|
|
8775
|
+
🔴 `v` is a version envelope: a consumer reading `v > 1` must REFUSE, never reinterpret.
|
|
8776
|
+
required: [v, id, binding, contributors, contentState, custody]
|
|
8777
|
+
additionalProperties: true
|
|
8778
|
+
properties:
|
|
8779
|
+
v: { type: integer, enum: [1] }
|
|
8780
|
+
id: { type: string }
|
|
8781
|
+
binding: { $ref: '#/components/schemas/MemoryProvenanceBinding' }
|
|
8782
|
+
contributors:
|
|
8783
|
+
type: array
|
|
8784
|
+
description: >
|
|
8785
|
+
The committed lineage BY-ENTRY projection, each row joined with its session's pollution record where
|
|
8786
|
+
one exists. PENDING (unsettled) lineage rows are never contributions — they surface as the
|
|
8787
|
+
`lineage_pending` exclusion instead.
|
|
8788
|
+
items:
|
|
8789
|
+
type: object
|
|
8790
|
+
required: [sessionId, lastRev]
|
|
8791
|
+
additionalProperties: true
|
|
8792
|
+
properties:
|
|
8793
|
+
sessionId: { type: string }
|
|
8794
|
+
lastRev: { type: string }
|
|
8795
|
+
lastAt: { type: integer }
|
|
8796
|
+
polluted:
|
|
8797
|
+
type: object
|
|
8798
|
+
required: [at, reason]
|
|
8799
|
+
additionalProperties: true
|
|
8800
|
+
properties:
|
|
8801
|
+
at: { type: integer }
|
|
8802
|
+
reason: { type: string }
|
|
8803
|
+
exclusion:
|
|
8804
|
+
type: object
|
|
8805
|
+
description: "The model-visible read faces' withholding state for this id, when any."
|
|
8806
|
+
required: [code]
|
|
8807
|
+
additionalProperties: true
|
|
8808
|
+
properties:
|
|
8809
|
+
code: { type: string, enum: [challenged, lineage_pending] }
|
|
8810
|
+
generation: { type: integer }
|
|
8811
|
+
at: { type: integer }
|
|
8812
|
+
contentState:
|
|
8813
|
+
type: string
|
|
8814
|
+
enum: [present, unavailable, capability-absent, absent]
|
|
8815
|
+
description: >
|
|
8816
|
+
The content half, LINKED to `binding` by construction: `absent` ⇔ binding `absent`;
|
|
8817
|
+
`capability-absent` ⇔ binding `unknown`; a row answers `present` (committed content servable — only
|
|
8818
|
+
then may `ingest` ride) or `unavailable` ("cannot read it" stays distinct from "does not exist").
|
|
8819
|
+
ingest:
|
|
8820
|
+
type: object
|
|
8821
|
+
description: "The typed repo-file ingest provenance carried by the committed content's frontmatter (with the un-whitewashable `trust` marker). Present only with `contentState: present`."
|
|
8822
|
+
required: [kind, path, contentHash, ingestedAt]
|
|
8823
|
+
additionalProperties: true
|
|
8824
|
+
properties:
|
|
8825
|
+
kind: { type: string, enum: [repo_file] }
|
|
8826
|
+
path: { type: string }
|
|
8827
|
+
contentHash: { type: string }
|
|
8828
|
+
ingestedAt: { type: integer }
|
|
8829
|
+
trust: { type: string, enum: [untrusted] }
|
|
8830
|
+
custody:
|
|
8831
|
+
type: object
|
|
8832
|
+
description: >
|
|
8833
|
+
The per-id evidence-chain read. `damaged` = the chain's integrity is impeached while a sound reading
|
|
8834
|
+
still answers (torn tail / evidence loss / ev-identity contradiction; `reason` carries the detail when
|
|
8835
|
+
one is known). `capability-absent` = the backend has no custody face — `events` is then empty and is
|
|
8836
|
+
NEVER fabricated.
|
|
8837
|
+
required: [state, events]
|
|
8838
|
+
additionalProperties: true
|
|
8839
|
+
properties:
|
|
8840
|
+
state: { type: string, enum: [complete, damaged, capability-absent] }
|
|
8841
|
+
events: { type: array, items: { $ref: '#/components/schemas/MemoryTransferEvidence' } }
|
|
8842
|
+
reason: { type: string }
|
|
8843
|
+
|
|
8844
|
+
MemoryErasureRequest:
|
|
8845
|
+
type: object
|
|
8846
|
+
description: >
|
|
8847
|
+
Request body of POST /v1/memory/erase.
|
|
8848
|
+
🔴 `requestId` is THIS request's IDEMPOTENCY IDENTITY and neither the server nor the engine ever mints one
|
|
8849
|
+
— a retry MUST carry the SAME id (a lost ack must not open a second real delete). Contrast
|
|
8850
|
+
`MemoryOriginClearRequest.requestId`, which is an AUDIT ATTRIBUTION and may legitimately change on a resume.
|
|
8851
|
+
🔴 `allowUnevidenced` is NEVER INJECTED by the server: the degraded lane is a human authorization statement.
|
|
8852
|
+
required: [requestId, select]
|
|
8853
|
+
additionalProperties: false
|
|
8854
|
+
properties:
|
|
8855
|
+
requestId: { type: string, minLength: 1 }
|
|
8856
|
+
select:
|
|
8857
|
+
type: object
|
|
8858
|
+
description: >
|
|
8859
|
+
The erasure selector. The documented forms are `{ids: string[]}` / `{scope: string}` /
|
|
8860
|
+
`{sessionId: string}`, EXACTLY ONE of them. Declared OPEN on purpose: the rule's single owner is core
|
|
8861
|
+
(`erasureRequestInvalid`) and the HTTP layer validates only the top-level structure — restating the
|
|
8862
|
+
field table here would be a second source of truth, and would pre-reject a fourth form core may grow.
|
|
8863
|
+
A form-right-meaning-wrong selector is answered 400 `config.memory_erasure_request` by the engine.
|
|
8864
|
+
additionalProperties: true
|
|
8865
|
+
allowUnevidenced:
|
|
8866
|
+
type: boolean
|
|
8867
|
+
description: "Opt-in to the evidence-capability-ABSENT degradation lane. Default = a loud refusal (409 `memory.erasure_evidence_unavailable`)."
|
|
8868
|
+
|
|
8869
|
+
MemoryErasedBinding:
|
|
8870
|
+
description: "core `ErasedBinding` — one erased row's coordinates (the snapshot's tagged binding law, without the account-trace extras)."
|
|
8871
|
+
oneOf:
|
|
8872
|
+
- type: object
|
|
8873
|
+
required: [state, scope, slug]
|
|
8874
|
+
additionalProperties: true
|
|
8875
|
+
properties:
|
|
8876
|
+
state: { type: string, enum: [bound] }
|
|
8877
|
+
scope: { type: string }
|
|
8878
|
+
slug: { type: string }
|
|
8879
|
+
- type: object
|
|
8880
|
+
required: [state]
|
|
8881
|
+
additionalProperties: true
|
|
8882
|
+
properties:
|
|
8883
|
+
state: { type: string, enum: [unbound] }
|
|
8884
|
+
|
|
8885
|
+
MemoryErasureAttestation:
|
|
8886
|
+
type: object
|
|
8887
|
+
description: >
|
|
8888
|
+
200 body of POST /v1/memory/erase — core `MemoryErasureAttestation` ITSELF, the deliverable a compliance
|
|
8889
|
+
caller archives. Every claim is black-box re-checkable.
|
|
8890
|
+
🔴 READ `evidenceCapability` BEFORE DECIDING WHETHER A RETRY IS SAFE — the two legs have different
|
|
8891
|
+
contracts (see the endpoint description). OPEN for the same reason as `EntryProvenanceAccount`.
|
|
8892
|
+
required: [v, requestId, at, status, evidenceCapability, custodyState, select, selectHash, resolvedIds, erased, notFound, conflicts, residuals]
|
|
8893
|
+
additionalProperties: true
|
|
8894
|
+
properties:
|
|
8895
|
+
v: { type: integer, enum: [1], description: 'Version envelope — a consumer reading `v > 1` must refuse, never reinterpret.' }
|
|
8896
|
+
requestId: { type: string }
|
|
8897
|
+
at: { type: integer }
|
|
8898
|
+
status:
|
|
8899
|
+
type: string
|
|
8900
|
+
enum: [complete, partial]
|
|
8901
|
+
description: '`complete` ⇔ conflicts empty ∧ every pinned id ∈ erased ∪ erasedPreviously ∪ (notFound with `custodyState === "complete"`). Everything else is `partial` (a replay converges it).'
|
|
8902
|
+
evidenceCapability:
|
|
8903
|
+
type: string
|
|
8904
|
+
enum: [journal, none]
|
|
8905
|
+
description: '`journal` = the File evidence path; `none` = the explicit `allowUnevidenced` degradation lane. ALWAYS PRESENT — a silent downgrade is the forbidden shape.'
|
|
8906
|
+
custodyState:
|
|
8907
|
+
type: string
|
|
8908
|
+
enum: [complete, damaged, capability-absent]
|
|
8909
|
+
description: "The evidence chain's integrity as read at assembly. `damaged` ⇒ every notFound row carries `historyUnknown` and `erasedPreviously` is never produced."
|
|
8910
|
+
select: { type: object, additionalProperties: true, description: 'The selector, echoed verbatim (same open form as the request).' }
|
|
8911
|
+
selectHash: { type: string, description: "sha256 hex of the selector's canonical JSON — equals the anchor row's." }
|
|
8912
|
+
resolvedIds: { type: array, items: { type: string }, description: "The pinned id set (= the anchor row's ids; a replay reads it back, never re-resolves)." }
|
|
8913
|
+
erased:
|
|
8914
|
+
type: array
|
|
8915
|
+
items:
|
|
8916
|
+
type: object
|
|
8917
|
+
required: [id, rev, binding, projectionsRemoved, sessions]
|
|
8918
|
+
additionalProperties: true
|
|
8919
|
+
properties:
|
|
8920
|
+
id: { type: string }
|
|
8921
|
+
rev: { type: string }
|
|
8922
|
+
binding: { $ref: '#/components/schemas/MemoryErasedBinding' }
|
|
8923
|
+
evidenceEv: { type: string, description: "The delete evidence row's `ev` — absent in `none` mode." }
|
|
8924
|
+
projectionsRemoved: { type: integer, description: 'Distinct census-confirmed projection files this transaction physically deleted (the sweep arm can answer > 1).' }
|
|
8925
|
+
sessions: { type: array, items: { type: string }, description: 'Contributing sessions (`none` mode answers []). A replay after a crash carries the chain''s set — an honest downgrade, never a fabricated rebuild.' }
|
|
8926
|
+
notFound:
|
|
8927
|
+
type: array
|
|
8928
|
+
description: >
|
|
8929
|
+
No row at judgment time and not claimable as erased under THIS request. Covers never-existed /
|
|
8930
|
+
organically deleted / deleted by another request — the chain can distinguish them, the attestation does
|
|
8931
|
+
not pretend to. `historyUnknown` ⇔ `custodyState !== "complete"`.
|
|
8932
|
+
items:
|
|
8933
|
+
type: object
|
|
8934
|
+
required: [id]
|
|
8935
|
+
additionalProperties: true
|
|
8936
|
+
properties:
|
|
8937
|
+
id: { type: string }
|
|
8938
|
+
historyUnknown: { type: boolean, enum: [true] }
|
|
8939
|
+
erasedPreviously:
|
|
8940
|
+
type: array
|
|
8941
|
+
description: 'Chain-read rows: an origin-ABSENT delete row carrying THIS requestId. Present only when non-empty; never produced in `none` mode or over a damaged chain.'
|
|
8942
|
+
items:
|
|
8943
|
+
type: object
|
|
8944
|
+
required: [id, ev, at]
|
|
8945
|
+
additionalProperties: true
|
|
8946
|
+
properties:
|
|
8947
|
+
id: { type: string }
|
|
8948
|
+
ev: { type: string }
|
|
8949
|
+
at: { type: integer }
|
|
8950
|
+
from:
|
|
8951
|
+
type: object
|
|
8952
|
+
required: [scope, slug]
|
|
8953
|
+
additionalProperties: true
|
|
8954
|
+
properties:
|
|
8955
|
+
scope: { type: string }
|
|
8956
|
+
slug: { type: string }
|
|
8957
|
+
conflicts:
|
|
8958
|
+
type: array
|
|
8959
|
+
description: 'Per-id PLANNING-stage faults (io/plan errors under the lock — there is no CAS loser inside a single lock span). Non-fatal; a replay converges them.'
|
|
8960
|
+
items:
|
|
8961
|
+
type: object
|
|
8962
|
+
required: [id, reason]
|
|
8963
|
+
additionalProperties: true
|
|
8964
|
+
properties:
|
|
8965
|
+
id: { type: string }
|
|
8966
|
+
reason: { type: string }
|
|
8967
|
+
residuals:
|
|
8968
|
+
type: object
|
|
8969
|
+
required: [quarantineHits, quarantineOpaque, propagation]
|
|
8970
|
+
additionalProperties: true
|
|
8971
|
+
properties:
|
|
8972
|
+
quarantineHits: { type: array, items: { type: string }, description: 'Quarantine files whose frontmatter id is in the pinned set (PRESERVE-NOT-DELETE: the attestation enumerates, an explicit second action clears).' }
|
|
8973
|
+
quarantineOpaque: { type: integer, description: 'Quarantine files that could not be attributed (fragments / non-entry shapes) — the honest BOUND of the enumeration.' }
|
|
8974
|
+
indexUncleared: { type: array, items: { type: string }, description: 'Derived MEMORY.md index files that may STILL carry a line naming an erased entry (the erase-time sweep could not read or rewrite them). Present only when non-empty.' }
|
|
8975
|
+
propagation:
|
|
8976
|
+
type: string
|
|
8977
|
+
enum: [local-store-only]
|
|
8978
|
+
description: 'Propagation boundary, SELF-DECLARED ALWAYS: deletion is a THIS-STORE fact; remote-peer convergence is the sync deployment''s half to prove.'
|
|
8979
|
+
|
|
8980
|
+
MemoryEntryOriginMark:
|
|
8981
|
+
type: object
|
|
8982
|
+
description: >
|
|
8983
|
+
core `MemoryEntryOrigin` — one entry's EXTERNAL-ORIGIN marker: the content came from material the model
|
|
8984
|
+
READ, not from first-hand facts in the session. The marker means "verify before use", not "poisoned".
|
|
8985
|
+
🔴 `cause` MAY BE ABSENT AND IS NEVER GUESSED: an entry marked before the cause vocabulary existed is
|
|
8986
|
+
honestly without one.
|
|
8987
|
+
required: [taint, at]
|
|
8988
|
+
additionalProperties: true
|
|
8989
|
+
properties:
|
|
8990
|
+
taint: { type: string, enum: [external] }
|
|
8991
|
+
cause: { type: string, enum: [observed, derived, static, unattributed] }
|
|
8992
|
+
at: { type: integer }
|
|
8993
|
+
|
|
8994
|
+
MemoryOriginExternalEntry:
|
|
8995
|
+
type: object
|
|
8996
|
+
description: 'One row of GET /v1/memory/origin/external: the marker plus the FULL provenance account. Open (statement-of-fact posture).'
|
|
8997
|
+
required: [id, slug, scope, origin, provenance]
|
|
8998
|
+
additionalProperties: true
|
|
8999
|
+
properties:
|
|
9000
|
+
id: { type: string }
|
|
9001
|
+
slug: { type: string }
|
|
9002
|
+
scope: { type: string }
|
|
9003
|
+
origin: { $ref: '#/components/schemas/MemoryEntryOriginMark' }
|
|
9004
|
+
provenance:
|
|
9005
|
+
allOf: [{ $ref: '#/components/schemas/EntryProvenanceAccount' }]
|
|
9006
|
+
description: 'IS the `EntryProvenanceAccount` of GET /v1/memory/entries/{entryId}/provenance — same discriminated form, same pass-through discipline. A CHALLENGED marked entry is still listed; the exclusion rides in here.'
|
|
9007
|
+
|
|
9008
|
+
MemoryOriginExternalResult:
|
|
9009
|
+
type: object
|
|
9010
|
+
description: >
|
|
9011
|
+
200 body of GET /v1/memory/origin/external.
|
|
9012
|
+
🔴 THE ANSWER COVERS ONLY THE SCOPES YOU NAMED, AND AN EMPTY RESULT IS NEVER "THE STORE IS CLEAN" — this
|
|
9013
|
+
server generation has no scope-enumeration read face, so incompleteness is SILENT. `scopes` echoes the set
|
|
9014
|
+
ACTUALLY AUDITED (after trimming / dropping empty segments / de-duplicating in order) so the coverage is
|
|
9015
|
+
observable rather than inferred.
|
|
9016
|
+
⚠️ `withheldOriginTagged` (an integer, minted only when > 0) rides the OPEN tail when the request did NOT
|
|
9017
|
+
declare `originAware=1`: every row of this face is marked, so the fence withholds them all. A caller that
|
|
9018
|
+
declares the flag (the SDK always does) never sees the key.
|
|
9019
|
+
required: [scopes, entries]
|
|
9020
|
+
additionalProperties: true
|
|
9021
|
+
properties:
|
|
9022
|
+
scopes: { type: array, items: { type: string } }
|
|
9023
|
+
entries: { type: array, items: { $ref: '#/components/schemas/MemoryOriginExternalEntry' } }
|
|
9024
|
+
|
|
9025
|
+
MemoryOriginClearanceEvent:
|
|
9026
|
+
type: object
|
|
9027
|
+
description: 'One TERMINAL event of a clearance (who / when / to / detail is the whole of the concept "event"). `detail` absent ⇒ the KEY is absent — on an audit face "no detail written" and "the detail is empty" are not the same thing.'
|
|
9028
|
+
required: [eventId, at, to, requestId]
|
|
9029
|
+
additionalProperties: true
|
|
9030
|
+
properties:
|
|
9031
|
+
eventId: { type: string }
|
|
9032
|
+
at: { type: integer }
|
|
9033
|
+
to: { type: string, enum: [done, failed] }
|
|
9034
|
+
requestId: { type: string, description: "THIS event's resolver (the opener on the normal path; a later resumer on a crash path)." }
|
|
9035
|
+
detail: { type: string, description: 'Mechanical detail (landing slug, refusal reason) — engine-composed, bounded by the writer.' }
|
|
9036
|
+
|
|
9037
|
+
MemoryOriginClearanceRow:
|
|
9038
|
+
type: object
|
|
9039
|
+
description: >
|
|
9040
|
+
One row of GET /v1/memory/origin/clearances — an EXPLICIT WHITELIST PROJECTION of core's
|
|
9041
|
+
`OriginClearanceRow`, guarded on the server side by a compile-time set-difference gate.
|
|
9042
|
+
🔴 core's `entryText` (the cleared entry's full body) is STRIPPED BY THE SERVER and its presence disclosed
|
|
9043
|
+
as `custodyBytes`; the nested `origin` is projected KEY BY KEY, with any member outside the documented
|
|
9044
|
+
three disclosed by NAME ONLY under `originUnknownKeys`.
|
|
9045
|
+
⚠️ THAT IS THE SERVER'S RESTRAINT, NOT A CLIENT-SIDE STRICTNESS — this schema stays OPEN like every 2xx
|
|
9046
|
+
body of the family. The server decides WHAT TO SEND; a consumer schema decides WHAT TO TOLERATE, and
|
|
9047
|
+
closing the latter only breaks on version skew. The whitelist is written down so a reader knows what the
|
|
9048
|
+
server promises, and `entryText` is simply NOT DECLARED so nobody reads a promise that was never made.
|
|
9049
|
+
required: [clearanceId, entryId, scope, slug, baseRev, origin, requestId, reason, at, status, events, custodyBytes]
|
|
9050
|
+
additionalProperties: true
|
|
9051
|
+
properties:
|
|
9052
|
+
clearanceId: { type: string }
|
|
9053
|
+
entryId: { type: string, description: 'The cleared entry. A clearance KEEPS THE SAME ID, so this key joins the clearance to the entry across its whole life.' }
|
|
9054
|
+
scope: { type: string }
|
|
9055
|
+
slug: { type: string }
|
|
9056
|
+
baseRev: { type: string, description: "The committed rev the clearance was judged against (the tombstone's CAS anchor)." }
|
|
9057
|
+
origin:
|
|
9058
|
+
allOf: [{ $ref: '#/components/schemas/MemoryEntryOriginMark' }]
|
|
9059
|
+
description: 'THE MARKER THAT WAS CLEARED, deep-projected to the documented three keys (audit: what exactly did the host vouch away).'
|
|
9060
|
+
originUnknownKeys:
|
|
9061
|
+
type: array
|
|
9062
|
+
items: { type: string }
|
|
9063
|
+
description: "Present only when the on-disk row's `origin` carried members outside the documented three — KEY NAMES ONLY, values never reach the wire. A normal row does not have this key."
|
|
9064
|
+
requestId:
|
|
9065
|
+
type: string
|
|
9066
|
+
description: >
|
|
9067
|
+
WHO cleared it — the mandatory audit attribution, never defaulted by core.
|
|
9068
|
+
🔴 IT IS SELF-DECLARED, NOT VERIFIED: any operator who can reach the valve can book a clearance under
|
|
9069
|
+
another name. The deployment-side counterpart is the server log line `memory_origin_cleared` (verified
|
|
9070
|
+
principal + claimed requestId + clearanceId to join on).
|
|
9071
|
+
reason: { type: string, description: "WHY — the host's stated ground, never defaulted (free text)." }
|
|
9072
|
+
at: { type: integer, description: 'When the row was opened.' }
|
|
9073
|
+
status: { type: string, enum: [pending, done, failed] }
|
|
9074
|
+
tombstonedAt: { type: integer, description: "This clearance's tombstone commit moment (SENT ONLY WHEN PRESENT). A row without it refuses conservatively on the resume leg." }
|
|
9075
|
+
events: { type: array, items: { $ref: '#/components/schemas/MemoryOriginClearanceEvent' } }
|
|
9076
|
+
custodyBytes:
|
|
9077
|
+
type: integer
|
|
9078
|
+
description: >
|
|
9079
|
+
The custody text's UTF-8 BYTE COUNT (not a JS string length — that counts UTF-16 code units and would
|
|
9080
|
+
under-report a CJK entry by up to two thirds, and this number is the only evidence of how much the
|
|
9081
|
+
recovery seat still holds). `0` = this row carries no bytes; `> 0` on a `pending` row = the crash
|
|
9082
|
+
recovery seat still holds a memory's body AND A HUMAN MUST ACT (call the clear endpoint again).
|
|
9083
|
+
|
|
9084
|
+
MemoryOriginClearancesResult:
|
|
9085
|
+
type: object
|
|
9086
|
+
description: '200 body of GET /v1/memory/origin/clearances.'
|
|
9087
|
+
required: [clearances]
|
|
9088
|
+
additionalProperties: true
|
|
9089
|
+
properties:
|
|
9090
|
+
clearances: { type: array, items: { $ref: '#/components/schemas/MemoryOriginClearanceRow' } }
|
|
9091
|
+
|
|
9092
|
+
MemoryOriginClearRequest:
|
|
9093
|
+
type: object
|
|
9094
|
+
description: >
|
|
9095
|
+
Request body of POST /v1/memory/origin/entries/{entryId}/clear.
|
|
9096
|
+
🔴 `requestId` is the AUDIT ATTRIBUTION, not an idempotency key (the opposite of the same-named field on
|
|
9097
|
+
POST /v1/memory/erase): passing a NEW id when resuming a pending row is legal and common. No length cap.
|
|
9098
|
+
🔴 `reason` is shape-checked only; whether an EMPTY reason is acceptable is the ENGINE's judgment
|
|
9099
|
+
(422 `memory.origin_clear_invalid`).
|
|
9100
|
+
required: [requestId, reason]
|
|
9101
|
+
additionalProperties: false
|
|
9102
|
+
properties:
|
|
9103
|
+
requestId: { type: string, minLength: 1 }
|
|
9104
|
+
reason: { type: string }
|
|
9105
|
+
|
|
9106
|
+
MemoryOriginClearReceipt:
|
|
9107
|
+
type: object
|
|
9108
|
+
description: >
|
|
9109
|
+
200 body of POST /v1/memory/origin/entries/{entryId}/clear.
|
|
9110
|
+
🔴 `origin` IS FORWARDED VERBATIM AND MAY CARRY MEMBERS OUTSIDE THE DOCUMENTED THREE. On a freshly opened
|
|
9111
|
+
row it is core's narrow rebuild (`committedOriginOf` mints exactly taint / cause? / at); on the RESUME leg
|
|
9112
|
+
core answers with the ledger row's ORIGINAL object (`clearEntryOrigin` finds the pending row and returns
|
|
9113
|
+
`origin: row.origin`), so version skew, a future core key, or core's own documented by-hand recovery can
|
|
9114
|
+
ride along — on a clearance that ALREADY COMMITTED SUCCESSFULLY. Only the clearances LIST face
|
|
9115
|
+
deep-projects it, which is why this receipt and that row are not the same shape.
|
|
9116
|
+
🔴 THERE IS NO "THIS WAS A RESUME" DISCRIMINATOR HERE, and the resume leg ignores your `reason` — read
|
|
9117
|
+
GET /v1/memory/origin/clearances back and check `reason` / `requestId` before treating a 200 as "my ground
|
|
9118
|
+
was recorded". `clearanceId` is that row's key.
|
|
9119
|
+
required: [entryId, clearanceId, origin, landedSlug]
|
|
9120
|
+
additionalProperties: true
|
|
9121
|
+
properties:
|
|
9122
|
+
entryId: { type: string }
|
|
9123
|
+
clearanceId: { type: string }
|
|
9124
|
+
origin:
|
|
9125
|
+
allOf: [{ $ref: '#/components/schemas/MemoryEntryOriginMark' }]
|
|
9126
|
+
description: 'The marker that was just cleared, verbatim.'
|
|
9127
|
+
landedSlug: { type: string, description: "The re-recorded entry's landing slug." }
|
|
9128
|
+
|
|
8322
9129
|
MetricsSummaryOps:
|
|
8323
9130
|
type: object
|
|
8324
9131
|
description: >
|
|
@@ -16707,9 +17514,9 @@ components:
|
|
|
16707
17514
|
type: string
|
|
16708
17515
|
enum: [darwin, linux]
|
|
16709
17516
|
description: 'The v1 CLOSED SET. A third value (e.g. `win32`) ⇒ 400 request.field_invalid — never silently accepted.'
|
|
16710
|
-
platformArch: { type: string }
|
|
16711
|
-
displayName: { type: string, description: 'Echoed back by `GET /v1/devices` as `name`, and HONESTLY ABSENT there when empty.' }
|
|
16712
|
-
workspaceRoot: { type: string, minLength: 1, description: 'The device-side root every instruction resolves against. Required and non-empty.
|
|
17517
|
+
platformArch: { type: string, maxLength: 32, description: 'At most 32 **UTF-8 bytes** (producer gates byte length; `maxLength` is the looser character bound, see the unit convention).' }
|
|
17518
|
+
displayName: { type: string, maxLength: 255, description: 'Echoed back by `GET /v1/devices` as `name`, and HONESTLY ABSENT there when empty. At most 255 **UTF-8 bytes** (producer gates byte length; `maxLength` is the looser character bound).' }
|
|
17519
|
+
workspaceRoot: { type: string, minLength: 1, maxLength: 1024, description: 'The device-side root every instruction resolves against. Required and non-empty. At most 1024 **UTF-8 bytes** (producer gates byte length; `maxLength` is the looser character bound) ⇒ over it 400 request.field_invalid (a silently truncated value is a DIFFERENT value).' }
|
|
16713
17520
|
|
|
16714
17521
|
DeviceEnrollResult:
|
|
16715
17522
|
type: object
|
|
@@ -16730,7 +17537,7 @@ components:
|
|
|
16730
17537
|
description: 'The `POST /v1/devices/{deviceId}/revoke` body (closed shape; the only accepted key is `reason`). Absent body is folded to `{}` server-side.'
|
|
16731
17538
|
additionalProperties: false
|
|
16732
17539
|
properties:
|
|
16733
|
-
reason: { type: string, maxLength: 512, description: 'At most 512
|
|
17540
|
+
reason: { type: string, maxLength: 512, description: 'At most 512 **UTF-8 bytes** (the producer gates `Buffer.byteLength(reason, "utf8")`, NOT `.length` — this is the one family where the file''s unit convention''s explicit-UTF-8 carve-out applies); `maxLength: 512` is the deliberately LOOSER character bound (a multi-byte value can pass this schema and still get 400 request.field_invalid naming the field — loud, never a client-side false refusal). A silently truncated audit reason is a DIFFERENT reason.' }
|
|
16734
17541
|
|
|
16735
17542
|
DeviceRevokeResult:
|
|
16736
17543
|
type: object
|