@sema-agent/core 5.22.0 → 5.24.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +169 -1
  2. package/dist/agents/subagent.js +3 -2
  3. package/dist/core/checkpoint-store.d.ts +38 -3
  4. package/dist/core/checkpoint-store.js +2 -1
  5. package/dist/core/governance-codes.js +3 -0
  6. package/dist/core/hooks.d.ts +69 -2
  7. package/dist/core/hooks.js +100 -15
  8. package/dist/core/memory-engine/engine.d.ts +28 -1
  9. package/dist/core/memory-engine/engine.js +62 -3
  10. package/dist/core/memory-engine/index.d.ts +1 -1
  11. package/dist/core/memory-engine/index.js +1 -1
  12. package/dist/core/memory-engine/layout.d.ts +69 -3
  13. package/dist/core/memory-engine/layout.js +75 -6
  14. package/dist/core/permission-rule-consent.js +2 -1
  15. package/dist/core/permission-rule-org.d.ts +36 -2
  16. package/dist/core/permission-rule-org.js +23 -0
  17. package/dist/core/permission-rule-store.d.ts +25 -14
  18. package/dist/core/permission-rule-store.js +7 -2
  19. package/dist/core/permission-rule-sync.d.ts +8 -0
  20. package/dist/core/permission-rule-sync.js +35 -6
  21. package/dist/core/runner/prepare-task.d.ts +10 -2
  22. package/dist/core/runner/prepare-task.js +120 -11
  23. package/dist/core/runner/runtask.js +46 -1
  24. package/dist/core/runner/session-file-state-replay.js +3 -0
  25. package/dist/core/tool-policy.d.ts +37 -4
  26. package/dist/core/tool-policy.js +49 -19
  27. package/dist/core/tool-result-store.d.ts +17 -1
  28. package/dist/core/tool-result-store.js +79 -4
  29. package/dist/core/trace.d.ts +47 -0
  30. package/dist/core/types.d.ts +45 -5
  31. package/dist/core/wiring-manifest.d.ts +16 -1
  32. package/dist/core/wiring-manifest.js +7 -1
  33. package/dist/index.d.ts +18 -10
  34. package/dist/index.js +5 -3
  35. package/dist/orchestration/goal.d.ts +10 -0
  36. package/dist/orchestration/goal.js +6 -5
  37. package/dist/stores/file/adoption/adopt.d.ts +146 -0
  38. package/dist/stores/file/adoption/adopt.js +611 -0
  39. package/dist/stores/file/adoption/marker.d.ts +202 -0
  40. package/dist/stores/file/adoption/marker.js +205 -0
  41. package/dist/stores/file/background-agent-store.js +2 -0
  42. package/dist/stores/file/checkpoint-store.js +2 -0
  43. package/dist/stores/file/file-snapshot-store.js +2 -0
  44. package/dist/stores/file/index.d.ts +2 -0
  45. package/dist/stores/file/index.js +4 -0
  46. package/dist/stores/file/mailbox-store.js +2 -0
  47. package/dist/stores/file/memory-store.js +2 -0
  48. package/dist/stores/file/session-policy-store.d.ts +11 -1
  49. package/dist/stores/file/session-policy-store.js +9 -2
  50. package/dist/stores/file/session-store.js +2 -0
  51. package/dist/stores/file/task-list-store.js +2 -0
  52. package/dist/stores/file/tool-result-store.js +2 -0
  53. package/dist/stores/file/usage-window-store.js +2 -0
  54. package/dist/stores/file/workflow-journal-store.js +2 -0
  55. package/dist/stores/file/workflow-run-store.js +2 -0
  56. package/dist/tools/fs/bash-readonly-classifier.js +59 -10
  57. package/dist/tools/fs/fs-bash.js +7 -4
  58. package/dist/tools/monitor.js +3 -3
  59. package/package.json +3 -2
@@ -4,6 +4,7 @@ import { join } from "node:path";
4
4
  import { callKeyOrdinal } from "../../core/workflow-journal-store.js";
5
5
  import { AppendLog } from "./fs-atomic.js";
