@sema-agent/core 5.33.0 → 5.35.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.
@@ -118,6 +118,34 @@ export type ApprovalSettledBy = "human" | "timeout" | "aborted";
118
118
  export declare const APPROVAL_SETTLED_BY_VALUES: readonly ApprovalSettledBy[];
119
119
  /** True iff `v` is one of the three {@link ApprovalSettledBy} words. */
120
120
  export declare function isApprovalSettledBy(v: unknown): v is ApprovalSettledBy;
121
+ /**
122
+ * design/252 G-7 — how long an approver-attribution identifier may be.
123
+ *
124
+ * Sized for the identifiers approval channels actually carry (a login, an email, an opaque account id,
125
+ * a queue name) with room to spare, and bounded at all because this value lands on an operator-plane
126
+ * frame that a deployment may persist: an unbounded field on a record channel is its own denial of a
127
+ * readable record. An over-long value is REFUSED, never truncated — a cut identifier names a different
128
+ * party, or nobody, and either is worse than the honest refusal.
129
+ */
130
+ export declare const APPROVER_ATTRIBUTION_MAX_CHARS = 256;
131
+ /**
132
+ * design/252 G-7 — screen a deployment-supplied approver identifier before it becomes an observation.
133
+ *
134
+ * THE POSTURE, stated so it is not mistaken for something stronger: core does not authenticate this
135
+ * value, does not compare it to anything, and never reads it back to decide anything. It is a
136
+ * TRANSCRIPTION of what the approval channel said about its own settlement — the channel (a server's
137
+ * approval card, an HMAC-verified callback, an operator console) is where identity is established, and
138
+ * a library that holds no identity surface cannot second-guess it. What core owns is that the value is
139
+ * a value: a string, bounded, free of control bytes, or else loudly refused.
140
+ *
141
+ * Returns `{}` for "nothing supplied" (absent, and an empty string — an id of no characters is the
142
+ * absence of an id, the same truthiness fold the deny-note seat uses), `{ approver }` for a value to
143
+ * carry, or `{ defect }` with the sentence a caller puts in its own refusal.
144
+ */
145
+ export declare function screenApproverAttribution(v: unknown): {
146
+ approver?: string;
147
+ defect?: string;
148
+ };
121
149
  /**
122
150
  * A three-state permission decision for a tool call (design/37). Upgrades the old two-state
123
151
  * `{allow|deny}`:
@@ -162,6 +190,7 @@ export type PermissionResult = {
162
190
  message?: string;
163
191
  decisionReason?: DecisionReason;
164
192
  settledBy?: Extract<ApprovalSettledBy, "human">;
193
+ approver?: string;
165
194
  } | {
166
195
  action: "ask";
167
196
  updatedInput?: unknown;
@@ -199,13 +228,86 @@ export type PermissionResult = {
199
228
  * when ANY folded-away concurrent ask bore it (monotone, tighten-only); it is consumed inside the
200
229
  * gate and deliberately NOT copied onto the park/approval-card request. */
201
230
  matchedAskRule?: string;
231
+ /** design/252 G-2 (additive): the RULE-PROVENANCE evidence behind this ask — see
232
+ * {@link AskRuleEvidence}. ENGINE-STAMPED inside the gate, once, after the org layer and the
233
+ * persisted-rule lane have both spoken; a policy that self-declares it is overwritten there
234
+ * (the member is a record of what the ENGINE's own governance layers did, so a layer's claim
235
+ * about itself is not evidence). Carried onto the approval request by the gate's own ask mint
236
+ * site. Display/reconciliation metadata, never adjudication input. */
237
+ ruleEvidence?: AskRuleEvidence;
202
238
  } | {
203
239
  action: "deny";
204
240
  updatedInput?: unknown;
205
241
  message?: string;
206
242
  decisionReason?: DecisionReason;
207
243
  settledBy?: ApprovalSettledBy;
244
+ approver?: string;
208
245
  };
