@code-yeongyu/senpi-agent-core 2026.10.27-1 → 2026.10.30-1

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 (72) hide show
  1. package/dist/agent-loop.js +1 -0
  2. package/dist/agent.js +3 -1
  3. package/dist/assistant-terminal-state.js +5 -2
  4. package/dist/conformance/index.d.ts +42 -0
  5. package/dist/conformance/index.js +23 -0
  6. package/dist/conformance/mapping.d.ts +24 -0
  7. package/dist/conformance/mapping.js +62 -0
  8. package/dist/conformance/registry.d.ts +15 -0
  9. package/dist/conformance/registry.js +75 -0
  10. package/dist/conformance/runner.d.ts +27 -0
  11. package/dist/conformance/runner.js +23 -0
  12. package/dist/conformance/suite-hash.d.ts +12 -0
  13. package/dist/conformance/suite-hash.js +20 -0
  14. package/dist/durable/store-admission.d.ts +7 -0
  15. package/dist/durable/store-admission.js +11 -0
  16. package/dist/durable/store-memory.d.ts +15 -0
  17. package/dist/durable/store-memory.js +140 -0
  18. package/dist/durable/store.d.ts +116 -0
  19. package/dist/durable/store.js +14 -0
  20. package/dist/durable/testing/kill-store.d.ts +13 -0
  21. package/dist/durable/testing/kill-store.js +77 -0
  22. package/dist/durable/testing/kill-wake.d.ts +12 -0
  23. package/dist/durable/testing/kill-wake.js +123 -0
  24. package/dist/durable/testing/manual-timers.d.ts +8 -0
  25. package/dist/durable/testing/manual-timers.js +31 -0
  26. package/dist/durable/testing/multi-host-cases.d.ts +6 -0
  27. package/dist/durable/testing/multi-host-cases.js +32 -0
  28. package/dist/durable/testing/store-cases.d.ts +21 -0
  29. package/dist/durable/testing/store-cases.js +168 -0
  30. package/dist/durable/testing/store-harness.d.ts +23 -0
  31. package/dist/durable/testing/store-harness.js +64 -0
  32. package/dist/durable/testing/wake-cases.d.ts +6 -0
  33. package/dist/durable/testing/wake-cases.js +100 -0
  34. package/dist/durable/testing/wake-harness.d.ts +64 -0
  35. package/dist/durable/testing/wake-harness.js +127 -0
  36. package/dist/durable/testing/wake-test-loop.d.ts +13 -0
  37. package/dist/durable/testing/wake-test-loop.js +40 -0
  38. package/dist/durable/wake-host-bun-timer.d.ts +31 -0
  39. package/dist/durable/wake-host-bun-timer.js +66 -0
  40. package/dist/durable/wake-host-store-alarm.d.ts +13 -0
  41. package/dist/durable/wake-host-store-alarm.js +20 -0
  42. package/dist/durable/wake-host.d.ts +68 -0
  43. package/dist/durable/wake-host.js +70 -0
  44. package/dist/harness/pico3/kinds/generation.js +3 -1
  45. package/dist/harness/utils/read-folders/index.d.ts +1 -1
  46. package/dist/harness/utils/read-folders/index.js +1 -1
  47. package/dist/harness/utils/read-folders/tree-sitter/engine.d.ts +1 -1
  48. package/dist/harness/utils/read-folders/tree-sitter/engine.js +38 -19
  49. package/dist/internal-events.d.ts +1 -0
  50. package/dist/internal-events.js +1 -0
  51. package/dist/testing/win32-sim.d.ts +76 -0
  52. package/dist/testing/win32-sim.js +197 -0
  53. package/dist/transport/in-process-carrier.d.ts +19 -0
  54. package/dist/transport/in-process-carrier.js +63 -0
  55. package/dist/transport/local-endpoint.d.ts +13 -0
  56. package/dist/transport/local-endpoint.js +20 -0
  57. package/dist/transport/reliable-transport.d.ts +40 -0
  58. package/dist/transport/reliable-transport.js +232 -0
  59. package/dist/transport/testing/fault-cases.d.ts +6 -0
  60. package/dist/transport/testing/fault-cases.js +30 -0
  61. package/dist/transport/testing/fault-transport.d.ts +44 -0
  62. package/dist/transport/testing/fault-transport.js +144 -0
  63. package/dist/transport/testing/multi-cases.d.ts +6 -0
  64. package/dist/transport/testing/multi-cases.js +113 -0
  65. package/dist/transport/testing/transport-cases.d.ts +6 -0
  66. package/dist/transport/testing/transport-cases.js +60 -0
  67. package/dist/transport/testing/transport-harness.d.ts +53 -0
  68. package/dist/transport/testing/transport-harness.js +127 -0
  69. package/dist/transport/transport.d.ts +60 -0
  70. package/dist/transport/transport.js +15 -0
  71. package/dist/types.d.ts +1 -0
  72. package/package.json +11 -3
