@sema-agent/core 7.0.1 → 7.1.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 (51) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/agents/repair-loop.d.ts +8 -7
  3. package/dist/agents/roster-store.d.ts +7 -2
  4. package/dist/agents/subagent.js +29 -5
  5. package/dist/brain/errors.d.ts +18 -0
  6. package/dist/brain/errors.js +3 -0
  7. package/dist/brain/stream-engine.js +6 -4
  8. package/dist/core/context-edit.d.ts +3 -0
  9. package/dist/core/governance-codes.d.ts +1 -1
  10. package/dist/core/governance-codes.js +4 -0
  11. package/dist/core/hooks.d.ts +26 -6
  12. package/dist/core/hooks.js +8 -5
  13. package/dist/core/image-downsample.d.ts +4 -3
  14. package/dist/core/memory-engine/engine.d.ts +62 -5
  15. package/dist/core/memory-engine/engine.js +90 -19
  16. package/dist/core/memory-engine/index.d.ts +1 -1
  17. package/dist/core/memory-engine/layout.d.ts +11 -3
  18. package/dist/core/roles.d.ts +36 -9
  19. package/dist/core/roles.js +19 -6
  20. package/dist/core/runner/prepare-memory.js +29 -22
  21. package/dist/core/runner/prepare-task.d.ts +5 -3
  22. package/dist/core/runner/prepare-task.js +88 -47
  23. package/dist/core/runner/runtask.d.ts +12 -3
  24. package/dist/core/runner/runtask.js +32 -4
  25. package/dist/core/safety-axis-vocab.d.ts +1 -1
  26. package/dist/core/strategy-store.d.ts +4 -1
  27. package/dist/core/task-registry-shared.d.ts +7 -3
  28. package/dist/core/tool-errors.d.ts +1 -1
  29. package/dist/core/tool-policy.d.ts +40 -7
  30. package/dist/core/tool-policy.js +63 -9
  31. package/dist/core/types.d.ts +97 -11
  32. package/dist/engine/compaction/compaction.js +6 -2
  33. package/dist/engine/harness/agent-harness.d.ts +28 -6
  34. package/dist/engine/harness/agent-harness.js +34 -2
  35. package/dist/engine/harness/messages.js +4 -0
  36. package/dist/engine/harness/types.d.ts +37 -0
  37. package/dist/engine/harness/types.js +5 -0
  38. package/dist/engine/session/session.js +3 -2
  39. package/dist/index.d.ts +1 -1
  40. package/dist/internal/harness.d.ts +1 -0
  41. package/dist/internal/harness.js +1 -0
  42. package/dist/orchestration/builtin-workflows.d.ts +17 -9
  43. package/dist/orchestration/run-workflow-tool.d.ts +9 -1
  44. package/dist/orchestration/run-workflow-tool.js +18 -8
  45. package/dist/orchestration/workflow-governance.js +1 -1
  46. package/dist/orchestration/workflow-types.d.ts +1 -0
  47. package/dist/orchestration/workflow.d.ts +9 -1
  48. package/dist/orchestration/workflow.js +5 -5
  49. package/dist/stores/file/mailbox-store.d.ts +2 -1
  50. package/package.json +1 -1
  51. package/test/export-surface.snapshot.json +3 -1