246
+ /**
247
+ * design/252 G-2 — WHY a piece of rule-provenance evidence is not on an ask.
248
+ *
249
+ * The vocabulary exists because a bare `undefined` reads the same for facts that are opposite: "no
250
+ * governance layer is wired here, so there is nothing to name" and "the layer ran and we could not
251
+ * read what it said" are not the same audit answer, and collapsing them is how an evidence chain
252
+ * comes to be reconstructed as "nothing governed this call". Every member of {@link AskRuleEvidence}
253
+ * therefore ships as a value OR a named absence, never as silence.
254
+ *
255
+ * - `"not_wired"` — the lane does not exist on this leg (an ungoverned deployment, no rule store).
256
+ * The field cannot apply; nothing was lost.
257
+ * - `"not_adjudicated"` — the lane exists but this call never reached it (an exempt tool, a call
258
+ * the rule grammar cannot describe). Applicable in principle, skipped in fact.
259
+ * - `"unavailable"` — the lane was consulted and could not read its source. The evidence is LOST,
260
+ * not absent, and this is the one member of the vocabulary that means an auditor should treat the
261
+ * chain as broken rather than empty.
262
+ * - `"no_match"` — consulted, readable, and nothing spoke for this call. A real negative answer.
263
+ * - `"not_reported"` — the supplying seam answered without the identity (a foreign overlay/store
264
+ * implementation, or one written before the identity was projected). Evidence lost at the seam.
265
+ */
266
+ export type AskEvidenceAbsence = "not_wired" | "not_adjudicated" | "unavailable" | "no_match" | "not_reported";
267
+ /** The closed set above, for runtime domain checks at the seams that accept a caller-supplied value. */
268
+ export declare const ASK_EVIDENCE_ABSENCE_VALUES: readonly AskEvidenceAbsence[];
269
+ /**
270
+ * design/252 G-2 — the machine-reconcilable provenance of the governance decision behind ONE ask.
271
+ *
272
+ * WHAT THIS IS: a PROJECTION of identity keys the engine's governance layers already hold — the org
273
+ * snapshot's `revision`, the personal rule's add `dot`s — onto the surface a consumer can actually
274
+ * read. It performs no new judgment and changes no verdict; removing it would leave every decision
275
+ * byte-identical. The prose channels ({@link PermissionResult}'s ask `message`,
276
+ * {@link AskRequest.persistedRuleShadowed}) say the same things to a PERSON; those are sanitized,
277
+ * capped, display-shaped strings, and reconciling a decision against a published policy revision by
278
+ * regexing them is not an audit trail. This member is the machine's copy.
279
+ *
280
+ * WHAT THIS IS NOT: an authority channel. Nothing in the engine reads it back to decide anything, and
281
+ * a host that ignores it entirely is governed exactly as before.
282
+ *
283
+ * ABSENCE DISCIPLINE (the reason each member has a `…Absent` twin): see {@link AskEvidenceAbsence}.
284
+ * Exactly one of each pair is present — a member and its absence reason are never both set, and never
285
+ * both missing, on evidence the engine stamped.
286
+ */
287
+ export interface AskRuleEvidence {
288
+ /** The org snapshot `revision` this call was adjudicated against — the published-policy version an
289
+ * auditor reconciles the decision against. Absent ⇒ {@link orgRevisionAbsent} names why. */
290
+ readonly orgRevision?: number;
291
+ /** Present iff {@link orgRevision} is not. `"unavailable"` here is the load-bearing one: the org
292
+ * layer spoke, its answer was "this deployment cannot see the organization's rules", and the ask
293
+ * in hand is the fail-closed tighten that followed — not an ask any published rule asked for. */
294
+ readonly orgRevisionAbsent?: AskEvidenceAbsence;
295
+ /** The org rule that spoke for this call, verbatim as published (org rules are administrator
296
+ * authored and are relayed unmodified — the prose channel's copy is the same text). Absent ⇒
297
+ * {@link orgRuleAbsent} names why. */
298
+ readonly orgRule?: string;
299
+ /** Present iff {@link orgRule} is not. */
300
+ readonly orgRuleAbsent?: AskEvidenceAbsence;
301
+ /** The add dots of the PERSONAL allow rule that matched this call but did not clear the ask (the
302
+ * #144 shadowed arm). The dots are the rule's durable identity — unlike
303
+ * {@link AskRequest.persistedRuleShadowed}, which is a sanitized, length-capped DISPLAY value and
304
+ * deliberately not an identity channel. A rule is a set of adds (concurrent approvals on one text
305
+ * each redeem their own dot), so this is an array by construction: render/reconcile the entries as
306
+ * data, never re-derive one scalar id by joining them. Absent ⇒ {@link personalRuleDotsAbsent}. */
307
+ readonly personalRuleDots?: readonly import("./permission-rule-model.js").RuleDot[];
308
+ /** Present iff {@link personalRuleDots} is not. */
309
+ readonly personalRuleDotsAbsent?: AskEvidenceAbsence;
310
+ }
209
311
  /** The human/model-readable text of a decision. */
210
312
  export declare function decisionText(d: PermissionResult): string | undefined;
211
313
  /**
@@ -682,6 +784,15 @@ export interface AskRequest {
682
784
  * structure: render the entries as data, never re-derive structure by splitting or joining them.
683
785
  * The durable park route carries the same value as `RiskDescriptor.probeCause`. */
