@sema-agent/core 5.38.0 → 5.39.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.
@@ -1,9 +1,12 @@
1
+ import { governanceBaselineProblem } from "./governance-baseline-validity.js";
1
2
  export function isSelfOrchestrationActive(spec, deps) {
2
3
  return spec.selfOrchestration === true && workflowsCapability(deps);
3
4
  }
4
5
  export function workflowsCapability(deps) {
5
- return (deps.workflowScriptRunner?.safeForUntrustedScripts === true &&
6
- deps.workflowGovernanceBaseline !== undefined);
6
+ return deps.workflowScriptRunner?.safeForUntrustedScripts === true && hasUsableGovernanceBaseline(deps);
7
+ }
8
+ function hasUsableGovernanceBaseline(deps) {
9
+ return governanceBaselineProblem(deps.workflowGovernanceBaseline) === null;
7
10
  }
8
11
  export function selfOrchestrationFailClosedReason(spec, deps) {
9
12
  if (spec.selfOrchestration !== true)
@@ -14,8 +17,10 @@ export function selfOrchestrationFailClosedReason(spec, deps) {
14
17
  if (deps.workflowScriptRunner?.safeForUntrustedScripts !== true) {
15
18
  missing.push("a hard WorkflowScriptRunner (RunnerDeps.workflowScriptRunner.safeForUntrustedScripts === true)");
16
19
  }
17
- if (deps.workflowGovernanceBaseline === undefined) {
18
- missing.push("a governance baseline (RunnerDeps.workflowGovernanceBaseline)");
20
+ const problem = governanceBaselineProblem(deps.workflowGovernanceBaseline);
21
+ if (problem !== null) {
22
+ missing.push(`a usable governance baseline (RunnerDeps.workflowGovernanceBaseline.${problem.where === "workflowGovernanceBaseline" ? "" : `${problem.where} `}` +
23
+ `is ${problem.found})`);
19
24
  }
20
25
  return (`self-orchestration (TaskSpec.selfOrchestration) requires ${missing.join(" and ")}; it is DISABLED for ` +
21
26
  `this task (fail-closed — the run_workflow tool is not mounted and the orchestration prompt is not injected).`);
@@ -40,10 +40,18 @@ export interface ReadDenyMatcher {
40
40
  /** Normalized entries (built-ins first, then additions; exact duplicates folded). The disclosure /
41
41
  * persistence face. */
42
42
  readonly entries: readonly NormalizedReadDenyEntry[];
43
- /** Judge ONE path view (segment-window semantics). Returns the first matching entry, or null. */
44
- matchPath(path: string): ReadDenyHit | null;
45
- /** §3.3 target judgment: canonical view + lexical view; EITHER hit decides. */
46
- matchTarget(canonicalKey: string, lexicalView?: string): ReadDenyHit | null;
43
+ /** Judge ONE path view (segment-window semantics). Returns the first matching entry, or null.
44
+ * `aliasResolved` = the key's MINTER reports the real filesystem already collapsed any win32
45
+ * component alias in it (canonicalizeTarget's realpath arm); unset both readings are judged,
46
+ * which is the fail-closed default. */
47
+ matchPath(path: string, opts?: {
48
+ readonly aliasResolved?: boolean;
49
+ }): ReadDenyHit | null;
50
+ /** §3.3 target judgment: canonical view + lexical view; EITHER hit decides. `aliasResolved`
51
+ * applies to the CANONICAL arm only — the lexical view is an unresolved spelling. */
52
+ matchTarget(canonicalKey: string, lexicalView?: string, opts?: {
53
+ readonly aliasResolved?: boolean;
54
+ }): ReadDenyHit | null;
47
55
  /** Traversal exclusions for the rg legs: `!`-polarity glob pairs — `!**\/<p>` plus `!**\/<p>/…`
48
56
  * (the entry itself and its descendants). */
49
57
  readonly rgExclusionGlobs: readonly ReadDenyRgGlob[];
@@ -145,7 +153,9 @@ export declare function compileSegmentPattern(raw: string, fold: "none" | "ascii
145
153
  * BOTH separator families — win32 canonical keys are backslash-form. Returns the matched raw pattern
146
154
  * or null.
147
155
  */
148
- export declare function matchSegmentPatterns(path: string, compiled: readonly CompiledSegmentPattern[]): string | null;
156
+ export declare function matchSegmentPatterns(path: string, compiled: readonly CompiledSegmentPattern[], opts?: {
157
+ readonly aliasResolved?: boolean;
158
+ }): string | null;
149
159
  /**
150
160
  * codex r3 — the PURE validation/canonicality predicate over a PERSISTED deny entry (checkpoint v9
151
161
  * face section), for the resume pre-CAS ladder: it must refuse BEFORE the approval CAS everything
@@ -91,15 +91,36 @@ export function compileSegmentPattern(raw, fold) {
91
91
  });
92
92
  return { raw, segments, foldAscii: fold === "ascii" };
93
93
  }
94
- export function matchSegmentPatterns(path, compiled) {
95
- const rawSegs = path.split(/[\\/]/).filter(Boolean);
96
- let loweredSegs;
94
+ export function matchSegmentPatterns(path, compiled, opts) {
95
+ const literalSegs = path.split(/[\\/]/).filter(Boolean);
96
+ const win32Segs = [];
97
+ for (const raw of literalSegs) {
98
+ const dot = /^\.{1,2}$/.test(raw) ? raw : raw.replace(/ +$/, "");
99
+ if (dot === ".")
100
+ continue;
101
+ if (dot === "..") {
102
+ const last = win32Segs[win32Segs.length - 1];
103
+ if (win32Segs.length > 0 && last !== "..")
104
+ win32Segs.pop();
105
+ else
106
+ win32Segs.push("..");
107
+ continue;
108
+ }
109
+ const stripped = raw.replace(/[. ]+$/, "");
110
+ win32Segs.push(stripped.length > 0 ? stripped : raw);
111
+ }
112
+ const sameView = opts?.aliasResolved === true ||
113
+ (win32Segs.length === literalSegs.length && win32Segs.every((s, i) => s === literalSegs[i]));
114
+ const views = sameView ? [literalSegs] : [literalSegs, win32Segs];
115
+ const lowered = new Map();
97
116
  for (const pat of compiled) {
98
- const segs = pat.foldAscii ? (loweredSegs ??= rawSegs.map(asciiLower)) : rawSegs;
99
- const n = pat.segments.length;
100
- for (let i = 0; i + n <= segs.length; i++) {
101
- if (pat.segments.every((re, j) => re.test(segs[i + j] ?? "")))
102
- return pat.raw;
117
+ for (const view of views) {
118
+ const segs = pat.foldAscii ? (lowered.get(view) ?? lowered.set(view, view.map(asciiLower)).get(view)) : view;
119
+ const n = pat.segments.length;
120
+ for (let i = 0; i + n <= segs.length; i++) {
121
+ if (pat.segments.every((re, j) => re.test(segs[i + j] ?? "")))
122
+ return pat.raw;
123
+ }
103
124
  }
104
125
  }
105
126
  return null;
@@ -171,15 +192,15 @@ export function compileReadDeny(additions = [], layer = "additions", builtin) {
171
192
  rgExclusionGlobs.push({ flag, glob: `!**/${g}` }, { flag, glob: `!**/${g}/**` });
172
193
  rgProbeGlobs.push({ flag, glob: `**/${g}` }, { flag, glob: `**/${g}/**` });
173
194
  }
174
- const matchPath = (path) => {
175
- const hit = matchSegmentPatterns(path, compiled);
195
+ const matchPath = (path, opts) => {
196
+ const hit = matchSegmentPatterns(path, compiled, opts);
176
197
  return hit === null ? null : { pattern: hit };
177
198
  };
178
199
  return {
179
200
  entries: normalized,
180
201
  matchPath,
181
- matchTarget(canonicalKey, lexicalView) {
182
- const canonical = matchPath(canonicalKey);
202
+ matchTarget(canonicalKey, lexicalView, opts) {
203
+ const canonical = matchPath(canonicalKey, opts);
183
204
  if (canonical !== null)
184
205
  return { ...canonical, matchedView: canonicalKey };
185
206
  if (lexicalView !== undefined) {
@@ -268,7 +268,9 @@ export declare function lexicalViewOf(spelled: string, base: string): string;
268
268
  * `executionEnv` — see design/44 §5.)
269
269
  */
270
270
  export declare function resolveKey(env: ExecutionEnv, rootCanonical: string, path: string, signal?: AbortSignal, baseCwd?: string, additionalRootsCanonical?: readonly string[], exactFileReadExemption?: (canonicalKey: string) => boolean, readDeny?: {
271
- matchTarget(canonicalKey: string, lexicalView?: string): {
271
+ matchTarget(canonicalKey: string, lexicalView?: string, opts?: {
272
+ readonly aliasResolved?: boolean;
273
+ }): {
272
274
  pattern: string;
273
275
  matchedView?: string;
274
276
  } | null;
@@ -291,6 +293,7 @@ export declare function resolveKey(env: ExecutionEnv, rootCanonical: string, pat
291
293
  export declare function canonicalizeTarget(env: ExecutionEnv, path: string, signal?: AbortSignal, baseCwd?: string): Promise<{
292
294
  ok: true;
293
295
  key: string;
296
+ aliasResolved?: true;
294
297
  } | {
295
298
  ok: false;
296
299
  message: string;
@@ -339,7 +339,9 @@ export async function resolveKey(env, rootCanonical, path, signal, baseCwd, addi
339
339
  return { ok: false, violation: { code: "invalid", message: `path "${path}" is a blocked device/special file (reading it can hang the process); refused.` } };
340
340
  }
341
341
  if (readDeny !== undefined) {
342
- const hit = readDeny.matchTarget(key, lexicalViewOf(path, baseCwd ?? rootCanonical));
342
+ const hit = readDeny.matchTarget(key, lexicalViewOf(path, baseCwd ?? rootCanonical), {
343
+ aliasResolved: canon.aliasResolved === true,
344
+ });
343
345
  if (hit !== null) {
344
346
  const lexicalHit = hit.matchedView !== undefined && hit.matchedView !== key;
345
347
  const judged = lexicalHit
@@ -419,7 +421,7 @@ export async function canonicalizeTarget(env, path, signal, baseCwd) {
419
421
  }
420
422
  const canon = await env.canonicalPath(abs, signal);
421
423
  if (canon.ok)
422
- return { ok: true, key: canon.value };
424
+ return { ok: true, key: canon.value, aliasResolved: true };
423
425
  const info = await env.fileInfo(abs, signal);
424
426
  if (!info.ok) {
425
427
  return { ok: false, message: `cannot determine whether "${path}" is a symlink: ${info.error.message}`, unresolvedSymlink: true };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "5.38.0",
3
+ "version": "5.39.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "_comment": "design/87 L3 — frozen public export surface of src/index.ts (name -> kind). DO NOT edit by hand to silence a red test. A removed/changed entry = a SemVer-BREAKING change; bump MAJOR and update this fixture in the SAME commit (design/87 §4.2 / §5.2). Regenerate via REGEN in test/export-surface.test.ts.",
3
- "count": 1585,
3
+ "count": 1597,
4
4
  "exports": {
5
5
  "A2ATaskState": "type",
6
6
  "A2ATaskStateReversal": "type",
@@ -408,6 +408,7 @@
408
408
  "LspToolOptions": "interface",
409
409
  "LspTransport": "interface",
410
410
  "MAILBOX_CONTRACT_SCOPE": "variable",
411
+ "MAILBOX_TOMBSTONED_RECIPIENT_CODE": "variable",
411
412
  "MASS_DELETION_FUSE_RATIO": "variable",
412
413
  "MAX_ACTOR_FIELD_CHARS": "variable",
413
414
  "MAX_EDIT_BYTES": "variable",
@@ -445,6 +446,8 @@
445
446
  "MailboxLease": "interface",
446
447
  "MailboxMessage": "interface",
447
448
  "MailboxStore": "interface",
449
+ "MailboxStoreError": "class",
450
+ "MailboxTombstonedRecipientContractHooks": "interface",
448
451
  "ManagedRetentionCapability": "interface",
449
452
  "ManifestDurability": "type",
450
453
  "MaterializedA2a": "interface",
@@ -1028,6 +1031,7 @@
1028
1031
  "WORKFLOW_SUBAGENT_PROMPT": "variable",
1029
1032
  "WORKFLOW_SUBAGENT_PROMPT_SCHEMA": "variable",
1030
1033
  "WORKTREE_PARENT": "variable",
1034
+ "WRITE_PROTECTED_DEFAULT_TABLE": "variable",
1031
1035
  "WebFetchConfig": "interface",
1032
1036
  "WebSearchConfig": "interface",
1033
1037
  "WiringFacts": "interface",
@@ -1077,6 +1081,11 @@
1077
1081
  "WorktreeSessionRef": "interface",
1078
1082
  "WorktreeToolsOptions": "interface",
1079
1083
  "WritablePermissionRuleStore": "interface",
1084
+ "WriteProtectedEntry": "type",
1085
+ "WriteProtectedHit": "interface",
1086
+ "WriteProtectedKind": "type",
1087
+ "WriteProtectedRow": "interface",
1088
+ "WriteProtectionMatcher": "interface",
1080
1089
  "ackAdoptionConfig": "function",
1081
1090
  "acquireCcLock": "function",
1082
1091
  "addDotsOf": "function",
@@ -1158,6 +1167,7 @@
1158
1167
  "collectBelowFrontier": "function",
1159
1168
  "combinePolicies": "function",
1160
1169
  "compileReadDeny": "function",
1170
+ "compileWriteProtection": "function",
1161
1171
  "complianceCallDenial": "function",
1162
1172
  "compose": "function",
1163
1173
  "composeConstitution": "function",
@@ -1362,6 +1372,7 @@
1362
1372
  "mailboxAckOwnershipContract": "function",
1363
1373
  "mailboxBundledOnlyContract": "function",
1364
1374
  "mailboxStoreContract": "function",
1375
+ "mailboxTombstonedRecipientContract": "function",
1365
1376
  "markTruncated": "function",
1366
1377
  "materializeA2aTools": "function",
1367
1378
  "materializeEntriesToFiles": "function",
@@ -1477,6 +1488,7 @@
1477
1488
  "resolveTaskLimits": "function",
1478
1489
  "resolveTaskModel": "function",
1479
1490
  "resolveUsageWindows": "function",
1491
+ "resolveWriteProtectedTable": "function",
1480
1492
  "restoreFrozenPaths": "function",
1481
1493
  "resumeWithVerification": "function",
1482
1494
  "retiredWorkflowNameAliases": "function",