@@ -332,8 +332,13 @@ export async function createRunWorkflowTool(d) {
332
332
  "body (equivalently, pass { phase: 'Fix' } in an agent's opts). Give each stage a phase so progress " +
333
333
  "renders as stage groups; use the same titles you declared in meta.phases.\n" +
334
334
  "• spec.modelName works only when the deployment configured a workflow model allowlist, and its legal " +
335
- "values are that deployment's own model CATALOG KEYS not generic tier words (a bare 'opus'/'sonnet'/" +
336
- "'haiku' is a name the catalog almost certainly does not hold, and is refused rather than approximated). " +
335
+ "values are the names on THAT allowlist which the deployment's model CATALOG KEYS also hold a name " +
336
+ "off the allowlist, and a listed name the catalog lacks, are both refused rather than approximated. " +
337
+ "Generic tier words ('opus'/'sonnet'/'haiku'/'pro'/'flash'/'best') are not a separate namespace: on a " +
338
+ "deployment that configured tier bindings they become ordinary catalog keys, but only the ones its " +
339
+ "bindings actually resolve; on one that did not they are simply absent, unless its catalog happens to " +
340
+ "declare that literal name. Either way the allowlist is the gate, so a tier word is legal here only " +
341
+ "when this deployment allowlisted it, never as a shortcut around it. " +
337
342
  "It also goes on the SPEC (the first argument), never in an agent()'s options object; `model` is accepted " +
338
343
  "there as an alias for the same field. If unsure, omit it and the agent runs on the deployment's default " +
339
344
  "role.\n" +
@@ -515,12 +520,17 @@ export async function createRunWorkflowTool(d) {
515
520
  ...(() => {
516
521
  if ("memoryCaptureOptedOut" in ctx) {
517
522
  return {
518
- parentMemoryCaptureState: () => ({
519
- optedOut: ctx.memoryCaptureOptedOut === true,
520
- indeterminate: ctx.memoryCaptureIndeterminate === true,
521
- ...(ctx.memoryCaptureControlDir !== undefined ? { controlDir: ctx.memoryCaptureControlDir } : {}),
522
- ...(ctx.memoryCaptureAncestors !== undefined ? { ancestors: ctx.memoryCaptureAncestors } : {}),
523
- }),
523
+ parentMemoryCaptureState: () => {
524
+ const o = ctx.memoryCaptureOptedOut ?? false;
525
+ const i = ctx.memoryCaptureIndeterminate ?? false;
526
+ const build = (optedOut, indeterminate) => ({
527
+ optedOut: optedOut === true,
528
+ indeterminate: indeterminate === true,
529
+ ...(ctx.memoryCaptureControlDir !== undefined ? { controlDir: ctx.memoryCaptureControlDir } : {}),
530
+ ...(ctx.memoryCaptureAncestors !== undefined ? { ancestors: ctx.memoryCaptureAncestors } : {}),
531
+ });
532
+ return o instanceof Promise || i instanceof Promise ? Promise.all([o, i]).then(([ov, iv]) => build(ov, iv)) : build(o, i);
533
+ },
524
534
  };
525
535
  }
526
536
  return d.parentMemoryCaptureState !== undefined ? { parentMemoryCaptureState: d.parentMemoryCaptureState } : {};
@@ -99,7 +99,7 @@ export function resolveModelName(name, allowlist, models) {
99
99
  if (!allowlist.includes(name)) {
100
100
  throw new WorkflowModelNotAllowedError(name, `not in the workflow model allowlist [${allowlist.join(", ")}]`);
101
101
  }
102
- const model = models?.[name];
102
+ const model = models !== undefined && Object.hasOwn(models, name) ? models[name] : undefined;
103
103
  if (!model) {
104
104
  throw new WorkflowModelNotAllowedError(name, "allowed by name but not present in RunnerDeps.models");
105
105
  }
@@ -392,6 +392,7 @@ export type WorkflowEvent = {
392
392
  output?: string;
393
393
  errorCode?: string;
394
394
  toolCalls?: number;
395
+ modelResolved?: string;
395
396
  replayed?: boolean;
396
397
  ts: number;
397
398
  } | {
@@ -405,7 +405,15 @@ export interface RunWorkflowOptions {
405
405
  sessionId: string;
406
406
  controlDir?: string;
407
407
  }>;
408
- };
408
+ } | Promise<{
409
+ optedOut: boolean;
410
+ indeterminate: boolean;
411
+ controlDir?: string;
412
+ ancestors?: ReadonlyArray<{
413
+ sessionId: string;
414
+ controlDir?: string;
415
+ }>;
416
+ }>;
409
417
  /** Call-time getter for the HOST run's RESOLVED Model object. A spawned agent whose
410
418
  * fold chain (script spec → agentType → governance baseline) produced NO model inherits the
411
419
  * parent's full object — baseUrl/key routing included — instead of falling to a string/role
@@ -60,7 +60,7 @@ function workflowModelLabel(spec, catalog) {
60
60
  return undefined;
61
61
  if (typeof model !== "string")
62
62
  return model.id ?? model.name;
63
- const served = catalog?.[model]?.id;
63
+ const served = catalog !== undefined && Object.hasOwn(catalog, model) ? catalog[model]?.id : undefined;
64
64
  return served ?? resolveModelDisplayLabel(model);
65
65
  }
66
66
  export const WORKFLOW_SUBAGENT_PROMPT = `You are a subagent spawned by a workflow orchestration script. Use the tools available to complete the task.
@@ -619,8 +619,8 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
619
619
  ...(opts.placementRoot !== undefined ? { placementRoot: opts.placementRoot } : {}),
620
620
  ...(opts.originatingSessionId !== undefined ? { parentSessionId: opts.originatingSessionId } : {}),
621
621
  };
622
- const captureFloorSeatsNow = () => {
623
- const s = opts.parentMemoryCaptureState?.();
622
+ const captureFloorSeatsNow = async () => {
623
+ const s = await opts.parentMemoryCaptureState?.();
624
624
  if (s === undefined)
625
625
  return {};
626
626
  return {
@@ -1255,7 +1255,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1255
1255
  opts.onForwardEvent(e.type === "task_progress" ? { ...e, workflowRunId: runId, workflowAgentLabel: label } : e);
1256
1256
  }
1257
1257
  : undefined;
1258
- const baseInternals = { ...(agentOpts.isolation ? { isolation: agentOpts.isolation } : {}), ...(opts.parentCwd !== undefined ? { parentCwd: opts.parentCwd } : {}), ...spawnAttribution, ...captureFloorSeatsNow(), ...(enrichedForward !== undefined ? { onForwardEvent: enrichedForward } : {}), delegationTaskType: "workflow", agentName: label, onWorkspaceResolved: createWorkspaceObserver(rec) };
1258
+ const baseInternals = { ...(agentOpts.isolation ? { isolation: agentOpts.isolation } : {}), ...(opts.parentCwd !== undefined ? { parentCwd: opts.parentCwd } : {}), ...spawnAttribution, ...(await captureFloorSeatsNow()), ...(enrichedForward !== undefined ? { onForwardEvent: enrichedForward } : {}), delegationTaskType: "workflow", agentName: label, onWorkspaceResolved: createWorkspaceObserver(rec) };
1259
1259
  let attempts = 0;
1260
1260
  let throttleRetried = false;
1261
1261
  let lastAttemptReason;
@@ -1574,7 +1574,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1574
1574
  ...(agentOpts.isolation ? { isolation: agentOpts.isolation } : {}),
1575
1575
  ...(opts.parentCwd !== undefined ? { parentCwd: opts.parentCwd } : {}),
1576
1576
  ...spawnAttribution,
1577
- ...captureFloorSeatsNow(),
1577
+ ...(await captureFloorSeatsNow()),
1578
1578
  ...(enrichedForwardS !== undefined ? { onForwardEvent: enrichedForwardS } : {}),
1579
1579
  delegationTaskType: "workflow",
1580
1580
  agentName: label,
@@ -30,7 +30,8 @@ export declare class FileMailboxStore implements MailboxStore {
30
30
  * file can never disagree. Before this, `AgentOne` and `agentone` produced two DIFFERENT shared-state
31
31
  * keys but the SAME file on a case-insensitive filesystem (macOS/Windows default) — one recipient read
32
32
  * the other's messages after a restart. Folding is also the semantically correct answer, not just the
33
- * safe one: the addressing layer's `normalizeAgentName` (task-registry.ts:621) already lowercases, so
33
+ * safe one: the addressing layer's `normalizeAgentName` (defined in core/task-registry-shared.ts and
34
+ * re-exported by task-registry.ts) already lowercases, so
34
35
  * two case-variant handles ARE the same agent; this makes the storage layer say so explicitly instead of
35
36
  * depending on an upstream coincidence, and behave identically on case-SENSITIVE filesystems (Linux),
36
37
  * where the old code silently split one logical agent's mail across two boxes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "7.0.1",
3
+ "version": "7.1.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -1,7 +1,7 @@
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
3
  "_tierComment": "#435 v1 — machine-readable layering of the public surface: stable = demonstrated by README.md / src/examples; internal = an `Internal`-marked name or a runner/engine deep-subtree declaration (the model seam src/engine/llm is excluded — it is the BYOM contract, not an engine internal); advanced = a supported export the front door does not walk you through. THIS IS AN INITIAL HEURISTIC, derived mechanically and expected to be refined ticket by ticket: no human reviewed these 1700+ entries one by one, and nothing here claims otherwise. Known bias: a short or English-word export name (ok, err, Result, Usage) can match ordinary prose in README.md and land `stable` on a coincidence. Every export MUST carry a tier — a new export with no row fails the gate in export-surface.test.ts.",
4
- "count": 1841,
4
+ "count": 1842,
5
5
  "exports": {
6
6
  "A2ATaskState": "type",
7
7
  "A2ATaskStateReversal": "type",
@@ -996,6 +996,7 @@
996
996
  "SessionCaptureOptOutRecord": "interface",
997
997
  "SessionCaptureRecordStore": "interface",
998
998
  "SessionError": "class",
999
+ "SessionMemoryStatus": "interface",
999
1000
  "SessionMetadata": "interface",
1000
1001
  "SessionPermissionRules": "interface",
1001
1002
  "SessionPlacement": "interface",
@@ -2839,6 +2840,7 @@
2839
2840
  "SessionCaptureOptOutRecord": "advanced",
2840
2841
  "SessionCaptureRecordStore": "advanced",
2841
2842
  "SessionError": "stable",
2843
+ "SessionMemoryStatus": "advanced",
2842
2844
  "SessionMetadata": "stable",
2843
2845
  "SessionPermissionRules": "advanced",
2844
2846
  "SessionPlacement": "advanced",