684
786
  readonly probeCause?: import("./checkpoint-store.js").ProbeCause;
787
+ /** design/252 G-2: the RULE-PROVENANCE evidence behind this ask — the org snapshot revision and the
788
+ * matched rules' own identity keys, each present as a value or as a named absence (see
789
+ * {@link AskRuleEvidence}). Present on EVERY ask the engine mints, so a consumer never has to tell a
790
+ * missing field from a field that means something: the gate's own mint site fills it from what its
791
+ * layers did, and the three INHERITED-lane sites — which present an ANCESTOR policy's decision, from
792
+ * upstream of both lanes — fill it with an all-`"not_adjudicated"` record rather than a fabricated
793
+ * revision. Optional on the TYPE only because a deployment may invoke its approver directly.
794
+ * RECONCILIATION metadata, never adjudication input. */
795
+ readonly ruleEvidence?: AskRuleEvidence;
685
796
  toolCallId: string;
686
797
  /** The (post-rewrite) args the tool would run with. */
687
798
  args: unknown;
@@ -859,12 +970,26 @@ export type OnAsk = "deny" | "allow" | ((req: AskRequest, signal?: AbortSignal)
859
970
  * domain the durable leg enforces pre-CAS for the same field), a THROWING read is a fail-closed
860
971
  * deny naming the true cause (never a raw rejection out of the gate), and the empty string reads