6
6
  import { canonicalStoreKey, sanitizePathComponent, writeThenLink } from "./fs-atomic.js";
7
+ import { assertAdoptionBootGate } from "./adoption/marker.js";
7
8
  import { oversizeJournalResult } from "../../core/workflow-journal-store.js";
8
9
  export { MAX_JOURNAL_RESULT_BYTES, oversizeJournalResult } from "../../core/workflow-journal-store.js";
9
10
  export const RESUME_CLAIM_TTL_MS = 60 * 60 * 1000;
@@ -28,6 +29,7 @@ export class FileWorkflowJournalStore {
28
29
  }
29
30
  constructor(root, fsyncEnabled = true) {
30
31
  this.fsyncEnabled = fsyncEnabled;
32
+ assertAdoptionBootGate(root, "FileWorkflowJournalStore");
31
33
  this.dir = join(root, "workflow-journal");
32
34
  mkdirSync(this.dir, { recursive: true, mode: 0o700 });
33
35
  this.claimsDir = join(this.dir, "claims");
@@ -2,6 +2,7 @@ import { join } from "node:path";
2
2
  import { assertRetentionPolicy } from "../../core/retention-policy.js";
3
3
  import { WorkflowRunStoreError, isTerminalWorkflowStatus, nextWorkflowRunOnUpdate, queryWorkflowRuns, } from "../../core/workflow-run-store.js";
4
4
  import { SharedLedgerTable } from "./shared-ledger.js";
5
+ import { assertAdoptionBootGate } from "./adoption/marker.js";
5
6
  const runLedgers = new SharedLedgerTable({
6
7
  keyOf: (run) => run.id,
7
8
  apply: (runs, ev) => {
@@ -21,6 +22,7 @@ export class FileWorkflowRunStore {
21
22
  return this.ledger.rows;
22
23
  }
23
24
  constructor(root, opts = {}) {
25
+ assertAdoptionBootGate(root, "FileWorkflowRunStore");
24
26
  this.fsyncEnabled = opts.fsync !== false;
25
27
  this.compactEvery = opts.compactEvery ?? 1000;
26
28
  const dir = join(root, "workflow-runs");
@@ -238,11 +238,51 @@ function isGrepClusterFileStdin(tok) {
238
238
  }
239
239
  return false;
240
240
  }
241
- function isCutSeparatedDelimiterOption(tok) {
242
- if (tok.includes("="))
241
+ const SEPARATED_VALUE_OPTIONS = {
242
+ head: { short: "cn", long: ["bytes", "lines"] },
243
+ tail: { short: "cns", long: ["bytes", "lines", "sleep-interval", "pid", "max-unchanged-stats"] },
244
+ cut: { short: "bcdf", long: ["bytes", "characters", "delimiter", "fields", "output-delimiter"] },
245
+ grep: {
246
+ ownsValueButNotSkippable: "ef",
247
+ short: "mABCdD",
248
+ long: [
249
+ "max-count",
250
+ "after-context",
251
+ "before-context",
252
+ "context",
253
+ "devices",
254
+ "directories",
255
+ "label",
256
+ "binary-files",
257
+ "group-separator",
258
+ "include",
259
+ "exclude",
260
+ "exclude-dir",
261
+ ],
262
+ },
263
+ };
264
+ function takesSeparatedValue(name, tok) {
265
+ const model = SEPARATED_VALUE_OPTIONS[name];
266
+ if (model === undefined)
243
267
  return false;
244
- const name = longOptionNameOf(tok);
245
- return name !== undefined && (isLongOptionAbbrevOf(name, "delimiter") || isLongOptionAbbrevOf(name, "output-delimiter"));
268
+ if (tok.startsWith("--")) {
269
+ if (tok.includes("="))
270
+ return false;
271
+ const long = longOptionNameOf(tok);
272
+ return long !== undefined && model.long.some((full) => isLongOptionAbbrevOf(long, full));
273
+ }
274
+ if (!/^-[A-Za-z0-9]/.test(tok))
275
+ return false;
276
+ for (let i = 1; i < tok.length; i++) {
277
+ const ch = tok[i];
278
+ if ((model.ownsValueButNotSkippable ?? "").includes(ch))
279
+ return false;
280
+ if (model.short.includes(ch))
281
+ return i === tok.length - 1;
282
+ if (!/[A-Za-z0-9]/.test(ch))
283
+ return false;
284
+ }
285
+ return false;
246
286
  }
247
287
  function isGrepFileStdinLongOption(tok) {
248
288
  const eq = tok.indexOf("=");
@@ -478,19 +518,28 @@ export function classifyCompoundReadonlyDetailed(command, allow, boundary) {
478
518
  let sawNonFlagOperand = false;
479
519
  let hasStdinDash = false;
480
520
  let operandCount = 0;
521
+ let endOfOptions = false;
481
522
  if (name !== "tr") {
482
523
  for (let k = 0; k < restArgs.length; k++) {
483
524
  const t = restArgs[k];
484
- if (name === "cut" && (t === "-d" || isCutSeparatedDelimiterOption(t))) {
525
+ if (endOfOptions) {
526
+ }
527
+ else if (t === "--") {
528
+ endOfOptions = true;
529
+ continue;
530
+ }
531
+ else if (takesSeparatedValue(name, t)) {
485
532
  k++;
486
533
  continue;
487
534
  }
488
- if (name === "grep" && (isGrepClusterFileStdin(t) || isGrepFileStdinLongOption(t))) {
489
- hasStdinDash = true;
490
- break;
535
+ else {
536
+ if (name === "grep" && (isGrepClusterFileStdin(t) || isGrepFileStdinLongOption(t))) {
537
+ hasStdinDash = true;
538
+ break;
539
+ }
540
+ if (t.startsWith("-") && t !== "-")
541
+ continue;
491
542
  }
492
- if (t.startsWith("-") && t !== "-")
493
- continue;
494
543
  const isGrepPatternPosition = name === "grep" && !grepPatternSuppliedByFlag && !sawNonFlagOperand;
495
544
  sawNonFlagOperand = true;
496
545
  operandCount++;
@@ -14,9 +14,12 @@ import { BASH_READONLY_DEFAULT_ALLOW, coarseReadonlyCheck, classifyBoundedReadon
14
14
  export function bashReversibilityProbe(allow, boundary) {
15
15
  const allowSet = new Set(allow ?? BASH_READONLY_DEFAULT_ALLOW);
16
16
  return (args) => {
17
- const command = args?.command;
17
+ const a = args;
18
+ const command = a?.command;
18
19
  if (typeof command !== "string")
19
20
  return { reversible: false };
21
+ if (a?.run_in_background === true)
22
+ return { reversible: false };
20
23
  const resolved = typeof boundary === "function" ? boundary() : boundary;
21
24
  if (classifyCompoundReadonly(command, allowSet, resolved) === undefined)
22
25
  return { reversible: true };
@@ -555,8 +558,8 @@ export function createBashTool(env, rootCanonical, coAuthor = false, cwdRef = {
555
558
  const bgCappedByMax = appliedTimeoutSec !== undefined && requestedTimeoutSec !== undefined && typeof bgCaps.maxBgTimeoutSec === "number" && requestedTimeoutSec > bgCaps.maxBgTimeoutSec;
556
559
  const budgetNote = appliedTimeoutSec !== undefined
557
560
  ? bgCappedByMax
558
- ? ` Time budget: requested ${requestedTimeoutSec}s, capped at ${bgCaps.maxBgTimeoutSec}s (env ceiling: requests above ${bgCaps.maxBgTimeoutSec}s are reduced to it) — auto-terminates if still running after ${appliedTimeoutSec}s.`
559
- : ` Time budget: auto-terminates if still running after ${appliedTimeoutSec}s (hard cap ${bgCaps.maxBgTimeoutSec}s).`
561
+ ? ` Time budget: requested ${requestedTimeoutSec}s, capped at ${bgCaps.maxBgTimeoutSec}s (env ceiling: requests above ${bgCaps.maxBgTimeoutSec}s are reduced to it) — the process may run up to ${appliedTimeoutSec}s.`
562
+ : ` Time budget: the process may run up to ${appliedTimeoutSec}s (hard cap ${bgCaps.maxBgTimeoutSec}s).`
560
563
  : "";
561
564
  const lifetimeNote = backgroundLifetimeNote(bgCaps, bgSessionScoped, bgEnvIsolatedOwned);
562
565
  const registry = taskOpts.taskRegistry;
@@ -655,7 +658,7 @@ export function createBashTool(env, rootCanonical, coAuthor = false, cwdRef = {
655
658
  const tail = stdoutSoFar.slice(-1_000);
656
659
  const adoptCaps = detachEnv.backgroundCapabilities;
657
660
  const adoptBudgetNote = typeof adoptCaps.defaultBgTimeoutSec === "number" && typeof adoptCaps.maxBgTimeoutSec === "number"
658
- ? ` Time budget: auto-terminates if still running after ${Math.min(adoptCaps.defaultBgTimeoutSec, adoptCaps.maxBgTimeoutSec)}s (hard cap ${adoptCaps.maxBgTimeoutSec}s).`
661
+ ? ` Time budget: the process may run up to ${Math.min(adoptCaps.defaultBgTimeoutSec, adoptCaps.maxBgTimeoutSec)}s (hard cap ${adoptCaps.maxBgTimeoutSec}s).`
659
662
  : "";
660
663
  return {
661
664
  content: (cause === "timeout"
@@ -121,7 +121,7 @@ export function createMonitorTool(env, opts) {
121
121
  const envWallMs = bgTimeout !== undefined ? bgTimeout * 1000 : undefined;
122
122
  const envWallWins = !isPersistent && envWallMs !== undefined && envWallMs < timeoutMs;
123
123
  const persistentBudget = bgTimeout !== undefined
124
- ? `the execution environment kills background processes after ${bgTimeout}s`
124
+ ? `the execution environment's background time budget ends the watch after ${bgTimeout}s`
125
125
  : `the execution environment's background time budget still applies`;
126
126
  const retainedByEnv = env.backgroundCapabilities.retainBackgroundProcesses === true;
127
127
  const retainedBySpec = opts.retainBackgroundProcesses === true;
@@ -142,8 +142,8 @@ export function createMonitorTool(env, opts) {
142
142
  const lifetime = isPersistent
143
143
  ? `It is persistent: no watch timeout of its own${timeoutIgnoredNote}, but ${persistentBudget}${persistentAnchor}`
144
144
  : envWallWins
145
- ? `It will be killed after ${envWallMs}ms if still running — the execution environment's background time budget (${bgTimeout}s) is shorter than the ${timeoutMs}ms watch timeout${clampNote}, and the shorter of the two wins.`
146
- : `It will be killed after ${timeoutMs}ms${clampNote} if still running.`;
145
+ ? `The watch may run up to ${envWallMs}ms — the execution environment's background time budget (${bgTimeout}s) is shorter than the ${timeoutMs}ms watch timeout${clampNote}, and the shorter of the two wins.`
146
+ : `The watch may run up to ${timeoutMs}ms${clampNote}.`;
147
147
  const content = onNotify !== undefined
148
148
  ? `Monitoring in background; task_id=${taskId}. Each stdout line becomes a notification event (lines within ` +
149
149
  `~200ms are batched); you'll get a final notification with the exit code when it ends — do not poll. ` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "5.22.0",
3
+ "version": "5.24.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -63,7 +63,8 @@
63
63
  "gate:domain-lexicon": "node scripts/verify-domain-lexicon.mjs",
64
64
  "gate:single-mint": "node scripts/verify-single-mint.mjs",
65
65
  "gate:nuia": "node scripts/verify-nuia-baseline.mjs",
66
- "gate:field-liveness": "node scripts/verify-field-liveness.mjs"
66
+ "gate:field-liveness": "node scripts/verify-field-liveness.mjs",
67
+ "gate:error-surface": "node scripts/verify-error-surface.mjs"
67
68
  },
68
69
  "dependencies": {
69
70
  "@modelcontextprotocol/sdk": "1.30.0",