@@ -0,0 +1,53 @@
1
+ import { type ManualTimers } from "../../durable/testing/manual-timers.ts";
2
+ import { type CarrierLink } from "../in-process-carrier.ts";
3
+ import type { CarrierSide, TransportClock, TransportSide } from "../transport.ts";
4
+ /** What a conformance host's decoder reports about one carrier message; data and ACK sequence numbers start at 1. */
5
+ export type WireView = {
6
+ readonly kind: "data" | "ack";
7
+ readonly lane: string;
8
+ readonly seq: number;
9
+ } | {
10
+ readonly kind: "other";
11
+ };
12
+ /** What a host provides to the TRANSPORT, MULTI-LANE/FAIR and FAULT-TRANSPORT cases. */
13
+ export interface TransportConformanceAdapter {
14
+ /** A new transport endpoint scheduling on `clock`; the cases attach two of them through an in-process carrier. */
15
+ createSide(clock: TransportClock): TransportSide;
16
+ /** Reads the lane and sequence number of a carrier message, so the cases can check resume from the last ACK. */
17
+ decode(message: unknown): WireView;
18
+ }
19
+ export interface TransportCaseAdapters {
20
+ readonly transport: TransportConformanceAdapter;
21
+ /** The platform whose local endpoint naming the link uses (`win32` names a pipe). */
22
+ readonly platform: string;
23
+ }
24
+ /** A fault on the carrier path: it wraps each side's view of the carrier and may drive the link. */
25
+ export interface CarrierFault {
26
+ wrap(side: CarrierSide): CarrierSide;
27
+ start(link: CarrierLink): void;
28
+ }
29
+ export interface TestLink {
30
+ readonly sender: TransportSide;
31
+ readonly link: CarrierLink;
32
+ readonly timers: ManualTimers;
33
+ /** Global delivery order, as `lane:n`. */
34
+ readonly log: readonly string[];
35
+ /** Every data frame sent again after the sender had seen its ACK. */
36
+ readonly violations: readonly string[];
37
+ send(lane: string, n: number): Promise<void>;
38
+ delivered(lane: string): readonly number[];
39
+ /** Holds the receiver on `lane` (from its next frame) until the returned release is called. */
40
+ gate(lane: string): () => void;
41
+ /** Makes the receiver take `ms` of transport time for each frame on `lane` (from its next frame). */
42
+ pace(lane: string, ms: number): void;
43
+ runUntil(done: () => boolean, what: string, budgetMs?: number): Promise<void>;
44
+ /** Advances transport time until every promise settles; a rejection fails the case. */
45
+ awaitAll(promises: readonly Promise<unknown>[], what: string): Promise<void>;
46
+ advance(ms: number): Promise<void>;
47
+ }
48
+ export declare function openLink(adapters: TransportCaseAdapters, fault?: (timers: ManualTimers) => CarrierFault): TestLink;
49
+ export declare const range: (from: number, to: number) => number[];
50
+ /** Every lane got exactly 1..count, in order, once; nothing is left unacknowledged; nothing acknowledged was re-sent. */
51
+ export declare function expectExactDelivery(link: TestLink, counts: Readonly<Record<string, number>>, what: string): void;
52
+ export declare const allDelivered: (link: TestLink, counts: Readonly<Record<string, number>>) => () => boolean;
53
+ //# sourceMappingURL=transport-harness.d.ts.map
@@ -0,0 +1,127 @@
1
+ import { createManualTimers } from "../../durable/testing/manual-timers.js";
2
+ import { expectEqual } from "../../durable/testing/store-harness.js";
3
+ import { createInProcessNetwork } from "../in-process-carrier.js";
4
+ import { localEndpoint } from "../local-endpoint.js";
5
+ const TICK_MS = 5;
6
+ const DRAIN_TURNS = 32;
7
+ const drainMicrotasks = async () => {
8
+ for (let turn = 0; turn < DRAIN_TURNS; turn++)
9
+ await Promise.resolve();
10
+ };
11
+ function tap(side, decode, violations) {
12
+ const acked = new Map();
13
+ return {
14
+ attached: (connection) => side.attached({
15
+ send: (message) => {
16
+ const view = decode(message);
17
+ if (view.kind === "data" && view.seq <= (acked.get(view.lane) ?? 0)) {
18
+ violations.push(`${view.lane}#${view.seq} sent again after its ACK`);
19
+ }
20
+ return connection.send(message);
21
+ },
22
+ close: () => connection.close(),
23
+ }),
24
+ message: (message) => {
25
+ const view = decode(message);
26
+ if (view.kind === "ack")
27
+ acked.set(view.lane, Math.max(acked.get(view.lane) ?? 0, view.seq));
28
+ side.message(message);
29
+ },
30
+ writable: () => side.writable(),
31
+ detached: () => side.detached(),
32
+ };
33
+ }
34
+ export function openLink(adapters, fault) {
35
+ const timers = createManualTimers(0);
36
+ const network = createInProcessNetwork({ schedule: (deliver) => void timers.setTimer(deliver, 0) });
37
+ const sender = adapters.transport.createSide(timers);
38
+ const receiver = adapters.transport.createSide(timers);
39
+ const violations = [];
40
+ const log = [];
41
+ const received = new Map();
42
+ const gates = new Map();
43
+ const paces = new Map();
44
+ const installed = fault?.(timers);
45
+ const outer = (side) => {
46
+ const tapped = tap(side, (message) => adapters.transport.decode(message), violations);
47
+ return installed === undefined ? tapped : installed.wrap(tapped);
48
+ };
49
+ const endpoint = localEndpoint(adapters.platform, "transport-conformance", "/run/senpi-conformance");
50
+ network.listen(endpoint, outer(receiver));
51
+ const link = network.dial(endpoint, outer(sender));
52
+ const decoder = new TextDecoder();
53
+ receiver.onFrame(async (frame) => {
54
+ await gates.get(frame.lane)?.promise;
55
+ const paceMs = paces.get(frame.lane);
56
+ if (paceMs !== undefined)
57
+ await new Promise((resolve) => void timers.setTimer(resolve, paceMs));
58
+ const n = Number(decoder.decode(frame.payload));
59
+ received.set(frame.lane, [...(received.get(frame.lane) ?? []), n]);
60
+ log.push(`${frame.lane}:${n}`);
61
+ });
62
+ link.connect();
63
+ installed?.start(link);
64
+ const advance = async (ms) => {
65
+ const end = timers.now() + ms;
66
+ while (timers.now() < end) {
67
+ await timers.advanceTo(Math.min(end, timers.now() + TICK_MS));
68
+ await drainMicrotasks();
69
+ }
70
+ };
71
+ const encoder = new TextEncoder();
72
+ const runUntil = async (done, what, budgetMs = 120_000) => {
73
+ const end = timers.now() + budgetMs;
74
+ while (!done()) {
75
+ if (timers.now() >= end) {
76
+ const counts = [...received].map(([lane, ns]) => `${lane}=${ns.length}`).join(" ");
77
+ throw new Error(`${what}: not reached within ${budgetMs} ms of transport time (delivered ${counts})`);
78
+ }
79
+ await advance(TICK_MS);
80
+ }
81
+ };
82
+ return {
83
+ sender,
84
+ link,
85
+ timers,
86
+ log,
87
+ violations,
88
+ send: (lane, n) => sender.send(lane, encoder.encode(String(n))),
89
+ delivered: (lane) => received.get(lane) ?? [],
90
+ gate(lane) {
91
+ const gate = Promise.withResolvers();
92
+ gates.set(lane, gate);
93
+ return () => {
94
+ gates.delete(lane);
95
+ gate.resolve();
96
+ };
97
+ },
98
+ pace(lane, ms) {
99
+ paces.set(lane, ms);
100
+ },
101
+ runUntil,
102
+ async awaitAll(promises, what) {
103
+ let settled = 0;
104
+ let failure;
105
+ for (const promise of promises) {
106
+ promise.then(() => void (settled += 1), (error) => {
107
+ settled += 1;
108
+ failure ??= error;
109
+ });
110
+ }
111
+ await runUntil(() => settled === promises.length, `${what}: every send settled`);
112
+ if (failure !== undefined)
113
+ throw failure;
114
+ },
115
+ advance,
116
+ };
117
+ }
118
+ export const range = (from, to) => Array.from({ length: to - from + 1 }, (_, i) => from + i);
119
+ /** Every lane got exactly 1..count, in order, once; nothing is left unacknowledged; nothing acknowledged was re-sent. */
120
+ export function expectExactDelivery(link, counts, what) {
121
+ const lanes = Object.keys(counts);
122
+ expectEqual(lanes.map((lane) => link.delivered(lane)), lanes.map((lane) => range(1, counts[lane] ?? 0)), `${what}: each lane delivered in order, once`);
123
+ expectEqual(lanes.map((lane) => link.sender.inFlight(lane)), lanes.map(() => 0), `${what}: nothing in flight`);
124
+ expectEqual(link.violations, [], `${what}: resume starts after each lane's last ACK`);
125
+ }
126
+ export const allDelivered = (link, counts) => () => Object.entries(counts).every(([lane, count]) => link.delivered(lane).length >= count);
127
+ //# sourceMappingURL=transport-harness.js.map
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The Transport contract of the durable core (packages/agent/docs/durable-core-adr.md, "Transport").
3
+ *
4
+ * A transport delivers opaque payloads between two endpoints on named lanes (one lane per scope). It owns per-lane order,
5
+ * exactly-once delivery of each frame while both endpoints live, resume after the carrier drops and re-attaches, and
6
+ * backpressure. Loops, hands, command IDs, epochs, deduplication of commands, consent, fencing and credentials are
7
+ * endpoint duties; a transport never inspects a payload.
8
+ */
9
+ /** One delivered frame: the lane it was sent on and its opaque payload. */
10
+ export interface TransportFrame {
11
+ readonly lane: string;
12
+ readonly payload: Uint8Array;
13
+ }
14
+ /** Receives frames. A lane's next frame is delivered only after this settles for the previous one; lanes are independent. */
15
+ export type FrameHandler = (frame: TransportFrame) => void | Promise<void>;
16
+ /** One endpoint of a transport link. */
17
+ export interface Transport {
18
+ /**
19
+ * Queues `payload` on `lane`. Resolves once the frame is inside the lane's send window, so a lane whose receiver falls
20
+ * behind holds its own senders and nobody else's. Rejects with {@link TransportClosedError} after `close()`.
21
+ */
22
+ send(lane: string, payload: Uint8Array): Promise<void>;
23
+ /** Sets the receiver. Frames that arrived earlier are delivered once it is set. */
24
+ onFrame(handler: FrameHandler): void;
25
+ /** Frames on `lane` that were sent and not yet acknowledged by the peer. */
26
+ inFlight(lane: string): number;
27
+ close(): void;
28
+ }
29
+ /**
30
+ * A message channel under a transport (in process, a local socket, the relay). It is ordered while it lasts, may drop
31
+ * at any time, and under faults may lose or delay messages. Messages are opaque to it.
32
+ */
33
+ export interface CarrierConnection {
34
+ /** Hands `message` to the carrier. `false` means it was not taken; the side waits for `writable()` and sends again. */
35
+ send(message: unknown): boolean;
36
+ close(): void;
37
+ }
38
+ /** What a carrier calls on the endpoint it serves. */
39
+ export interface CarrierSide {
40
+ /** A new connection is up (the first, or a re-attach after a drop). */
41
+ attached(connection: CarrierConnection): void;
42
+ message(message: unknown): void;
43
+ /** The current connection takes messages again after a refused `send`. */
44
+ writable(): void;
45
+ /** The current connection is gone; anything in flight on it is lost. */
46
+ detached(): void;
47
+ }
48
+ /** A transport endpoint as a carrier sees it. */
49
+ export interface TransportSide extends Transport, CarrierSide {
50
+ }
51
+ /** The clock a transport schedules on; tests pass manual timers. */
52
+ export interface TransportClock {
53
+ now(): number;
54
+ /** Runs `callback` after `ms`; the returned function cancels it. */
55
+ setTimer(callback: () => unknown, ms: number): () => void;
56
+ }
57
+ export declare class TransportClosedError extends Error {
58
+ constructor();
59
+ }
60
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The Transport contract of the durable core (packages/agent/docs/durable-core-adr.md, "Transport").
3
+ *
4
+ * A transport delivers opaque payloads between two endpoints on named lanes (one lane per scope). It owns per-lane order,
5
+ * exactly-once delivery of each frame while both endpoints live, resume after the carrier drops and re-attaches, and
6
+ * backpressure. Loops, hands, command IDs, epochs, deduplication of commands, consent, fencing and credentials are
7
+ * endpoint duties; a transport never inspects a payload.
8
+ */
9
+ export class TransportClosedError extends Error {
10
+ constructor() {
11
+ super("transport closed");
12
+ this.name = "TransportClosedError";
13
+ }
14
+ }
15
+ //# sourceMappingURL=transport.js.map
package/dist/types.d.ts CHANGED
@@ -546,6 +546,7 @@ export type AgentEvent = {
546
546
  } | {
547
547
  type: "message_start";
548
548
  message: AgentMessage;
549
+ streaming?: true;
549
550
  } | {
550
551
  type: "message_update";
551
552
  message: AgentMessage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-agent-core",
3
- "version": "2026.10.27-1",
3
+ "version": "2026.10.30-1",
4
4
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -14,6 +14,10 @@
14
14
  "types": "./dist/node.d.ts",
15
15
  "import": "./dist/node.js"
16
16
  },
17
+ "./conformance": {
18
+ "types": "./dist/conformance/index.d.ts",
19
+ "import": "./dist/conformance/index.js"
20
+ },
17
21
  "./harness/context": {
18
22
  "types": "./dist/harness/context.d.ts",
19
23
  "import": "./dist/harness/context.js"
@@ -38,6 +42,10 @@
38
42
  "types": "./dist/harness/session/testing/index.d.ts",
39
43
  "import": "./dist/harness/session/testing/index.js"
40
44
  },
45
+ "./testing/win32-sim": {
46
+ "types": "./dist/testing/win32-sim.d.ts",
47
+ "import": "./dist/testing/win32-sim.js"
48
+ },
41
49
  "./package.json": "./package.json"
42
50
  },
43
51
  "files": [
@@ -62,8 +70,8 @@
62
70
  },
63
71
  "dependencies": {
64
72
  "@earendil-works/chord": "0.99.1",
65
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.27-1",
66
- "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.27-1",
73
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.30-1",
74
+ "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.30-1",
67
75
  "diff": "9.0.0",
68
76
  "ignore": "7.0.9",
69
77
  "typebox": "1.3.34",