861
972
  * as absent (truthiness, the durable consumer's own read).
973
+ *
974
+ * `approver` (design/252 G-7) — the ATTRIBUTION seat: the identifier the approval channel reports for
975
+ * the party that ended this wait. Core does not authenticate it, compare it, or read it back to decide
976
+ * anything; it transcribes it onto the settlement observation next to `settledBy`, so an audit that can
977
+ * already say "a person ended this wait" can also say which one, without core growing an identity
978
+ * surface it deliberately does not have. Read the two together: `settledBy:"timeout"` with an
979
+ * `approver` names the queue whose window elapsed, NOT someone who refused.
980
+ * Screened, not trusted (see {@link screenApproverAttribution}): a non-string, an over-long value or one
981
+ * carrying control characters is a loud fail-closed refusal at {@link resolveAsk}, the same posture this
982
+ * seam takes for every other out-of-contract value — and the same reason, since a defective attribution
983
+ * that were quietly dropped would leave a settlement looking unattributed rather than misreported. It is
984
+ * carried on the arms where the CALLER settled something (its allow, its human/timeout deny) and never
985
+ * on an end the engine produced (an abort, a throw, an unclonable edit): nobody approved those.
862
986
  */
863
987
  export type AskOutcome = boolean | "unavailable" | {
864
988
  allow: boolean;
865
989
  updatedInput?: unknown;
866
990
  settledBy?: Extract<ApprovalSettledBy, "human" | "timeout">;
867
991
  reason?: string;
992
+ approver?: string;
868
993
  };
869
994
  /**
870
995
  * ruled 2026-08-04 — forward an approver into a delegated child, stamping every ask it raises with the
@@ -13,6 +13,26 @@ export const APPROVAL_SETTLED_BY_VALUES = ["human", "timeout", "aborted"];
13
13
  export function isApprovalSettledBy(v) {
14
14
  return typeof v === "string" && APPROVAL_SETTLED_BY_VALUES.includes(v);
15
15
  }
16
+ export const APPROVER_ATTRIBUTION_MAX_CHARS = 256;
17
+ const APPROVER_REFUSED_CHARS_RE = /[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u2028\u2029\u202a-\u202e\u2066-\u2069]/u;
18
+ export function screenApproverAttribution(v) {
19
+ if (v === undefined || v === "")
20
+ return {};
21
+ if (typeof v !== "string") {
22
+ return { defect: `an approver attribution must be a plain string (got ${v === null ? "null" : typeof v})` };
23
+ }
24
+ if (v.length > 2 * APPROVER_ATTRIBUTION_MAX_CHARS || [...v].length > APPROVER_ATTRIBUTION_MAX_CHARS) {
25
+ return { defect: `an approver attribution is capped at ${APPROVER_ATTRIBUTION_MAX_CHARS} characters and this one is longer; it is refused rather than truncated, because a cut identifier names someone else` };
26
+ }
27
+ if (/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/.test(v)) {
28
+ return { defect: "an approver attribution contains an unpaired surrogate; it is not well-formed text and would collapse to a replacement character in a UTF-8 sink, so two distinct approvers could converge into one record" };
29
+ }
30
+ if (APPROVER_REFUSED_CHARS_RE.test(v)) {
31
+ return { defect: "an approver attribution carries control characters, line separators or bidirectional overrides; an identifier whose bytes can forge a line — or reorder what a reader sees — in whatever renders it is refused" };
32
+ }
33
+ return { approver: v };
34
+ }
35
+ export const ASK_EVIDENCE_ABSENCE_VALUES = ["not_wired", "not_adjudicated", "unavailable", "no_match", "not_reported"];
16
36
  export function decisionText(d) {
17
37
  return d.message;
18
38
  }
@@ -899,10 +919,12 @@ export async function resolveAsk(req, onAsk, signal) {
899
919
  let supplied;
900
920
  let allowed;
901
921
  let suppliedEdit;
922
+ let suppliedApprover;
902
923
  try {
903
924
  supplied = ok.settledBy;
904
925
  allowed = ok.allow;
905
926
  suppliedEdit = ok.updatedInput;
927
+ suppliedApprover = ok.approver;
906
928
  }
907
929
  catch (err) {
908
930
  return {
@@ -930,6 +952,16 @@ export async function resolveAsk(req, onAsk, signal) {
930
952
  settledBy: "aborted",
931
953
  };
932
954
  }
955
+ const attribution = screenApproverAttribution(suppliedApprover);
956
+ if (attribution.defect !== undefined) {
957
+ return {
958
+ action: "deny",
959
+ message: `the approver for "${req.toolName}" reported an attribution this seam refuses: ${attribution.defect}; denied fail-closed`,
960
+ decisionReason: "mode",
961
+ settledBy: "aborted",
962
+ };
963
+ }
964
+ const attributionCell = attribution.approver !== undefined ? { approver: attribution.approver } : {};
933
965
  if (supplied === "timeout" && allowed === true) {
934
966
  return {
935
967
  action: "deny",
@@ -969,10 +1001,11 @@ export async function resolveAsk(req, onAsk, signal) {
969
1001
  : humanRefusalMessage(req, reasonText),
970
1002
  decisionReason: "mode",
971
1003
  settledBy: supplied === "timeout" ? "timeout" : "human",
1004
+ ...attributionCell,
972
1005
  };
973
1006
  }
974
1007
  if (suppliedEdit === undefined)
975
- return { action: "allow", decisionReason: "mode", presentedInput: presented.value, settledBy: "human" };
1008
+ return { action: "allow", decisionReason: "mode", presentedInput: presented.value, settledBy: "human", ...attributionCell };
976
1009
  const edit = tryCloneArgs(suppliedEdit);
977
1010
  if (!edit.ok) {
978
1011
  return {
@@ -982,7 +1015,7 @@ export async function resolveAsk(req, onAsk, signal) {
982
1015
  settledBy: "aborted",
983
1016
  };
984
1017
  }
985
- return { action: "allow", updatedInput: edit.value, decisionReason: "mode", settledBy: "human" };
1018
+ return { action: "allow", updatedInput: edit.value, decisionReason: "mode", settledBy: "human", ...attributionCell };
986
1019
  }
987
1020
  const okRaw = ok;
988
1021
  if (okRaw === true)
@@ -3437,6 +3437,26 @@ export type TaskEvent = ({
3437
3437
  * must not treat "absent" as "a human decided".
3438
3438
  */
3439
3439
  settledBy?: import("./tool-policy.js").ApprovalSettledBy;
3440
+ /**
3441
+ * design/252 G-7 — WHOSE settlement that was: the identifier the approval channel reported for
3442
+ * the party that ended this wait, beside the {@link settledBy} word that says what KIND of end
3443
+ * it was. The two are read together and neither substitutes for the other:
3444
+ * `settledBy:"timeout"` with an `approver` names the queue whose window elapsed, NOT someone
3445
+ * who refused.
3446
+ *
3447
+ * WHAT CORE PROMISES ABOUT IT — exactly one thing: it is what the settling caller said, screened
3448
+ * for shape (a plain string, bounded, no control characters) and otherwise untouched. Core does
3449
+ * NOT authenticate it, does not compare it to a principal, and never reads it back to decide
3450
+ * anything. Identity is established by the approval channel a deployment integrates (its card,
3451
+ * its signature, its console); this is the transcription that lets an audit which already knows
3452
+ * a person ended a wait also say which person, without the engine growing an identity surface it
3453
+ * deliberately does not have. Treat it accordingly: it is a RECORD of a claim, and its
3454
+ * trustworthiness is exactly the trustworthiness of the channel that made it.
3455
+ *
3456
+ * ABSENT on every frame that settled no approval, and on a settled approval whose channel named
3457
+ * nobody. Absence means nobody SAID — never "nobody approved this", and never "a human did".
3458
+ */
3459
+ approver?: string;
3440
3460
  /**
3441
3461
  * design/99 §E1 — `true` when {@link output} was SIZE-bounded by core (the full body exceeded the cap and
3442
3462
  * was degraded to a truncated string). Lets a consumer detect truncation programmatically instead of
@@ -3876,8 +3896,12 @@ export interface TaskStream extends AsyncIterable<TaskEvent> {
3876
3896
  /**
3877
3897
  * Inject a mid-task **steering** message (design/47) that the running task sees at the start of its
3878
3898
  * next turn (delivered via the harness steering queue). Resolves once queued; **throws** (code
3879
- * `steering.not_running`) if the task hasn't started yet or has already finished — never silently
3880
- * dropped. By default the text enters as a normal **user** message (in an orchestration the caller IS
3899
+ * `steering.not_running`) once the task has finished (teardown included) — never silently dropped. A
3900
+ * steer issued BEFORE the run has ISSUED ITS FIRST PROMPT is not refused: it is HELD for the birth
3901
+ * window and enters the queue as soon as the loop goes live, so the model sees it at the next turn
3902
+ * boundary like any other steer — it is refused if the loop ends first, or if that BOUNDED wait runs
3903
+ * out while the run still has not started (ruled 2026-08-05; a retry under the same `inputId` is then
3904
+ * clean — a refusal reserves nothing). By default the text enters as a normal **user** message (in an orchestration the caller IS
3881
3905
  * the task's user). Pass `trusted: true` ONLY for operator/system-level guidance: it is wrapped as a
3882
3906
  * `<system-reminder>` (elevated authority) — do NOT use it for caller/third-party content that could
3883
3907
  * carry a prompt injection. The seam gives the channel; the caller owns the judgement (design/43 §6).
@@ -3885,10 +3909,55 @@ export interface TaskStream extends AsyncIterable<TaskEvent> {
3885
3909
  * design/171 §5.2 — `actor` attributes WHO steered (a shared session's second voice): the text gets
3886
3910
  * the speaker envelope from the single projection point and the queued message carries the
3887
3911
  * metadata seat. Attribution only, never authority; absent = anonymous (bytes unchanged).
3912
+ *
3913
+ * design/171 §6.3 (additive) — `inputId` is the caller's correlation/idempotency key, the SAME key
3914
+ * space as the parked queue's `PendingSteerEntry.inputId` and the `human_input` event's `inputId`
3915
+ * (an ingress that already minted a message id passes it here, and the emitted frame carries it
3916
+ * VERBATIM instead of a fresh uuidv7). It exists because the two legs of one steering ingress must be
3917
+ * equally replay-safe: the parked leg has taken this key since the queue landed, so a retried request
3918
+ * that arrives while the task is LIVE was the only one that injected twice.
3919
+ * - **Absent ⇒ nothing changes**: a uuidv7 is minted for the event, nothing is recorded for the call,
3920
+ * and the delivered bytes are what they always were. Every pre-existing caller is on this arm.
3921
+ * - **Replay ⇒ idempotent no-op**: re-steering an id this stream already ACCEPTED, with an identical
3922
+ * payload (same text, same `trusted`, same `actor`), injects nothing and emits no second
3923
+ * `human_input` frame. What is remembered is exactly what QUEUED: a delivery that was refused
3924
+ * (not running) can be retried under its own id, and a call whose DELIVERED payload is
3925
+ * whitespace-only — which the harness discards without minting a frame; note a `trusted` steer is
3926
+ * wrapped first, so it queues even for blank text — reserves nothing, leaving that id usable.
3927
+ * - **Same id, DIFFERENT instruction ⇒ typed throw** `steering.duplicate_input_id`: a key is not
3928
+ * evidence of a replay, and two callers colliding on one id must not silently lose the second
3929
+ * (the parked leg's `appendPendingSteer` refuses it identically). Re-issue under a fresh id.
3930
+ * - **Bad value ⇒ typed throw** `steering.invalid_content`, never a silent fallback to "no id": the
3931
+ * value domain is the parked leg's (a non-empty string of at most `MAX_STEER_INPUT_ID_CHARS`
3932
+ * characters, and never the reserved `LEGACY_PENDING_STEER_INPUT_ID`), so one key is accepted or
3933
+ * refused the same way on both legs. Validated before the liveness check, like the parked leg's.
3934
+ * - **Liveness outranks the key**: once the run's loop-liveness latch has flipped (the same signal
3935
+ * the injection path stops polling on) a replay is refused `steering.not_running` like any other
3936
+ * steer, never answered "already accepted" — the parked leg's row CAS answers `false` for a
3937
+ * resolved checkpoint on a replayed id for the same reason. The one asymmetry, stated rather than
3938
+ * papered over: in the sub-window where the harness has gone idle but the latch has not yet
3939
+ * flipped, a FRESH steer polls (and is refused when the latch flips) while a replay answers
3940
+ * immediately with the SAME outcome its original call reported — a key whose answer depended on
3941
+ * microsecond timing would defeat its own purpose. Note what that outcome has always meant on this
3942
+ * verb: ACCEPTED INTO THE QUEUE, not consumed by the model. A steer accepted in the last moments of
3943
+ * a run can be stranded by the run ending before the next boundary drains it (true of every steer,
3944
+ * keyed or not); a caller that needs delivery evidence reads the run's own events, not this receipt.
3945
+ * - **The receipt does NOT distinguish the two**: a fresh accept and a replay both resolve `void`,
3946
+ * exactly as `setPendingSteer` answers `true` for both. The observable difference is on the event
3947
+ * stream (a fresh accept emits the `human_input` frame; a replay emits none), which is also where
3948
+ * the parked leg's difference shows (a replay adds no queue entry, so the resume drains one frame).
3949
+ * - **Honest window** (weaker than the parked leg's, deliberately stated): the live dedup domain is
3950
+ * THIS stream object — one run leg, in this process. It is not persisted, so it does not span a
3951
+ * restart, a replica, or a second `runTaskStream`/`resumeStream` call on the same session; and it
3952
+ * holds only what the LIVE verb accepted, so an id already delivered by the parked leg (drained
3953
+ * into this run's resume prompt) is NOT in it and WOULD inject again. A deployment that needs
3954
+ * cross-leg or cross-process idempotency owns that half (its own key ledger), the same division
3955
+ * of labor `notify`'s park-window dedup states.
3888
3956
  */
3889
3957
  steer(text: string, options?: {
3890
3958
  trusted?: boolean;
3891
3959
  actor?: ActorAssertion;
3960
+ inputId?: string;
3892
3961
  }): Promise<void>;
3893
3962
  /**
3894
3963
  * design/144 §2 — inject an EXTERNAL structured event into this run's task-notification lane, as a
package/dist/index.d.ts CHANGED
@@ -108,6 +108,7 @@ export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
108
108
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
109
109
  export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, SystemInjectionQueue, type TaskNotificationPayload, type TaskNotificationStatus, type ExternalNotificationInput, type SystemInjection, type SystemInjectionPriority, } from "./core/task-notification.js";
110
110
  export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, type WiringManifest, type WiringFacts, type WiringLegKind, type AskSeamForm, type AskEffective, type QuestionChannelState, type SeamProvenance, type ParkLaneReason, type ManifestDurability, type StaticWiringDeps, type StaticWiringSpec, } from "./core/wiring-manifest.js";
111
+ export { probeParkRoundTrip, durableParkGapOf, durableParkGapFor, PARK_SELFCHECK_SCOPE_PREFIX, type ParkSelfCheckResult, type ParkProbeFinding, type ParkProbeFindingCode, } from "./core/park-selfcheck.js";
111
112
  export { type StoreDurability } from "./core/checkpoint-store.js";
112
113
  export { type StoreFidelity } from "./core/checkpoint-store.js";
113
114
  export { projectHumanInput, buildHumanInputEvent, type HumanInputSource, type HumanInputFrame, type HumanInputEvent, type HumanInputDelivery, } from "./core/human-input-projection.js";
@@ -131,7 +132,7 @@ export type { SchedulerCapability, SchedulerErrorCode, ScheduledIntent, Schedule
131
132
  export { createSchedulerTools, type SchedulerToolContext, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTINEL, AUTONOMOUS_LOOP_DYNAMIC_SENTINEL, } from "./tools/scheduler-tools.js";
132
133
  export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, type AutonomousLoopPromptOptions, } from "./tools/loop-tick.js";
133
134
  export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
134
- export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, type ApprovalSettledBy, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, } from "./core/tool-policy.js";
135
+ export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, type ApprovalSettledBy, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, type AskRuleEvidence, type AskEvidenceAbsence, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
135
136
  export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassifyFn, type AutoModeClassifyInput, } from "./core/auto-mode.js";
136
137
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, type AutoModeRules, type BuildAutoModePromptOptions, type AutoModeWindowOptions, } from "./core/auto-mode-prompt.js";
137
138
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
@@ -161,7 +162,7 @@ export { FilePermissionRuleStoreProvider } from "./stores/file/permission-rule-s
161
162
  export { adoptFilePermissionRuleStore, type AdoptFileRuleStoreResult } from "./stores/file/permission-rule-adopt.js";
162
163
  export { AdoptionError, assertAdoptionBootGate, readRootAdoptionFile, writeRootAdoptionFile, ROOT_ADOPTION_FILE, type AdoptionErrorCode, type AdoptionSource, type AdoptionReport, type AdoptionReceipt, type AdoptionLegReport, type AffectedDeploymentConfig, type RootAdoptionFile, } from "./stores/file/adoption/marker.js";
163
164
  export { adoptLocalDataRoot, ackAdoptionConfig, witnessAdoptionConfig, listAdoptionQuarantine, readAdoptionStatus, type AdoptionStatus, type AdoptLocalDataRootOptions, type AdoptLocalDataRootResult, type AdoptionCarriageLeg, type AdoptionCarriageLegContext, type AdoptionConfigWitnessReceipt, } from "./stores/file/adoption/adopt.js";
164
- export { formatHookFeedback, runToolGate, createHookEnvCapabilities, createPreToolUseConstraintPolicy, type Hooks, type HookToolContext, type HookEnvCapabilities, type HookToolOutput, type PreToolUseResult, type PostToolUseResult, type UserPromptSubmitResult, type HookToolFailure, type PostToolUseFailureResult, type PostToolBatchCall, type PostToolBatchResult, type PreCompactContext, type PreCompactResult, type PostCompactContext, type StopFailureContext, type PermissionDeniedPayload, type PermissionDeniedSource, } from "./core/hooks.js";
165
+ export { formatHookFeedback, runToolGate, createHookEnvCapabilities, createPreToolUseConstraintPolicy, type PersistedRuleHit, type PersistedRuleUnreadable, type PersistedRuleAnswer, normalizePersistedRuleHit, type Hooks, type HookToolContext, type HookEnvCapabilities, type HookToolOutput, type PreToolUseResult, type PostToolUseResult, type UserPromptSubmitResult, type HookToolFailure, type PostToolUseFailureResult, type PostToolBatchCall, type PostToolBatchResult, type PreCompactContext, type PreCompactResult, type PostCompactContext, type StopFailureContext, type PermissionDeniedPayload, type PermissionDeniedSource, } from "./core/hooks.js";
165
166
  export { InMemoryMemoryStore, composeMemoryBlock, composeLayeredMemoryBlock, normalizeMemorySpec, type NormalizedMemorySpec, type MemorySpecInput, supportsConsolidation, supportsPeriodicConsolidation, firstSentence, expandLexicalTerms, lexicalSearchMatch, type Embedder, classifyPromotable, enforcePromotableWriteGate, guardedMemoryStore, MemoryGateError, detectSecret, enforceSecretWriteGate, enforceStructuredNoteSecretGate, type UtilityGate, type MemoryStore, type MemoryVectorMode, type ScoredMemory, type MemoryNoteHeader, type MemoryNoteRecord, type MemoryNoteType, type StructuredNoteInput, } from "./core/memory.js";
166
167
  export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, type MemoryBackendContractHooks, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, readV2HeaderHints, type V2HeaderHints, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, type ControlPlaneRebuildReceipt, type StrictControlPlaneLedger, type ChallengeAssignment, type ChallengeEvent, type ChallengedHistoryRow, type LineagePendingTxn, type LineagePromotion, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type MemoryGetDetails, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, type MemoryAnnouncement, type ScanFinding, type MemoryEngineOptions, type MemoryInjection, type MemoryBackend, type MemoryEntry, type MemoryEntryFrontmatter, type MemoryEntryHeader, type ScoredMemoryEntry, type NotePatch, type PatchReport, type MaterializedFile, type MemorySessionHandle, type HarvestReport, type HarvestRejection, type HarvestRejectionCode, type ParsedEntryFile, type ScannedEntryFile, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, type ParsedScopeKey, migrateScope, type MigrateScopeReport, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, type RemoteMemoryFile, type RemoteMemoryBaseline, type RemoteMaterialization, type RemoteHarvestResult, type InboundEntryFinding, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, type MemorySyncCursor, type MemorySyncConflict, type MemorySyncPlan, type RecallSource, syncMemoryScope, type MemorySyncTransport, type MemorySyncRequestBody, type MemorySyncResponseBody, type MemorySyncClientConflict, type SyncMemoryScopeOptions, type MemorySyncClientResult, } from "./core/memory-engine/index.js";
167
168
  export { SHARED_MEMORY_READ_CAP_BYTES, SHARED_MEMORY_LIST_PAGE_SIZE, SharedMemoryStoreError, type SharedMemoryStoreProvider, type SharedMemoryStoreReader, type SharedMemoryPagedList, type SharedMemoryStoreInfo, type SharedMemoryDocumentEntry, type SharedMemorySnapshot, type SharedMemoryRequestContext, type MemoryListDetails, type MemoryReadDetails, } from "./core/shared-memory/types.js";
package/dist/index.js CHANGED
@@ -87,6 +87,7 @@ export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
87
87
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
88
88
  export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, SystemInjectionQueue, } from "./core/task-notification.js";
89
89
  export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, } from "./core/wiring-manifest.js";
90
+ export { probeParkRoundTrip, durableParkGapOf, durableParkGapFor, PARK_SELFCHECK_SCOPE_PREFIX, } from "./core/park-selfcheck.js";
90
91
  export {} from "./core/checkpoint-store.js";
91
92
  export {} from "./core/checkpoint-store.js";
92
93
  export { projectHumanInput, buildHumanInputEvent, } from "./core/human-input-projection.js";
@@ -108,7 +109,7 @@ export { hasScheduler, isValidCronExpr, SchedulerError } from "./core/scheduler.
108
109
  export { createSchedulerTools, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTINEL, AUTONOMOUS_LOOP_DYNAMIC_SENTINEL, } from "./tools/scheduler-tools.js";
109
110
  export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, } from "./tools/loop-tick.js";
110
111
  export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
111
- export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, } from "./core/tool-policy.js";
112
+ export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
112
113
  export { parseAutoModeResponse, createAutoModeDecider, } from "./core/auto-mode.js";
113
114
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, } from "./core/auto-mode-prompt.js";
114
115
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
@@ -123,7 +124,7 @@ export { FilePermissionRuleStoreProvider } from "./stores/file/permission-rule-s
123
124
  export { adoptFilePermissionRuleStore } from "./stores/file/permission-rule-adopt.js";
124
125
  export { AdoptionError, assertAdoptionBootGate, readRootAdoptionFile, writeRootAdoptionFile, ROOT_ADOPTION_FILE, } from "./stores/file/adoption/marker.js";
125
126
  export { adoptLocalDataRoot, ackAdoptionConfig, witnessAdoptionConfig, listAdoptionQuarantine, readAdoptionStatus, } from "./stores/file/adoption/adopt.js";
126
- export { formatHookFeedback, runToolGate, createHookEnvCapabilities, createPreToolUseConstraintPolicy, } from "./core/hooks.js";
127
+ export { formatHookFeedback, runToolGate, createHookEnvCapabilities, createPreToolUseConstraintPolicy, normalizePersistedRuleHit, } from "./core/hooks.js";
127
128
  export { InMemoryMemoryStore, composeMemoryBlock, composeLayeredMemoryBlock, normalizeMemorySpec, supportsConsolidation, supportsPeriodicConsolidation, firstSentence, expandLexicalTerms, lexicalSearchMatch, classifyPromotable, enforcePromotableWriteGate, guardedMemoryStore, MemoryGateError, detectSecret, enforceSecretWriteGate, enforceStructuredNoteSecretGate, } from "./core/memory.js";
128
129
  export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, readV2HeaderHints, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, migrateScope, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, syncMemoryScope, } from "./core/memory-engine/index.js";
129
130
  export { SHARED_MEMORY_READ_CAP_BYTES, SHARED_MEMORY_LIST_PAGE_SIZE, SharedMemoryStoreError, } from "./core/shared-memory/types.js";
@@ -198,7 +198,7 @@ export interface WorkflowAgentHandle {
198
198
  * its reply with the returned marker. Returns that MARKER so the launcher can correlate the worker's tagged
199
199
  * reply via the #5 transcript (the worker self-stamps; reply is best-effort). One-directional + leader-driven
200
200
  * (the worker can't address the leader except by the marker). A steer issued BEFORE the worker's loop goes
201
- * live is parked and delivered onto turn 1 (birth-window delivery, bounded — ledger item 36); steers are
201
+ * live is parked and delivered into the NEXT turn's context (birth-window delivery, bounded — ledger item 36); steers are
202
202
  * delivered in call order, the birth window included. Rejects with `steering.not_running` once the task has
203
203
  * finished (teardown included).
204
204
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "5.33.0",
3
+ "version": "5.35.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -36,6 +36,7 @@
36
36
  },
37
37
  "files": [
38
38
  "dist",
39
+ "test/export-surface.snapshot.json",
39
40
  "README.md",
40
41
  "NOTICE.md",
41
42
  "CHANGELOG.md",