@sema-agent/core 7.37.1 → 7.38.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +168 -0
- package/dist/agents/observer.js +1 -1
- package/dist/agents/peer-notices.js +4 -4
- package/dist/agents/subagent.js +1 -1
- package/dist/agents/team-role.js +2 -2
- package/dist/brain/anthropic.js +10 -9
- package/dist/brain/errors.d.ts +26 -0
- package/dist/brain/errors.js +34 -0
- package/dist/brain/input-too-long.d.ts +0 -31
- package/dist/brain/input-too-long.js +1 -18
- package/dist/brain/open-responses.js +10 -9
- package/dist/brain/openai.js +10 -9
- package/dist/brain/status-sink.d.ts +1 -1
- package/dist/brain/stream-engine.js +10 -2
- package/dist/brain/stream-shared.d.ts +11 -3
- package/dist/brain/stream-shared.js +4 -2
- package/dist/brain/tool-call-id.d.ts +29 -8
- package/dist/brain/tool-call-id.js +36 -6
- package/dist/brain/tool-call-repair.js +1 -1
- package/dist/core/a2a-task-state.js +2 -2
- package/dist/core/artifact-host.js +6 -6
- package/dist/core/ask-origin.js +6 -6
- package/dist/core/ask-unresolvable-notice.js +1 -1
- package/dist/core/auto-mode.js +4 -4
- package/dist/core/background-agent-store.js +2 -2
- package/dist/core/checkpoint-execution-record.js +1 -1
- package/dist/core/checkpoint-store.d.ts +2 -2
- package/dist/core/checkpoint-store.js +2 -2
- package/dist/core/child-clamp-seats.js +3 -3
- package/dist/core/compliance.js +1 -1
- package/dist/core/context-edit.js +1 -1
- package/dist/core/context-guard.d.ts +0 -19
- package/dist/core/context-guard.js +0 -38
- package/dist/core/decision-receipt.d.ts +27 -1
- package/dist/core/decision-receipt.js +176 -1
- package/dist/core/engine-notice.d.ts +60 -8
- package/dist/core/engine-notice.js +2 -1
- package/dist/core/gate-fold.js +3 -2
- package/dist/core/gate-outcome.js +4 -4
- package/dist/core/governance-codes.d.ts +221 -13
- package/dist/core/governance-codes.js +104 -4
- package/dist/core/hook-seats.js +2 -2
- package/dist/core/hooks.d.ts +21 -2
- package/dist/core/hooks.js +4 -1
- package/dist/core/mailbox-store.js +3 -3
- package/dist/core/mcp-failure.js +4 -4
- package/dist/core/mcp-injection-drop.js +1 -1
- package/dist/core/memory-engine/consolidation.js +1 -1
- package/dist/core/memory-engine/delegation-settlement.d.ts +8 -0
- package/dist/core/memory-engine/delegation-settlement.js +7 -0
- package/dist/core/memory-engine/engine.d.ts +12 -2
- package/dist/core/memory-engine/engine.js +10 -6
- package/dist/core/memory-engine/export-bundle.js +1 -1
- package/dist/core/memory-engine/layout.d.ts +6 -6
- package/dist/core/memory-engine/layout.js +6 -8
- package/dist/core/memory-engine/session-write-home.d.ts +30 -0
- package/dist/core/memory-engine/session-write-home.js +34 -0
- package/dist/core/memory-engine/tools.js +1 -1
- package/dist/core/memory-engine/types.js +1 -1
- package/dist/core/memory.js +1 -1
- package/dist/core/mode-policy.d.ts +10 -1
- package/dist/core/mode-policy.js +15 -7
- package/dist/core/model-seat.js +1 -1
- package/dist/core/permission-rule-model.js +3 -3
- package/dist/core/permission-rule-store.js +2 -2
- package/dist/core/read-only-shell-table.js +8 -8
- package/dist/core/retention.js +2 -2
- package/dist/core/runner/assemble-result.js +15 -5
- package/dist/core/runner/classifier-ladder.js +1 -1
- package/dist/core/runner/denial-limit-arms.d.ts +2 -0
- package/dist/core/runner/denial-limit-arms.js +6 -2
- package/dist/core/runner/inherited-ask-grants.d.ts +6 -5
- package/dist/core/runner/inherited-ask-grants.js +3 -2
- package/dist/core/runner/prepare-artifact.d.ts +3 -2
- package/dist/core/runner/prepare-context-lane.d.ts +19 -15
- package/dist/core/runner/prepare-context-lane.js +11 -15
- package/dist/core/runner/prepare-gate-stations.js +5 -5
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -3
- package/dist/core/runner/prepare-hands-readface.js +1 -0
- package/dist/core/runner/prepare-inherited-gate.js +1 -1
- package/dist/core/runner/prepare-policy-chain.js +10 -10
- package/dist/core/runner/prepare-turn-wiring.d.ts +2 -2
- package/dist/core/runner/run-harness-handlers.js +3 -0
- package/dist/core/runner/run-identity-wiring.d.ts +1 -1
- package/dist/core/runner/run-identity-wiring.js +0 -2
- package/dist/core/runner/run-leg.js +2 -2
- package/dist/core/runner/run-stop-and-final-verify.js +1 -0
- package/dist/core/runner/run-turn-boundary.js +1 -1
- package/dist/core/runner/session-mode-seats.d.ts +11 -3
- package/dist/core/runner/session-mode-seats.js +6 -0
- package/dist/core/runner/session-rule-policy.js +2 -3
- package/dist/core/runner/stream-steer-verb.js +1 -1
- package/dist/core/runner/tool-call-id-fold-seat.js +16 -9
- package/dist/core/runner/tool-turn-repair-seat.d.ts +11 -1
- package/dist/core/runner/tool-turn-repair-seat.js +35 -0
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/runtime-caps.js +1 -1
- package/dist/core/safety-axis-vocab.d.ts +5 -0
- package/dist/core/safety-axis-vocab.js +5 -0
- package/dist/core/session-reconcile.d.ts +9 -7
- package/dist/core/session-reconcile.js +21 -1
- package/dist/core/shared-memory/types.js +1 -1
- package/dist/core/side-query.d.ts +6 -1
- package/dist/core/store-contracts/read-back-shape-contract.js +1 -1
- package/dist/core/swappable-deps.js +1 -1
- package/dist/core/task-notification.js +4 -4
- package/dist/core/task-registry-shared.js +1 -1
- package/dist/core/terminal-cause.d.ts +19 -0
- package/dist/core/tool-face.js +6 -6
- package/dist/core/tool-policy.d.ts +17 -11
- package/dist/core/tool-policy.js +38 -72
- package/dist/core/tool-roster.js +1 -1
- package/dist/core/tool-search-setting.js +1 -1
- package/dist/core/trace.d.ts +5 -4
- package/dist/core/types.d.ts +2 -2
- package/dist/core/types.js +1 -1
- package/dist/core/wiring-manifest.js +2 -2
- package/dist/core/write-protect.js +1 -1
- package/dist/engine/harness/agent-harness.js +10 -6
- package/dist/engine/llm/tool-turns.d.ts +93 -4
- package/dist/engine/llm/tool-turns.js +63 -9
- package/dist/engine/llm/types.d.ts +11 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +4 -2
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/workflow-governance.js +2 -2
- package/dist/orchestration/workflow-types.js +2 -2
- package/dist/shell/lex.js +7 -7
- package/dist/tools/fs/bash-program-position.js +2 -2
- package/dist/tools/fs/fs-shared.d.ts +2 -1
- package/dist/tools/fs/fs-shared.js +1 -1
- package/dist/tools/fs/index.d.ts +13 -6
- package/dist/tools/fs/index.js +12 -4
- package/dist/tools/fs/read-deny-home.d.ts +98 -45
- package/dist/tools/fs/read-deny-home.js +294 -117
- package/dist/tools/fs/read-deny.d.ts +2 -2
- package/dist/tools/fs/read-deny.js +2 -2
- package/dist/tools/fs/safety.d.ts +2 -0
- package/dist/tools/fs/safety.js +2 -2
- package/dist/tools/fs/search.d.ts +4 -3
- package/dist/tools/fs/search.js +3 -3
- package/dist/tools/fs/shell-read-check.d.ts +9 -0
- package/dist/tools/fs/shell-read-check.js +3 -2
- package/dist/tools/fs/shell-read-establish.d.ts +3 -0
- package/dist/tools/fs/shell-read-establish.js +1 -1
- package/dist/tools/fs/shell-read-identity.d.ts +4 -3
- package/dist/tools/fs/shell-read-identity.js +2 -2
- package/dist/tools/fs/shell-read-judge.js +2 -2
- package/dist/tools/fs/shell-read-plan.js +2 -2
- package/dist/tools/fs/shell-read-write-targets.js +1 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +17 -1
- package/wire-changes/core@7.38.0.json +664 -0
- package/wire-changes/core@7.38.1.json +56 -0
|
@@ -829,6 +829,11 @@ export interface SessionMemoryStatus {
|
|
|
829
829
|
/** Newest lineage `lastAt` for this session (ms epoch) — rides the same single ledger read as
|
|
830
830
|
* `committedCount`. Absent = ledger unreadable, or no committed contribution exists at all. */
|
|
831
831
|
lastCaptureAt?: number;
|
|
832
|
+
/** The canonical absolute directory of the session's write home: the home of the write scope its
|
|
833
|
+
* newest leg wrote under (the path that leg's handle carries as `writableRoot`). Absent = no source
|
|
834
|
+
* names a scope, or a source faulted — see the method doc for the sources and the absence law. An
|
|
835
|
+
* engine predating the key never answers it, but absence alone does not identify such an engine. */
|
|
836
|
+
writeHome?: string;
|
|
832
837
|
}
|
|
833
838
|
export declare class MemoryEngine {
|
|
834
839
|
private readonly backend;
|
|
@@ -1608,9 +1613,14 @@ export declare class MemoryEngine {
|
|
|
1608
1613
|
* carried rather than dropped; absent ⇔ the ledger is unreadable OR the session has no
|
|
1609
1614
|
* committed contribution at all (there is no such moment to name — the one key whose absence
|
|
1610
1615
|
* also covers "no fact exists", stated here so consumers need not guess).
|
|
1616
|
+
* - `writeHome` — the canonical home of the write scope the session wrote under most recently (the newer of the dated account row and the newest committed contribution); sources, order and the
|
|
1617
|
+
* absence law in {@link sessionWriteHomeOf} (a faulted source never falls through).
|
|
1611
1618
|
*
|
|
1612
|
-
*
|
|
1613
|
-
* headers/getByIds)
|
|
1619
|
+
* Committed reads throughout (capture face + lineage sidecar + session-account sidecar + scope registry +
|
|
1620
|
+
* audit-face headers/getByIds), none of which commits anything for this session; the File backend's
|
|
1621
|
+
* audit face runs that backend's layout seat as each of its lock-free read faces does (loose root content
|
|
1622
|
+
* in a v2 store is adopted into its owner's home — `foldedCount`'s header read has always reached it).
|
|
1623
|
+
* Never throws — an unreadable source is an absent key, which IS the answer.
|
|
1614
1624
|
*/
|
|
1615
1625
|
sessionMemoryStatus(sessionId: string): Promise<SessionMemoryStatus>;
|
|
1616
1626
|
/** The committed, side-effect-free audit read: the adoption-restricted committed view when the
|
|
@@ -13,6 +13,7 @@ import { ConsolidationRefusedError, CONSOLIDATION_FORCE_THROTTLE_FLOOR_MS, CONSO
|
|
|
13
13
|
import { deriveShadowedSet } from "./shadowing.js";
|
|
14
14
|
import { classifySessionSettlements, clearHoldResolution, closeSessionAccount, disposeHold, effectiveSettlements, expireOverdueSettlements, foreignDanglingSessionAccounts, openLayerWriters, terminateSessionAccount, sessionAccountLeg, markHoldReleased, openInstructionHold, openSessionAccount, readHoldCustody, readHolds, reconcileHolds, replayExternalSettlementEffects, resolveHoldRecord, resolveSessionAccountRecord, resolveSettlementRecord, sessionSettlements, sessionUnattributedSet, emptyUnattributedSet, convertV1SidecarKeys, } from "./delegation-settlement.js";
|
|
15
15
|
import { noReplaceRestore, retireStagingToCustody, stagingPathBeside } from "./delegation-settlement.js";
|
|
16
|
+
import { sessionWriteHomeOf } from "./session-write-home.js";
|
|
16
17
|
import { landedUndecided, layerDigest, layerObservationLookup, readOnlyLayerCandidates, readOnlyLayerCensus, recordLayerObservations, settleUndecidedLayerObservations, observationBacked, sameObservation, undecidedLayerObservations } from "./layer-observations.js";
|
|
17
18
|
import { markOriginClearanceTombstoned, openOriginClearance, readOriginClearances, settleOriginClearance } from "./origin-clearance.js";
|
|
18
19
|
import { DEFAULT_MAX_ENTRY_DEPTH, MEMORY_INDEX_FILENAME, canonicalJsonStringify, captureErasureInput, erasureRequestInvalid, erasureSelectHash, scanEntryFiles, } from "./file-backend.js";
|
|
@@ -2006,7 +2007,7 @@ export class MemoryEngine {
|
|
|
2006
2007
|
unattributedSet = sessionUnattributedSet(this.controlDir, lineageSessionId);
|
|
2007
2008
|
}
|
|
2008
2009
|
}
|
|
2009
|
-
const rec = reconcileLineage(this.controlDir
|
|
2010
|
+
const rec = reconcileLineage(this.controlDir);
|
|
2010
2011
|
this.settlePromotions(rec.promoted);
|
|
2011
2012
|
for (const u of rec.undecidable) {
|
|
2012
2013
|
report.warnings.push(u.kind === "latch-only"
|
|
@@ -2779,7 +2780,7 @@ export class MemoryEngine {
|
|
|
2779
2780
|
if (lineageArmed) {
|
|
2780
2781
|
try {
|
|
2781
2782
|
recordLineageCredential(this.controlDir, txnId, patchReport.applied.filter((a) => a.op !== "delete").map((a) => a.id), this.now);
|
|
2782
|
-
const promoted = promoteLineagePending(this.controlDir, txnId
|
|
2783
|
+
const promoted = promoteLineagePending(this.controlDir, txnId);
|
|
2783
2784
|
this.settlePromotions(promoted);
|
|
2784
2785
|
}
|
|
2785
2786
|
catch (err) {
|
|
@@ -3108,7 +3109,7 @@ export class MemoryEngine {
|
|
|
3108
3109
|
}
|
|
3109
3110
|
try {
|
|
3110
3111
|
recordLineageCredential(this.controlDir, txnId, patchReport.applied.filter((a) => a.op !== "delete").map((a) => a.id), this.now);
|
|
3111
|
-
const promoted = promoteLineagePending(this.controlDir, txnId
|
|
3112
|
+
const promoted = promoteLineagePending(this.controlDir, txnId);
|
|
3112
3113
|
this.settlePromotions(promoted);
|
|
3113
3114
|
}
|
|
3114
3115
|
catch (err) {
|
|
@@ -3720,7 +3721,7 @@ export class MemoryEngine {
|
|
|
3720
3721
|
if (lineageArmed) {
|
|
3721
3722
|
try {
|
|
3722
3723
|
recordLineageCredential(this.controlDir, txnId, report.applied.filter((a) => a.op !== "delete").map((a) => a.id), this.now);
|
|
3723
|
-
this.settlePromotions(promoteLineagePending(this.controlDir, txnId
|
|
3724
|
+
this.settlePromotions(promoteLineagePending(this.controlDir, txnId));
|
|
3724
3725
|
}
|
|
3725
3726
|
catch (err) {
|
|
3726
3727
|
plan.audit.push({ at: this.now(), event: "lineage_settle_failed", detail: thrownText(err) });
|
|
@@ -4325,13 +4326,13 @@ export class MemoryEngine {
|
|
|
4325
4326
|
let mine;
|
|
4326
4327
|
try {
|
|
4327
4328
|
const committed = readLineageRecord(this.controlDir).committed;
|
|
4328
|
-
const ids = new
|
|
4329
|
+
const ids = new Map();
|
|
4329
4330
|
let lastAt;
|
|
4330
4331
|
for (const [entryId, sessions] of Object.entries(committed)) {
|
|
4331
4332
|
const c = sessions[sessionId];
|
|
4332
4333
|
if (c === undefined)
|
|
4333
4334
|
continue;
|
|
4334
|
-
ids.
|
|
4335
|
+
ids.set(entryId, c.lastAt);
|
|
4335
4336
|
if (lastAt === undefined || c.lastAt > lastAt)
|
|
4336
4337
|
lastAt = c.lastAt;
|
|
4337
4338
|
}
|
|
@@ -4342,6 +4343,9 @@ export class MemoryEngine {
|
|
|
4342
4343
|
}
|
|
4343
4344
|
catch {
|
|
4344
4345
|
}
|
|
4346
|
+
const writeHome = await sessionWriteHomeOf({ memoryDir: this.memoryDir, controlDir: this.controlDir, sessionId, contributions: mine, readEntries: (ids) => this.committedAuditFace().getByIds(ids) });
|
|
4347
|
+
if (writeHome !== undefined)
|
|
4348
|
+
out.writeHome = writeHome;
|
|
4345
4349
|
if (mine !== undefined) {
|
|
4346
4350
|
if (mine.size === 0) {
|
|
4347
4351
|
out.foldedCount = 0;
|
|
@@ -3,7 +3,7 @@ import { canonicalJsonStringify } from "./file-backend.js";
|
|
|
3
3
|
import { computeEntryRev } from "./frontmatter.js";
|
|
4
4
|
import { MEMORY_ORIGIN_CAUSES } from "./types.js";
|
|
5
5
|
import { ambiguousOriginRepresentation, committedOriginOf } from "./frontmatter.js";
|
|
6
|
-
export const BUNDLE_SECTION_KEYS = ["meta", "entries", "governance", "residuals"];
|
|
6
|
+
export const BUNDLE_SECTION_KEYS = Object.freeze(["meta", "entries", "governance", "residuals"]);
|
|
7
7
|
function sha256Line(text) {
|
|
8
8
|
return createHash("sha256").update(`${text}\n`, "utf8").digest("hex");
|
|
9
9
|
}
|
|
@@ -733,10 +733,10 @@ export interface LineagePromotion {
|
|
|
733
733
|
marked?: true;
|
|
734
734
|
}
|
|
735
735
|
/** Promote a CREDENTIALED pending transaction: rows named by the credential's applied set join the
|
|
736
|
-
* committed contribution set (deduped by (entryId, sessionId) — a repeat pair
|
|
737
|
-
* lastRev
|
|
736
|
+
* committed contribution set (deduped by (entryId, sessionId) — a repeat pair takes the incoming
|
|
737
|
+
* lastRev and the credential's date, never grows the set); rows NOT in the applied set (conflicted/refused ops) are
|
|
738
738
|
* dropped without ever becoming ancestry. Throws when the txn is unknown or uncredentialed. */
|
|
739
|
-
export declare function promoteLineagePending(controlDir: string, txnId: string
|
|
739
|
+
export declare function promoteLineagePending(controlDir: string, txnId: string): LineagePromotion[];
|
|
740
740
|
/** The host adjudication for the UNDECIDABLE crash window (a pending txn with no credential —
|
|
741
741
|
* design/180 r5: never auto-discarded). `"promote"` = the host verified the commit landed (all
|
|
742
742
|
* staged rows join the committed set); `"discard"` = the host verified it did not. Idempotent on
|
|
@@ -744,7 +744,7 @@ export declare function promoteLineagePending(controlDir: string, txnId: string,
|
|
|
744
744
|
export declare function adjudicateLineagePending(controlDir: string, txnId: string, action: "promote" | "discard", now: () => number): LineagePromotion[];
|
|
745
745
|
/** Reconciliation (harvest-opening / host-triggered): promote every CREDENTIALED orphan pending
|
|
746
746
|
* txn; report the uncredentialed ones (their latch stays until the host adjudicates). */
|
|
747
|
-
export declare function reconcileLineage(controlDir: string
|
|
747
|
+
export declare function reconcileLineage(controlDir: string): {
|
|
748
748
|
promoted: LineagePromotion[];
|
|
749
749
|
undecidable: Array<{
|
|
750
750
|
txnId: string;
|
|
@@ -853,8 +853,8 @@ export interface LineageImportDivergence {
|
|
|
853
853
|
}
|
|
854
854
|
/**
|
|
855
855
|
* design/178 v2-c §5.1 — the lineage FIDELITY importer: write bundle lineage rows into the
|
|
856
|
-
* committed set VERBATIM (`lastRev`/`lastAt` carried byte-for-byte
|
|
857
|
-
*
|
|
856
|
+
* committed set VERBATIM (`lastRev`/`lastAt` carried byte-for-byte: the promote leg dates a contribution by its credential,
|
|
857
|
+
* the import carries the exporter's date — neither reads a clock here). Deterministic same-key merge, idempotent under
|
|
858
858
|
* re-import of the same bundle:
|
|
859
859
|
* - destination has no (entryId, sessionId) row ⇒ the source row lands verbatim;
|
|
860
860
|
* - both present, source `lastAt` is GREATER ⇒ source wins ("last contribution" is a time-axis
|
|
@@ -313,7 +313,7 @@ export function dirCaseFolds(dir) {
|
|
|
313
313
|
}
|
|
314
314
|
export const SCOPE_DIR_LABEL_RE = /^[A-Za-z0-9_][A-Za-z0-9._-]*$/;
|
|
315
315
|
export const CASEFOLD_PROBE_PREFIX = ".casefold-probe-";
|
|
316
|
-
export const SCOPE_REGISTRY_SHAPES = ["absent", "v1", "v2", "migrating"];
|
|
316
|
+
export const SCOPE_REGISTRY_SHAPES = Object.freeze(["absent", "v1", "v2", "migrating"]);
|
|
317
317
|
export const REGISTRY_SHAPE_DERIVES_PATHS = {
|
|
318
318
|
absent: true,
|
|
319
319
|
v1: false,
|
|
@@ -1657,7 +1657,7 @@ export function recordLineageCredential(controlDir, txnId, appliedIds, now) {
|
|
|
1657
1657
|
return { next: rec, result: undefined };
|
|
1658
1658
|
});
|
|
1659
1659
|
}
|
|
1660
|
-
export function promoteLineagePending(controlDir, txnId
|
|
1660
|
+
export function promoteLineagePending(controlDir, txnId) {
|
|
1661
1661
|
return lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
|
|
1662
1662
|
const txn = rec.pending[txnId];
|
|
1663
1663
|
if (txn === undefined)
|
|
@@ -1666,7 +1666,7 @@ export function promoteLineagePending(controlDir, txnId, now) {
|
|
|
1666
1666
|
throw new ControlPlaneCorruptError(`memory lineage ledger: refusing to promote uncredentialed pending transaction ${txnId} (the commit outcome is not durably known)`);
|
|
1667
1667
|
}
|
|
1668
1668
|
const applied = new Set(txn.credential.appliedIds);
|
|
1669
|
-
const at =
|
|
1669
|
+
const at = txn.credential.at;
|
|
1670
1670
|
const promoted = [];
|
|
1671
1671
|
for (const row of txn.rows) {
|
|
1672
1672
|
if (!applied.has(row.entryId))
|
|
@@ -1692,7 +1692,7 @@ export function adjudicateLineagePending(controlDir, txnId, action, now) {
|
|
|
1692
1692
|
return { next: rec, result: [] };
|
|
1693
1693
|
}
|
|
1694
1694
|
const applied = txn.credential !== undefined ? new Set(txn.credential.appliedIds) : undefined;
|
|
1695
|
-
const at = now();
|
|
1695
|
+
const at = txn.credential?.at ?? now();
|
|
1696
1696
|
const promoted = [];
|
|
1697
1697
|
for (const row of txn.rows) {
|
|
1698
1698
|
if (applied !== undefined && !applied.has(row.entryId))
|
|
@@ -1705,11 +1705,10 @@ export function adjudicateLineagePending(controlDir, txnId, action, now) {
|
|
|
1705
1705
|
return { next: rec, result: promoted };
|
|
1706
1706
|
});
|
|
1707
1707
|
}
|
|
1708
|
-
export function reconcileLineage(controlDir
|
|
1708
|
+
export function reconcileLineage(controlDir) {
|
|
1709
1709
|
return lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
|
|
1710
1710
|
const promoted = [];
|
|
1711
1711
|
const undecidable = [];
|
|
1712
|
-
let at;
|
|
1713
1712
|
let changed = false;
|
|
1714
1713
|
for (const [txnId, txn] of Object.entries(rec.pending)) {
|
|
1715
1714
|
if (txn.credential === undefined) {
|
|
@@ -1720,9 +1719,8 @@ export function reconcileLineage(controlDir, now) {
|
|
|
1720
1719
|
for (const row of txn.rows) {
|
|
1721
1720
|
if (!applied.has(row.entryId))
|
|
1722
1721
|
continue;
|
|
1723
|
-
at ??= now();
|
|
1724
1722
|
const sessions = (rec.committed[row.entryId] ??= Object.create(null));
|
|
1725
|
-
sessions[txn.sessionId] = { lastRev: row.rev, lastAt: at, ...(row.marked === true ? { marked: true } : {}), ...(txn.incarnation !== undefined ? { incarnation: txn.incarnation } : {}) };
|
|
1723
|
+
sessions[txn.sessionId] = { lastRev: row.rev, lastAt: txn.credential.at, ...(row.marked === true ? { marked: true } : {}), ...(txn.incarnation !== undefined ? { incarnation: txn.incarnation } : {}) };
|
|
1726
1724
|
promoted.push({ entryId: row.entryId, sessionId: txn.sessionId, rev: row.rev, ...(row.marked === true ? { marked: true } : {}) });
|
|
1727
1725
|
}
|
|
1728
1726
|
delete rec.pending[txnId];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A session's write home, as {@link import("./engine.js").MemoryEngine.sessionMemoryStatus} reports it
|
|
3
|
+
* (`SessionMemoryStatus.writeHome`): the canonical absolute directory of the write scope the session wrote under MOST
|
|
4
|
+
* RECENTLY — `canonicalize(scopeDirFor(write scope))`, the derivation behind a leg's handle's `writableRoot`. Two sources
|
|
5
|
+
* name a scope, each dated, and the NEWER one wins:
|
|
6
|
+
* 1. the session's account row: every account-opening mount (a sessioned, unrestricted mount with a write face) records
|
|
7
|
+
* its write scope there, dated by the account's (re-)open time — readable from any engine on the control plane;
|
|
8
|
+
* 2. the entry carrying the session's newest committed lineage contribution, dated by that contribution — a session that
|
|
9
|
+
* wrote without opening an account (provenance "off", a row written before the field) names its home this way, and so
|
|
10
|
+
* does a leg that wrote AFTER an older account leg (an off-mode commit under another scope after a carry leg ended:
|
|
11
|
+
* the older row must not hide the newer home).
|
|
12
|
+
* 3. neither — ABSENT: a session that never opened an account and never committed.
|
|
13
|
+
* A FAULTED source (a read that throws: an unreadable account ledger or lineage) answers absent and never falls through —
|
|
14
|
+
* it may hide the newer scope, so the other source's word would be a stand-in. A SILENT source (the newest contribution's
|
|
15
|
+
* entry is gone — deleted or a tombstone — or there is no account row) yields to the other source: a deleted note is an
|
|
16
|
+
* ordinary state, not a fault, and the account row is the durable record of where the session writes. Older contributions
|
|
17
|
+
* are never consulted. A registry that cannot derive a home (v1, an unfinished layout move)
|
|
18
|
+
* answers absent before either source is read, so no read here goes through a face whose layout seat would finish the move
|
|
19
|
+
* first and then name a post-move home.
|
|
20
|
+
*/
|
|
21
|
+
import type { MemoryEntry } from "./types.js";
|
|
22
|
+
export declare function sessionWriteHomeOf(input: {
|
|
23
|
+
memoryDir: string;
|
|
24
|
+
controlDir: string;
|
|
25
|
+
sessionId: string;
|
|
26
|
+
/** The caller's lineage read: entry id → this session's `lastAt` on it (undefined ⇔ that read faulted). */
|
|
27
|
+
contributions: ReadonlyMap<string, number> | undefined;
|
|
28
|
+
/** A committed, side-effect-free entry read (the engine's audit face). */
|
|
29
|
+
readEntries: (ids: readonly string[]) => Promise<MemoryEntry[]>;
|
|
30
|
+
}): Promise<string | undefined>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { canonicalize, REGISTRY_SHAPE_DERIVES_PATHS, scopeDirFor, scopeRegistryShape } from "./layout.js";
|
|
2
|
+
import { sessionAccountWriteHome } from "./delegation-settlement.js";
|
|
3
|
+
export async function sessionWriteHomeOf(input) {
|
|
4
|
+
const { memoryDir, controlDir, sessionId, contributions } = input;
|
|
5
|
+
try {
|
|
6
|
+
if (!REGISTRY_SHAPE_DERIVES_PATHS[scopeRegistryShape(controlDir)])
|
|
7
|
+
return undefined;
|
|
8
|
+
if (contributions === undefined)
|
|
9
|
+
return undefined;
|
|
10
|
+
let newest;
|
|
11
|
+
for (const [id, at] of contributions) {
|
|
12
|
+
if (newest === undefined || at > newest.at || (at === newest.at && id > newest.id))
|
|
13
|
+
newest = { id, at };
|
|
14
|
+
}
|
|
15
|
+
const account = sessionAccountWriteHome(controlDir, sessionId);
|
|
16
|
+
const sources = [];
|
|
17
|
+
const fromAccount = async () => account?.writeScope;
|
|
18
|
+
const fromNewest = async () => (newest === undefined ? undefined : (await input.readEntries([newest.id])).find((e) => e.id === newest.id)?.scope);
|
|
19
|
+
if (account !== undefined && (newest === undefined || account.openedAt >= newest.at))
|
|
20
|
+
sources.push(fromAccount, fromNewest);
|
|
21
|
+
else
|
|
22
|
+
sources.push(fromNewest, fromAccount);
|
|
23
|
+
let scope;
|
|
24
|
+
for (const source of sources) {
|
|
25
|
+
scope = await source();
|
|
26
|
+
if (scope !== undefined)
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
return scope === undefined ? undefined : canonicalize(scopeDirFor(memoryDir, controlDir, scope));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -11,7 +11,7 @@ import { toolFace } from "../tool-catalog-entries.js";
|
|
|
11
11
|
export const MEMORY_SEARCH_TOOL_NAME = "memory_search";
|
|
12
12
|
export const MEMORY_GET_TOOL_NAME = "memory_get";
|
|
13
13
|
export const MEMORY_INDEX_TOOL_NAME = "memory_index";
|
|
14
|
-
export const MEMORY_ENGINE_TOOL_NAMES = [MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_INDEX_TOOL_NAME];
|
|
14
|
+
export const MEMORY_ENGINE_TOOL_NAMES = Object.freeze([MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_INDEX_TOOL_NAME]);
|
|
15
15
|
export const MEMORY_SEARCH_DEFAULT_LIMIT = 8;
|
|
16
16
|
export const MEMORY_SEARCH_MAX_LIMIT = 20;
|
|
17
17
|
export const MEMORY_INDEX_PAGE_ROWS = 100;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const MEMORY_ORIGIN_CAUSES = ["observed", "derived", "static", "unattributed"];
|
|
1
|
+
export const MEMORY_ORIGIN_CAUSES = Object.freeze(["observed", "derived", "static", "unattributed"]);
|
|
2
2
|
export function writableRootOf(handle) {
|
|
3
3
|
if (handle.writableRoot === null)
|
|
4
4
|
throw new Error("memory session handle has no write face (writeScope is null) — writableRoot is not available");
|
package/dist/core/memory.js
CHANGED
|
@@ -363,7 +363,7 @@ export function parseNoteTimestamp(ts) {
|
|
|
363
363
|
const ms = Date.parse(`${ts.replace(" ", "T")}:00Z`);
|
|
364
364
|
return Number.isFinite(ms) ? { mtimeMs: ms } : { mtimeMs: 0, timestampMissing: true };
|
|
365
365
|
}
|
|
366
|
-
export const MEMORY_PROJECT_KEY_ABSENT_REASONS = ["not_git", "bare", "unreadable"];
|
|
366
|
+
export const MEMORY_PROJECT_KEY_ABSENT_REASONS = Object.freeze(["not_git", "bare", "unreadable"]);
|
|
367
367
|
export function normalizeMemorySpec(input) {
|
|
368
368
|
if (!input)
|
|
369
369
|
return undefined;
|
|
@@ -128,9 +128,12 @@ export interface PermissionModeArming {
|
|
|
128
128
|
* or a rejection from it is contained. */
|
|
129
129
|
readonly onArmFault?: (error: unknown) => void;
|
|
130
130
|
}
|
|
131
|
-
/** A live ceiling: the parent leg's own controller (`current` tracks the parent's steers), or a recorded word.
|
|
131
|
+
/** A live ceiling: the parent leg's own controller (`current` tracks the parent's steers), or a recorded word. A ceiling
|
|
132
|
+
* that can start something on a read (a controller, the engine's leg ceiling) also answers `observed` — the same word,
|
|
133
|
+
* read without starting anything; one without it is read through `current` on an observation too. */
|
|
132
134
|
export type PermissionModeCeiling = {
|
|
133
135
|
readonly current: PermissionMode;
|
|
136
|
+
readonly observed?: PermissionMode;
|
|
134
137
|
} | PermissionMode;
|
|
135
138
|
/**
|
|
136
139
|
* The one resolution of a leg's effective mode. `current` is derived on every read from three inputs —
|
|
@@ -170,8 +173,14 @@ export declare class PermissionModeController {
|
|
|
170
173
|
* failure goes to {@link PermissionModeArming.onArmFault}; the leg then has no classifier, so its asks go on to the
|
|
171
174
|
* approval seat as under `default`. */
|
|
172
175
|
get current(): PermissionMode;
|
|
176
|
+
/** The effective mode read to REPORT it (a hook context), not to decide with it: the word {@link current} answers,
|
|
177
|
+
* reached through the ceilings' own `observed`, so neither this leg nor an ancestor starts its classifier's arming.
|
|
178
|
+
* A decision read is what starts it; an observation that did would arm a leg earlier than its own gate, and so move
|
|
179
|
+
* who answers the leg's next ask. */
|
|
180
|
+
get observed(): PermissionMode;
|
|
173
181
|
/** The three-member answer as it stands right now. */
|
|
174
182
|
receipt(): PermissionModeReceipt;
|
|
183
|
+
private receiptOf;
|
|
175
184
|
/** The ceiling's source moved to another leg (the parent session's next leg); `before` is this leg's receipt under the
|
|
176
185
|
* previous one. A ceiling fold the move produced — or changed — is announced, as the construction-time fold is. */
|
|
177
186
|
ceilingRebound(before: PermissionModeReceipt): void;
|
package/dist/core/mode-policy.js
CHANGED
|
@@ -6,8 +6,8 @@ import { clampPermissionMode } from "./ask-origin.js";
|
|
|
6
6
|
import { argvOf, lexShellText } from "../shell/lex.js";
|
|
7
7
|
import { COMMAND_RULE_TOOL } from "./permission-rule-model.js";
|
|
8
8
|
import { createSafeNotifier } from "./safe-notify.js";
|
|
9
|
-
export const PERMISSION_MODE_FOLD_REASONS = ["operator_deny", "face_absent", "inherited_ceiling"];
|
|
10
|
-
export const ACCEPT_EDITS_FILE_COMMANDS = ["mkdir", "touch", "rm", "rmdir", "mv", "cp", "sed"];
|
|
9
|
+
export const PERMISSION_MODE_FOLD_REASONS = Object.freeze(["operator_deny", "face_absent", "inherited_ceiling"]);
|
|
10
|
+
export const ACCEPT_EDITS_FILE_COMMANDS = Object.freeze(["mkdir", "touch", "rm", "rmdir", "mv", "cp", "sed"]);
|
|
11
11
|
const MODE_POLICY_TABLE = {
|
|
12
12
|
plan: { enablePlanMode: true, writeGate: "plan", fileCommands: [], writeFace: undefined },
|
|
13
13
|
acceptEdits: { enablePlanMode: false, writeGate: "accept_root", fileCommands: ACCEPT_EDITS_FILE_COMMANDS, writeFace: undefined },
|
|
@@ -94,14 +94,16 @@ export class PermissionModeController {
|
|
|
94
94
|
get requested() {
|
|
95
95
|
return this.steers.at(-1)?.word ?? this.requestedBySpec;
|
|
96
96
|
}
|
|
97
|
-
ceilingNow() {
|
|
98
|
-
|
|
97
|
+
ceilingNow(observe) {
|
|
98
|
+
if (this.ceiling === undefined || typeof this.ceiling === "string")
|
|
99
|
+
return this.ceiling;
|
|
100
|
+
return observe ? (this.ceiling.observed ?? this.ceiling.current) : this.ceiling.current;
|
|
99
101
|
}
|
|
100
102
|
get declared() {
|
|
101
103
|
return this.declaration().effective;
|
|
102
104
|
}
|
|
103
|
-
declaration() {
|
|
104
|
-
const ceiling = this.ceilingNow();
|
|
105
|
+
declaration(observe = false) {
|
|
106
|
+
const ceiling = this.ceilingNow(observe);
|
|
105
107
|
if (ceiling === undefined)
|
|
106
108
|
return { effective: this.requested ?? "default", clamped: false };
|
|
107
109
|
return clampPermissionMode(this.requested, ceiling);
|
|
@@ -117,8 +119,14 @@ export class PermissionModeController {
|
|
|
117
119
|
}
|
|
118
120
|
return effective;
|
|
119
121
|
}
|
|
122
|
+
get observed() {
|
|
123
|
+
return this.receiptOf(true).effective;
|
|
124
|
+
}
|
|
120
125
|
receipt() {
|
|
121
|
-
|
|
126
|
+
return this.receiptOf(false);
|
|
127
|
+
}
|
|
128
|
+
receiptOf(observe) {
|
|
129
|
+
const { effective: declared, clamped } = this.declaration(observe);
|
|
122
130
|
const requested = this.requested;
|
|
123
131
|
const base = requested !== undefined ? { requested } : {};
|
|
124
132
|
const a = this.arming;
|
package/dist/core/model-seat.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const MODEL_ROLES = ["default", "summarize", "subagent", "team", "synthesize", "advisor", "verifier", "classifier", "consolidate"];
|
|
1
|
+
export const MODEL_ROLES = Object.freeze(["default", "summarize", "subagent", "team", "synthesize", "advisor", "verifier", "classifier", "consolidate"]);
|
|
@@ -8,7 +8,7 @@ import { heldValues } from "./tool-name-readings.js";
|
|
|
8
8
|
import { carriesDotSegment, isAbsoluteForFamily, isIdentityDir, isPathIdentity, isShellRootedSpellingUnmapped, isUncIdentity, pathFamilyOf, pathIdentityOf } from "../tools/fs/safety.js";
|
|
9
9
|
import { readShellCommand } from "./shell-lexer.js";
|
|
10
10
|
import { readOnlyLineObjection, readOnlyShellVerdict } from "./read-only-shell.js";
|
|
11
|
-
export const RULE_BEHAVIORS = ["deny", "ask", "allow"];
|
|
11
|
+
export const RULE_BEHAVIORS = Object.freeze(["deny", "ask", "allow"]);
|
|
12
12
|
const RULE_BEHAVIOR_SET = new Set(RULE_BEHAVIORS);
|
|
13
13
|
export function isRuleBehavior(v) {
|
|
14
14
|
return RULE_BEHAVIOR_SET.has(v);
|
|
@@ -342,7 +342,7 @@ function parsePathRuleContent(text, tool, content, behavior) {
|
|
|
342
342
|
}
|
|
343
343
|
return { rule: { behavior, rule: formatRuleText(content, "path", tool), tool, match: "path", command: content } };
|
|
344
344
|
}
|
|
345
|
-
export const PATH_RULE_BASES = ["cwd", "root", "home"];
|
|
345
|
+
export const PATH_RULE_BASES = Object.freeze(["cwd", "root", "home"]);
|
|
346
346
|
export const PATH_RULE_BASE_LABEL = {
|
|
347
347
|
cwd: "the working directory",
|
|
348
348
|
root: "the project root",
|
|
@@ -493,7 +493,7 @@ function hasUnescapedStar(s) {
|
|
|
493
493
|
export function ruleAdmitsCommand(rule, command) {
|
|
494
494
|
return admitsUnder(rule, command, MATCH_READING);
|
|
495
495
|
}
|
|
496
|
-
export const PROGRAM_RUN_REACHES = ["reached", "not_reached", "unreadable"];
|
|
496
|
+
export const PROGRAM_RUN_REACHES = Object.freeze(["reached", "not_reached", "unreadable"]);
|
|
497
497
|
const REACHED = { reach: "reached" };
|
|
498
498
|
const NOT_REACHED = { reach: "not_reached" };
|
|
499
499
|
export function programRunReachOf(rule, command) {
|
|
@@ -462,8 +462,8 @@ export async function ruleStoreChecksum(payload) {
|
|
|
462
462
|
const { createHash } = await import("node:crypto");
|
|
463
463
|
return `sha256:${createHash("sha256").update(canonicalize(payload), "utf8").digest("hex")}`;
|
|
464
464
|
}
|
|
465
|
-
export const REMOVAL_LIVENESS_WORDS = ["yes", "no", "unknown"];
|
|
466
|
-
export const RULE_WRITE_COMMITTED_WORDS = ["no", "unknown"];
|
|
465
|
+
export const REMOVAL_LIVENESS_WORDS = Object.freeze(["yes", "no", "unknown"]);
|
|
466
|
+
export const RULE_WRITE_COMMITTED_WORDS = Object.freeze(["no", "unknown"]);
|
|
467
467
|
function resolveRuleWriter(store) {
|
|
468
468
|
let w;
|
|
469
469
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const READ_ONLY_FLAG_ARITIES = ["none", "number", "string", "char", "{}", "EOF"];
|
|
1
|
+
export const READ_ONLY_FLAG_ARITIES = Object.freeze(["none", "number", "string", "char", "{}", "EOF"]);
|
|
2
2
|
export const FLAG_VALUE_ACCEPTS = {
|
|
3
3
|
none: () => false,
|
|
4
4
|
number: (value) => /^\d+$/.test(value),
|
|
@@ -474,12 +474,12 @@ export const READ_ONLY_COMMAND_TABLE = {
|
|
|
474
474
|
},
|
|
475
475
|
},
|
|
476
476
|
};
|
|
477
|
-
export const READ_ONLY_BARE_PROGRAMS = ["docker ps", "docker images", "cal", "uptime", "cat", "head", "tail", "wc", "stat", "strings", "hexdump", "od", "nl", "id", "uname", "free", "df", "du", "locale", "groups", "nproc", "basename", "dirname", "realpath", "cut", "paste", "tr", "column", "tac", "rev", "fold", "expand", "unexpand", "fmt", "comm", "cmp", "numfmt", "readlink", "diff", "true", "false", "sleep", "which", "type", "expr", "seq", "tsort", "pr"];
|
|
478
|
-
export const READ_ONLY_GLOB_PROGRAMS = ["ls", "cat", "head", "tail", "wc", "stat", "grep", "egrep", "fgrep", "diff", "du", "df", "echo", "strings", "hexdump", "od", "nl", "cut", "column", "tr", "tac", "rev", "cmp", "basename", "dirname", "realpath", "readlink", "sha256sum", "sha1sum", "md5sum", "cd"];
|
|
477
|
+
export const READ_ONLY_BARE_PROGRAMS = Object.freeze(["docker ps", "docker images", "cal", "uptime", "cat", "head", "tail", "wc", "stat", "strings", "hexdump", "od", "nl", "id", "uname", "free", "df", "du", "locale", "groups", "nproc", "basename", "dirname", "realpath", "cut", "paste", "tr", "column", "tac", "rev", "fold", "expand", "unexpand", "fmt", "comm", "cmp", "numfmt", "readlink", "diff", "true", "false", "sleep", "which", "type", "expr", "seq", "tsort", "pr"]);
|
|
478
|
+
export const READ_ONLY_GLOB_PROGRAMS = Object.freeze(["ls", "cat", "head", "tail", "wc", "stat", "grep", "egrep", "fgrep", "diff", "du", "df", "echo", "strings", "hexdump", "od", "nl", "cut", "column", "tr", "tac", "rev", "cmp", "basename", "dirname", "realpath", "readlink", "sha256sum", "sha1sum", "md5sum", "cd"]);
|
|
479
479
|
export const READ_ONLY_EXACT_FORMS = [["claude", "-h"], ["claude", "--help"], ["node", "-v"], ["node", "--version"], ["python", "--version"], ["python3", "--version"], ["ip", "addr"]];
|
|
480
|
-
export const READ_ONLY_BARE_ONLY = ["pwd", "whoami", "alias"];
|
|
481
|
-
export const FIND_ACTION_PRIMARIES = ["-delete", "-exec", "-execdir", "-ok", "-okdir", "-fprint", "-fprint0", "-fls", "-fprintf", "-files0-from"];
|
|
482
|
-
export const FIND_VALUE_PRIMARIES = ["-name", "-iname", "-path", "-ipath", "-lname", "-ilname", "-regex", "-iregex", "-wholename", "-iwholename", "-samefile", "-newer", "-anewer", "-cnewer", "-mnewer", "-perm", "-user", "-group", "-uid", "-gid", "-size", "-type", "-xtype", "-fstype", "-inum", "-links", "-used", "-context", "-amin", "-cmin", "-mmin", "-atime", "-ctime", "-mtime", "-mindepth", "-maxdepth", "-printf", "-regextype", "-D", "-f", "-flags", "-Bnewer", "-Btime", "-Bmin", "-files0-from", "-xattrname"];
|
|
480
|
+
export const READ_ONLY_BARE_ONLY = Object.freeze(["pwd", "whoami", "alias"]);
|
|
481
|
+
export const FIND_ACTION_PRIMARIES = Object.freeze(["-delete", "-exec", "-execdir", "-ok", "-okdir", "-fprint", "-fprint0", "-fls", "-fprintf", "-files0-from"]);
|
|
482
|
+
export const FIND_VALUE_PRIMARIES = Object.freeze(["-name", "-iname", "-path", "-ipath", "-lname", "-ilname", "-regex", "-iregex", "-wholename", "-iwholename", "-samefile", "-newer", "-anewer", "-cnewer", "-mnewer", "-perm", "-user", "-group", "-uid", "-gid", "-size", "-type", "-xtype", "-fstype", "-inum", "-links", "-used", "-context", "-amin", "-cmin", "-mmin", "-atime", "-ctime", "-mtime", "-mindepth", "-maxdepth", "-printf", "-regextype", "-D", "-f", "-flags", "-Bnewer", "-Btime", "-Bmin", "-files0-from", "-xattrname"]);
|
|
483
483
|
export const FIND_NEWER_PRIMARY = /^-newer[aBcm][aBcmt]$/;
|
|
484
|
-
export const READ_ONLY_ENV_NAMES = ["GOEXPERIMENT", "GOOS", "GOARCH", "CGO_ENABLED", "GO111MODULE", "RUST_BACKTRACE", "RUST_LOG", "NODE_ENV", "PYTHONUNBUFFERED", "PYTHONDONTWRITEBYTECODE", "PYTEST_DISABLE_PLUGIN_AUTOLOAD", "PYTEST_DEBUG", "ANTHROPIC_API_KEY", "LANG", "LANGUAGE", "LC_ALL", "LC_CTYPE", "LC_TIME", "CHARSET", "TERM", "COLORTERM", "NO_COLOR", "FORCE_COLOR", "TZ", "LS_COLORS", "LSCOLORS", "GREP_COLOR", "GREP_COLORS", "GCC_COLORS", "TIME_STYLE", "BLOCK_SIZE", "BLOCKSIZE", "COLUMNS", "LINES", "CLICOLOR", "CLICOLOR_FORCE", "CI", "DEBIAN_FRONTEND", "GIT_TERMINAL_PROMPT"];
|
|
485
|
-
export const XARGS_READ_ONLY_TARGETS = ["echo", "printf", "wc", "grep", "egrep", "fgrep", "head", "tail"];
|
|
484
|
+
export const READ_ONLY_ENV_NAMES = Object.freeze(["GOEXPERIMENT", "GOOS", "GOARCH", "CGO_ENABLED", "GO111MODULE", "RUST_BACKTRACE", "RUST_LOG", "NODE_ENV", "PYTHONUNBUFFERED", "PYTHONDONTWRITEBYTECODE", "PYTEST_DISABLE_PLUGIN_AUTOLOAD", "PYTEST_DEBUG", "ANTHROPIC_API_KEY", "LANG", "LANGUAGE", "LC_ALL", "LC_CTYPE", "LC_TIME", "CHARSET", "TERM", "COLORTERM", "NO_COLOR", "FORCE_COLOR", "TZ", "LS_COLORS", "LSCOLORS", "GREP_COLOR", "GREP_COLORS", "GCC_COLORS", "TIME_STYLE", "BLOCK_SIZE", "BLOCKSIZE", "COLUMNS", "LINES", "CLICOLOR", "CLICOLOR_FORCE", "CI", "DEBIAN_FRONTEND", "GIT_TERMINAL_PROMPT"]);
|
|
485
|
+
export const XARGS_READ_ONLY_TARGETS = Object.freeze(["echo", "printf", "wc", "grep", "egrep", "fgrep", "head", "tail"]);
|
package/dist/core/retention.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export const RETENTION_CASCADE_DOMAINS = ["checkpoints", "session-trees", "orphan-tool-results"];
|
|
2
|
-
export const RETENTION_CASCADE_EXEMPT = ["session-pollution", "session-capture-optout", "lineage.json"];
|
|
1
|
+
export const RETENTION_CASCADE_DOMAINS = Object.freeze(["checkpoints", "session-trees", "orphan-tool-results"]);
|
|
2
|
+
export const RETENTION_CASCADE_EXEMPT = Object.freeze(["session-pollution", "session-capture-optout", "lineage.json"]);
|
|
3
3
|
function codedError(code, message) {
|
|
4
4
|
const e = new Error(message);
|
|
5
5
|
e.code = code;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
2
|
-
import { extractErrorCode, isProviderEchoShape, stripErrorCodePrefix } from "../../brain/errors.js";
|
|
2
|
+
import { extractErrorCode, isProviderEchoShape, refusedTheRequest, stripErrorCodePrefix } from "../../brain/errors.js";
|
|
3
3
|
import { terminalProjection } from "./terminal-projection.js";
|
|
4
4
|
import { haltConsumed } from "./halt-attribution.js";
|
|
5
5
|
import { providerReportedModel } from "../message-utils.js";
|
|
@@ -124,12 +124,17 @@ export function terminalCauseOf(final, stats, flags) {
|
|
|
124
124
|
else if (final.stopReason === "error") {
|
|
125
125
|
let message = final.errorMessage ?? "model error";
|
|
126
126
|
let code;
|
|
127
|
-
|
|
127
|
+
const apiError = readMember(final, "isApiErrorMessage") === true;
|
|
128
|
+
const apiStatus = readMember(final, "apiErrorStatus");
|
|
129
|
+
const apiRequestId = readMember(final, "requestId");
|
|
130
|
+
const apiCode = readMember(final, "apiErrorCode");
|
|
131
|
+
if (apiError) {
|
|
128
132
|
apiFailure = {
|
|
129
|
-
...(
|
|
130
|
-
...(typeof
|
|
133
|
+
...(apiStatus !== undefined ? { status: apiStatus } : {}),
|
|
134
|
+
...(typeof apiRequestId === "string" && isProviderEchoShape(apiRequestId) ? { requestId: apiRequestId } : {}),
|
|
131
135
|
};
|
|
132
136
|
}
|
|
137
|
+
let providerNamed = true;
|
|
133
138
|
const lifted = extractErrorCode(message);
|
|
134
139
|
if (lifted) {
|
|
135
140
|
code = lifted;
|
|
@@ -137,14 +142,19 @@ export function terminalCauseOf(final, stats, flags) {
|
|
|
137
142
|
}
|
|
138
143
|
else if (flags.gitCoreOverBudget) {
|
|
139
144
|
code = "irreducible_core_over_budget";
|
|
145
|
+
providerNamed = false;
|
|
140
146
|
}
|
|
141
147
|
else if (flags.conflict) {
|
|
142
148
|
code = "conflict";
|
|
149
|
+
providerNamed = false;
|
|
143
150
|
}
|
|
144
151
|
else {
|
|
145
152
|
code = "provider.error";
|
|
146
153
|
}
|
|
147
|
-
|
|
154
|
+
const detail = apiError && providerNamed && typeof apiStatus === "number" && refusedTheRequest(apiStatus)
|
|
155
|
+
? { kind: "provider_rejected", status: apiStatus, ...(typeof apiCode === "string" && isProviderEchoShape(apiCode) ? { code: apiCode } : {}) }
|
|
156
|
+
: undefined;
|
|
157
|
+
terminal = { kind: "failed", code, message, ...(detail !== undefined ? { detail } : {}) };
|
|
148
158
|
}
|
|
149
159
|
else {
|
|
150
160
|
terminal = { kind: "completed" };
|
|
@@ -18,7 +18,7 @@ function sameRouteQuietly(a, b) {
|
|
|
18
18
|
return false;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
export const CLASSIFIER_ROLE_DEFAULT_REASONS = ["tier_below_main", "tier_table", "role_fallback", "main_only", "role_unresolvable"];
|
|
21
|
+
export const CLASSIFIER_ROLE_DEFAULT_REASONS = Object.freeze(["tier_below_main", "tier_table", "role_fallback", "main_only", "role_unresolvable"]);
|
|
22
22
|
export function classifierSeatModel(args) {
|
|
23
23
|
const { main } = args;
|
|
24
24
|
const deps = normalizeSeatDeps(args.deps);
|
|
@@ -188,6 +188,8 @@ export type InheritedClassifierJudgment<A extends InheritedAsk> =
|
|
|
188
188
|
* vouches for no name and the seat stays empty (absence = unknown); a block AT the bound reaches no seat at
|
|
189
189
|
* all, since it becomes an ask and the disposition then names that resolution.
|
|
190
190
|
*/
|
|
191
|
+
/** The provenance sentence an inherited card carries when the ancestor's ask brought no words of its own — the one spelling the request mint and the frame join share. */
|
|
192
|
+
export declare const inheritedAskFallbackMessage: (toolName: string) => string;
|
|
191
193
|
export declare function judgeInheritedClassifier<A extends InheritedAsk>(opts: {
|
|
192
194
|
autoMode: InheritedAutoMode | undefined;
|
|
193
195
|
/** The leg's live permission mode (the gate's seat): under `dontAsk` / `bypassPermissions` / `plan` the decider is not
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { markTightenMessage } from "../safety-axis-vocab.js";
|
|
1
2
|
import { classifierParseFailureDenyMessage, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow } from "../auto-mode.js";
|
|
2
3
|
import { sanitizeAutoModeArmingRecipe, tightenDenialLimit } from "../auto-mode-arming.js";
|
|
3
4
|
import { classifierUnavailableArm } from "../classifier-unavailable-arm.js";
|
|
@@ -79,9 +80,12 @@ export function inheritedAskCarry(judged, liveApprover, tracker) {
|
|
|
79
80
|
...fallbackCarry(judged.fallback, liveApprover, tracker),
|
|
80
81
|
};
|
|
81
82
|
}
|
|
83
|
+
export const inheritedAskFallbackMessage = (toolName) => `approval required for "${toolName}" (inherited parent policy)`;
|
|
82
84
|
export async function judgeInheritedClassifier(opts) {
|
|
83
|
-
const { autoMode,
|
|
84
|
-
const facts = opts.facts ?? INHERITED_STATION_FACTS;
|
|
85
|
+
const { autoMode, req } = opts;
|
|
86
|
+
const facts = opts.facts ?? INHERITED_STATION_FACTS, m = facts.mandateMarks;
|
|
87
|
+
const frames = m.shellGated === true ? [] : [...(m.egress === true ? [markTightenMessage(req.toolName, "egress")] : []), ...(m.irreversibility === "always" ? [markTightenMessage(req.toolName, "irreversible")] : [])];
|
|
88
|
+
const ask = (frames.length > 0 ? { ...opts.ask, message: [opts.ask.message ?? inheritedAskFallbackMessage(req.toolName), ...frames].join(" — ") } : opts.ask);
|
|
85
89
|
const origin = askOriginOf(ask, facts);
|
|
86
90
|
const classification = classifyAsk(ask, facts);
|
|
87
91
|
const classifierStandsDown = () => {
|
|
@@ -20,10 +20,8 @@ export declare function askGrantCovers(granted: readonly AskClass[], asked: read
|
|
|
20
20
|
export declare function askGrantShapeOf(args: unknown): string | undefined;
|
|
21
21
|
/**
|
|
22
22
|
* Duplicate-frame collapse, the CONSUME half: an inherited layer already resolved THIS call as a clean allow at the
|
|
23
|
-
* SAME approver function, on EXACTLY the byte-shape this
|
|
24
|
-
*
|
|
25
|
-
* · SOURCE: only a caller-slot POLICY ask may reuse — a safety tighten (`decisionReason:"safety"`), a hook-promoted
|
|
26
|
-
* ask ("hook") or a classifier ask carries a DIFFERENT authority and keeps its frame.
|
|
23
|
+
* SAME approver function, on EXACTLY the byte-shape this ask now presents, for a question that covers this one — asking
|
|
24
|
+
* again is the 2-frame UX debt, not a second authority. Guards, all mismatches re-ask (⇒ `undefined`):
|
|
27
25
|
* · IDENTITY: the approver FUNCTION reference (never a name), compared through `askApproverIdentity`: a delegated
|
|
28
26
|
* child's approver is the ancestor's own wrapped with provenance — a different function object, the same operator
|
|
29
27
|
* (the wrapper grants no authority of its own); identity answers only "same consultation".
|
|
@@ -31,7 +29,10 @@ export declare function askGrantShapeOf(args: unknown): string | undefined;
|
|
|
31
29
|
* `presentedInput` — the same binding as a main-gate approval (shown == executed, by construction).
|
|
32
30
|
* · CLASS: the gate's stamped `classes` is covered by the set the person answered ({@link askGrantCovers}); a class
|
|
33
31
|
* the gate's own judges add (a protected target, an out-of-root read) was not on the card. Not covered ⇒ the gate
|
|
34
|
-
* asks its own question and the grant stays (it answers what it answered; the call-site sweep ends it).
|
|
32
|
+
* asks its own question and the grant stays (it answers what it answered; the call-site sweep ends it). The class
|
|
33
|
+
* set is also the AUTHORITY test (a hook's ask carries `hook`, a fallback `denial_limit_fallback`, a marked tool
|
|
34
|
+
* `operator_forced`, a protected write `write_protection`, an out-of-root read `probe`; the shell seat's classify-tier
|
|
35
|
+
* hesitation is `ordinary`, the one question a yes on the same bytes answers) — the ask's `decisionReason` is not read.
|
|
35
36
|
* · LEDGER: the ONE real consultation (timed on the wrapper arm) is booked here — one frame happened, one is booked.
|
|
36
37
|
* The grant lives within one gate evaluation (consumed on reuse, swept at the call site): a REOPENED checkpoint's
|
|
37
38
|
* re-adjudication never sees a stale grant.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { askMandateMember } from "../ask-origin.js";
|
|
1
2
|
import { primaryActivityArg } from "../arg-summary.js";
|
|
2
3
|
import { askApproverIdentity, carryEngineAttestations } from "../tool-policy.js";
|
|
3
4
|
export function askGrantCovers(granted, asked) {
|
|
@@ -12,13 +13,13 @@ export function askGrantShapeOf(args) {
|
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
export function consumeInheritedAskGrant(grants, onAsk, humanReviewRef, decision, req) {
|
|
15
|
-
if (decision.decisionReason !== undefined && decision.decisionReason !== "rule")
|
|
16
|
-
return undefined;
|
|
17
16
|
const grant = grants.get(req.toolCallId);
|
|
18
17
|
if (grant === undefined || askApproverIdentity(grant.approver) !== askApproverIdentity(onAsk) || grant.argsJson !== askGrantShapeOf(req.args))
|
|
19
18
|
return undefined;
|
|
20
19
|
if (!askGrantCovers(grant.classes, decision.action === "ask" ? decision.classes : undefined))
|
|
21
20
|
return undefined;
|
|
21
|
+
if (decision.action === "ask" && askMandateMember(decision).mandate === "operator_always")
|
|
22
|
+
return undefined;
|
|
22
23
|
grants.delete(req.toolCallId);
|
|
23
24
|
humanReviewRef.count += 1;
|
|
24
25
|
humanReviewRef.totalWaitMs += grant.waitMs;
|