@sema-agent/core 5.33.0 → 5.34.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.
- package/CHANGELOG.md +52 -0
- package/dist/core/push-queue.d.ts +4 -1
- package/dist/core/push-queue.js +2 -1
- package/dist/core/runner/prepare-task.d.ts +13 -0
- package/dist/core/runner/runtask.js +13 -5
- package/dist/core/safe-notify.d.ts +9 -0
- package/dist/core/safe-notify.js +9 -0
- package/package.json +2 -1
- package/test/export-surface.snapshot.json +1552 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.34.0 — 2026-08-15
|
|
4
|
+
|
|
5
|
+
No BREAKING changes. Two behavior-surface additions (both additive seats), one packaging addition,
|
|
6
|
+
new test/gate infrastructure; zero resolution-result changes.
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- `RunInternals.onStatusEvent` (#253): the run's own top-level `status` TaskEvent stream (brain
|
|
11
|
+
liveness — rate-limit/retry/reconnect/circuit-open) offered to the internals holder beside the
|
|
12
|
+
queue. A composition entry (verify/cascade/`runDeveloperTask`) drains its inner legs' queues
|
|
13
|
+
itself, so an inner leg's retry disclosure previously died inside the gate. The sink receives the
|
|
14
|
+
SAME (frozen) frame object the stream carries, gated on queue acceptance (a late detached emission
|
|
15
|
+
cannot reach the sink after the stream refused it), contained per the host-callback isolation
|
|
16
|
+
primitive: a throwing OR async-rejecting sink never faults the leg, first failure per site is
|
|
17
|
+
disclosed via `console.warn`, later ones are counted.
|
|
18
|
+
- Status frames on the task stream are now frozen (`Object.freeze`, flat object): a mutating host
|
|
19
|
+
callback can no longer rewrite the queued wire event. `PushQueue.push` returns whether the value
|
|
20
|
+
was accepted (compatible signature widening, `void` callers unaffected).
|
|
21
|
+
- The export-surface snapshot (`test/export-surface.snapshot.json`) ships in the npm package —
|
|
22
|
+
downstream consumption gates can anchor the in-package file instead of a vendored copy.
|
|
23
|
+
- `examples/` (source tree, excluded from the build): a runnable end-to-end governed-agent arc
|
|
24
|
+
(`src/examples/governed-agent/`) — org rules, narrowing, durable approval park, cross-worker
|
|
25
|
+
resume with non-widening enforcement — 29 self-checking steps, offline and key-less, with a
|
|
26
|
+
smoke test on the real loading path. Not part of the published artifact.
|
|
27
|
+
- Request-prefix/caching test infrastructure: a prefix-stability baseline (adjacent requests as
|
|
28
|
+
append-extensions, three known breaks pinned as current-state), a live provider cache-hit leg
|
|
29
|
+
(same-session second turn must be served from the prefix cache; measured ≈97.5% on deepseek), and
|
|
30
|
+
a byte-equal request-reconstruction pin across durable resume and session fork.
|
|
31
|
+
- ship-post-lint registry legs: cited F-nnn/A-nnn ledger files must exist and carry the shipping
|
|
32
|
+
version; zero citations require an explicit `FEATS: none` / `AUDITS: none`.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- 5.34 pre-release rescan (two confirmed): an `async` sink at any run-internals seat now has its
|
|
37
|
+
rejection contained through the same notifier/site as a synchronous throw
|
|
38
|
+
(`observeThenableRejection`, one source for status + both activity sites — previously a rejected
|
|
39
|
+
async `onActivity` escaped as an unhandled rejection and could terminate the process); and
|
|
40
|
+
ship-post-lint's published-output delta and `--tree-fingerprint` now fold packaged non-dist files
|
|
41
|
+
(`pkg:` prefix), so a change to the shipped snapshot can no longer report as byte-identical output.
|
|
42
|
+
|
|
43
|
+
- #248: the run-internals activity sink (`onActivity`) joins the host-callback isolation primitive —
|
|
44
|
+
a throwing sink previously failed the leg (misclassified `provider.error`) and, inside a cascade,
|
|
45
|
+
escalated a rung whose non-idempotent write had already run (measured: the write ran twice). Now
|
|
46
|
+
contained: swallow + first-failure disclosure + per-site counts.
|
|
47
|
+
|
|
48
|
+
### Docs
|
|
49
|
+
|
|
50
|
+
- The blackbox gate leaves the release tiers (its leg is owned by the independent black-box
|
|
51
|
+
acceptance institution; the script remains a dev-time self-check).
|
|
52
|
+
- Six live standing-leg env-gated skips join the skip baseline (historical omission; union merge,
|
|
53
|
+
zero removals).
|
|
54
|
+
|
|
3
55
|
## 5.33.0 — 2026-08-14
|
|
4
56
|
|
|
5
57
|
### BREAKING (operational)
|
|
@@ -3,7 +3,10 @@ export declare class PushQueue<T> implements AsyncIterable<T> {
|
|
|
3
3
|
private buffer;
|
|
4
4
|
private waiters;
|
|
5
5
|
private closed;
|
|
6
|
-
|
|
6
|
+
/** Returns whether the value was ACCEPTED (`false` after close — the push is a silent no-op then).
|
|
7
|
+
* Callers that mirror a pushed value to a second reader gate on this so both sides stay in parity
|
|
8
|
+
* (#253: a status frame must not reach a side sink the stream itself already refused). */
|
|
9
|
+
push(value: T): boolean;
|
|
7
10
|
close(): void;
|
|
8
11
|
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
9
12
|
}
|
package/dist/core/push-queue.js
CHANGED
|
@@ -4,7 +4,7 @@ export class PushQueue {
|
|
|
4
4
|
closed = false;
|
|
5
5
|
push(value) {
|
|
6
6
|
if (this.closed) {
|
|
7
|
-
return;
|
|
7
|
+
return false;
|
|
8
8
|
}
|
|
9
9
|
const waiter = this.waiters.shift();
|
|
10
10
|
if (waiter) {
|
|
@@ -13,6 +13,7 @@ export class PushQueue {
|
|
|
13
13
|
else {
|
|
14
14
|
this.buffer.push(value);
|
|
15
15
|
}
|
|
16
|
+
return true;
|
|
16
17
|
}
|
|
17
18
|
close() {
|
|
18
19
|
this.closed = true;
|
|
@@ -1285,6 +1285,19 @@ export interface RunInternals {
|
|
|
1285
1285
|
* MODEL context (this is purely a render channel). Absent unless the deployment opted in.
|
|
1286
1286
|
*/
|
|
1287
1287
|
onForwardEvent?: (event: TaskEvent) => void;
|
|
1288
|
+
/**
|
|
1289
|
+
* #253 — the run's OWN top-level `status` TaskEvent stream (brain liveness: rate-limit/retry/
|
|
1290
|
+
* reconnect/circuit-open), offered to the internals holder beside the queue. The queue alone was
|
|
1291
|
+
* enough for a direct `runTask` caller (the TaskStream carries these frames), but a COMPOSITION
|
|
1292
|
+
* entry (verify/cascade) drains its inner legs' queues itself — without this seat, an inner leg's
|
|
1293
|
+
* retry disclosure died inside the gate and the wire showed a silent stall. Fed the SAME frame
|
|
1294
|
+
* object the queue receives, at the same moment; contained by the run's safe notifier (#248 form:
|
|
1295
|
+
* a throwing sink is swallowed, first failure per site disclosed, never faults the leg). Subagent
|
|
1296
|
+
* frames still ride {@link onForwardEvent} — this seat is ONLY the run's own status type.
|
|
1297
|
+
*/
|
|
1298
|
+
onStatusEvent?: (event: Extract<TaskEvent, {
|
|
1299
|
+
type: "status";
|
|
1300
|
+
}>) => void;
|
|
1288
1301
|
/**
|
|
1289
1302
|
* design/115 P2 core slice — trusted run-local system-injection sink. `Runner.runLocked` wires this to the
|
|
1290
1303
|
* live TaskStream queue plus the current harness follow-up lane; it is not a public TaskSpec field.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { persistedReadDenyEntryProblem } from "../../tools/fs/read-deny.js";
|
|
2
|
-
import { createSafeNotifier } from "../safe-notify.js";
|
|
2
|
+
import { createSafeNotifier, observeThenableRejection } from "../safe-notify.js";
|
|
3
3
|
import { AgentHarness, DEFAULT_COMPACTION_SETTINGS, uuidv7 } from "../../internal/harness.js";
|
|
4
4
|
import { snapshotActorAssertion } from "../../internal/llm.js";
|
|
5
5
|
import { CheckpointError, BINDING_CHECKPOINT_VERSION, checkpointVersionOf, F012_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, FACE_CHECKPOINT_VERSION, remainingBudgetMicroUsd, readPendingSteerQueue, remainingTokens, validatePendingSteer, winnerFromOutcome, } from "../checkpoint-store.js";
|
|
@@ -1154,7 +1154,7 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
1154
1154
|
rs.counters.groundingSignalPostR9 = true;
|
|
1155
1155
|
}
|
|
1156
1156
|
const activityArg = primaryActivityArg(event.args);
|
|
1157
|
-
internalsNotifier.notify(() => internals?.onActivity?.({ phase: "start", toolCallId: event.toolCallId, toolName: event.toolName, at: startAt, ...(activityArg !== undefined ? { arg: activityArg } : {}) }), "runtask.onActivity.start");
|
|
1157
|
+
internalsNotifier.notify(() => observeThenableRejection(internals?.onActivity?.({ phase: "start", toolCallId: event.toolCallId, toolName: event.toolName, at: startAt, ...(activityArg !== undefined ? { arg: activityArg } : {}) }), internalsNotifier, "runtask.onActivity.start"), "runtask.onActivity.start");
|
|
1158
1158
|
pushContent({
|
|
1159
1159
|
type: "tool_start",
|
|
1160
1160
|
toolCallId: event.toolCallId,
|
|
@@ -1203,7 +1203,7 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
1203
1203
|
ok: !event.isError,
|
|
1204
1204
|
ts: toolNow,
|
|
1205
1205
|
}));
|
|
1206
|
-
internalsNotifier.notify(() => internals?.onActivity?.({ phase: "end", toolCallId: event.toolCallId, toolName: event.toolName, isError: event.isError, at: toolNow }), "runtask.onActivity.end");
|
|
1206
|
+
internalsNotifier.notify(() => observeThenableRejection(internals?.onActivity?.({ phase: "end", toolCallId: event.toolCallId, toolName: event.toolName, isError: event.isError, at: toolNow }), internalsNotifier, "runtask.onActivity.end"), "runtask.onActivity.end");
|
|
1207
1207
|
if (prepared.lspDiagnostics && !event.isError) {
|
|
1208
1208
|
const name = event.toolName;
|
|
1209
1209
|
if (name === "Edit" || name === "Write" || name === "NotebookEdit") {
|
|
@@ -2329,8 +2329,11 @@ export class Runner {
|
|
|
2329
2329
|
queue.push({ type: "message_committed", entryId, role, ...(toolCallId !== undefined ? { toolCallId } : {}), ...ident() });
|
|
2330
2330
|
};
|
|
2331
2331
|
const subagentName = parentToolCallId !== undefined && internals?.agentName !== undefined ? inlineUntrusted(internals.agentName.slice(0, 320), 80) : undefined;
|
|
2332
|
+
const statusSinkNotifier = createSafeNotifier({
|
|
2333
|
+
onError: (f) => console.warn(`[sema-core] ${f.site}: run-internals status sink threw (contained; further failures counted, not re-disclosed): ${f.error.message}`),
|
|
2334
|
+
});
|
|
2332
2335
|
const statusEmit = (s) => {
|
|
2333
|
-
|
|
2336
|
+
const frame = {
|
|
2334
2337
|
type: "status",
|
|
2335
2338
|
phase: s.phase,
|
|
2336
2339
|
...(s.detail !== undefined ? { detail: s.detail } : {}),
|
|
@@ -2340,7 +2343,12 @@ export class Runner {
|
|
|
2340
2343
|
...(s.maxRetries !== undefined ? { maxRetries: s.maxRetries } : {}),
|
|
2341
2344
|
...(s.errClass !== undefined ? { errClass: s.errClass } : {}),
|
|
2342
2345
|
...ident(),
|
|
2343
|
-
}
|
|
2346
|
+
};
|
|
2347
|
+
Object.freeze(frame);
|
|
2348
|
+
const accepted = queue.push(frame);
|
|
2349
|
+
if (accepted && internals?.onStatusEvent !== undefined) {
|
|
2350
|
+
statusSinkNotifier.notify(() => observeThenableRejection(internals.onStatusEvent?.(frame), statusSinkNotifier, "runtask.onStatusEvent"), "runtask.onStatusEvent");
|
|
2351
|
+
}
|
|
2344
2352
|
};
|
|
2345
2353
|
const telemetryEmit = (t) => {
|
|
2346
2354
|
emitTrace(rs.telemetry.tracer, () => t.kind === "failover"
|
|
@@ -79,3 +79,12 @@ export interface SafeNotifier {
|
|
|
79
79
|
* notifier whose callbacks never throw costs one object and nothing per call beyond the `try`.
|
|
80
80
|
*/
|
|
81
81
|
export declare function createSafeNotifier(opts?: SafeNotifierOptions): SafeNotifier;
|
|
82
|
+
/**
|
|
83
|
+
* Observe a `=> void` host callback's RETURN value for the async dialect: TypeScript accepts an
|
|
84
|
+
* `async` function at a void seat, and a rejected one would escape {@link SafeNotifier.notify}
|
|
85
|
+
* (which deliberately ignores return values — see the scope note above) as an unhandled rejection.
|
|
86
|
+
* Call this with the callback's return value INSIDE the notify thunk: a thenable's rejection is
|
|
87
|
+
* routed back through the same notifier/site, so async and sync failures share one count and one
|
|
88
|
+
* bounded disclosure, and neither can fault the engine's control flow.
|
|
89
|
+
*/
|
|
90
|
+
export declare function observeThenableRejection(r: unknown, notifier: SafeNotifier, site: string): void;
|
package/dist/core/safe-notify.js
CHANGED
|
@@ -57,3 +57,12 @@ export function createSafeNotifier(opts) {
|
|
|
57
57
|
},
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
+
export function observeThenableRejection(r, notifier, site) {
|
|
61
|
+
if (typeof r?.then === "function") {
|
|
62
|
+
r.then(undefined, (err) => {
|
|
63
|
+
notifier.notify(() => {
|
|
64
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
65
|
+
}, site);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/core",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.34.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",
|