@acquiredintelligence/attend 0.0.0-stage → 0.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.
- package/LICENSE +21 -0
- package/README.md +580 -2
- package/dist/attend.d.ts +30 -0
- package/dist/attend.d.ts.map +1 -0
- package/dist/attend.js +428 -0
- package/dist/attend.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/ids.d.ts +12 -0
- package/dist/core/ids.d.ts.map +1 -0
- package/dist/core/ids.js +97 -0
- package/dist/core/ids.js.map +1 -0
- package/dist/core/reducer.d.ts +26 -0
- package/dist/core/reducer.d.ts.map +1 -0
- package/dist/core/reducer.js +437 -0
- package/dist/core/reducer.js.map +1 -0
- package/dist/core/seen.d.ts +22 -0
- package/dist/core/seen.d.ts.map +1 -0
- package/dist/core/seen.js +61 -0
- package/dist/core/seen.js.map +1 -0
- package/dist/core/state.d.ts +12 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +90 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/step.d.ts +14 -0
- package/dist/core/step.d.ts.map +1 -0
- package/dist/core/step.js +17 -0
- package/dist/core/step.js.map +1 -0
- package/dist/core/threshold.d.ts +40 -0
- package/dist/core/threshold.d.ts.map +1 -0
- package/dist/core/threshold.js +72 -0
- package/dist/core/threshold.js.map +1 -0
- package/dist/core/types.d.ts +704 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +24 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/window.d.ts +38 -0
- package/dist/core/window.d.ts.map +1 -0
- package/dist/core/window.js +322 -0
- package/dist/core/window.js.map +1 -0
- package/dist/decisions.d.ts +53 -0
- package/dist/decisions.d.ts.map +1 -0
- package/dist/decisions.js +115 -0
- package/dist/decisions.js.map +1 -0
- package/dist/errors.d.ts +36 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +53 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/adapter.d.ts +21 -0
- package/dist/internal/adapter.d.ts.map +1 -0
- package/dist/internal/adapter.js +62 -0
- package/dist/internal/adapter.js.map +1 -0
- package/dist/internal/assert.d.ts +5 -0
- package/dist/internal/assert.d.ts.map +1 -0
- package/dist/internal/assert.js +14 -0
- package/dist/internal/assert.js.map +1 -0
- package/dist/internal/clock.d.ts +8 -0
- package/dist/internal/clock.d.ts.map +1 -0
- package/dist/internal/clock.js +38 -0
- package/dist/internal/clock.js.map +1 -0
- package/dist/internal/http.d.ts +42 -0
- package/dist/internal/http.d.ts.map +1 -0
- package/dist/internal/http.js +122 -0
- package/dist/internal/http.js.map +1 -0
- package/dist/internal/images.d.ts +18 -0
- package/dist/internal/images.d.ts.map +1 -0
- package/dist/internal/images.js +75 -0
- package/dist/internal/images.js.map +1 -0
- package/dist/internal/json.d.ts +3 -0
- package/dist/internal/json.d.ts.map +1 -0
- package/dist/internal/json.js +5 -0
- package/dist/internal/json.js.map +1 -0
- package/dist/internal/prompt.d.ts +31 -0
- package/dist/internal/prompt.d.ts.map +1 -0
- package/dist/internal/prompt.js +149 -0
- package/dist/internal/prompt.js.map +1 -0
- package/dist/internal/shared.d.ts +12 -0
- package/dist/internal/shared.d.ts.map +1 -0
- package/dist/internal/shared.js +134 -0
- package/dist/internal/shared.js.map +1 -0
- package/dist/jev.d.ts +74 -0
- package/dist/jev.d.ts.map +1 -0
- package/dist/jev.js +156 -0
- package/dist/jev.js.map +1 -0
- package/dist/llm.d.ts +164 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +79 -0
- package/dist/llm.js.map +1 -0
- package/dist/mcp.d.ts +130 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +550 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory.d.ts +10 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +50 -0
- package/dist/memory.js.map +1 -0
- package/dist/postgres.d.ts +25 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +56 -0
- package/dist/postgres.js.map +1 -0
- package/dist/redis.d.ts +20 -0
- package/dist/redis.d.ts.map +1 -0
- package/dist/redis.js +83 -0
- package/dist/redis.js.map +1 -0
- package/dist/server/compat.d.ts +13 -0
- package/dist/server/compat.d.ts.map +1 -0
- package/dist/server/compat.js +5 -0
- package/dist/server/compat.js.map +1 -0
- package/dist/server/cursor.d.ts +15 -0
- package/dist/server/cursor.d.ts.map +1 -0
- package/dist/server/cursor.js +50 -0
- package/dist/server/cursor.js.map +1 -0
- package/dist/server/events.d.ts +42 -0
- package/dist/server/events.d.ts.map +1 -0
- package/dist/server/events.js +189 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/fanout.d.ts +28 -0
- package/dist/server/fanout.d.ts.map +1 -0
- package/dist/server/fanout.js +184 -0
- package/dist/server/fanout.js.map +1 -0
- package/dist/server/guard.d.ts +19 -0
- package/dist/server/guard.d.ts.map +1 -0
- package/dist/server/guard.js +27 -0
- package/dist/server/guard.js.map +1 -0
- package/dist/server/http.d.ts +4 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +323 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/ingest.d.ts +4 -0
- package/dist/server/ingest.d.ts.map +1 -0
- package/dist/server/ingest.js +400 -0
- package/dist/server/ingest.js.map +1 -0
- package/dist/server/install.d.ts +5 -0
- package/dist/server/install.d.ts.map +1 -0
- package/dist/server/install.js +91 -0
- package/dist/server/install.js.map +1 -0
- package/dist/server/poll.d.ts +4 -0
- package/dist/server/poll.d.ts.map +1 -0
- package/dist/server/poll.js +45 -0
- package/dist/server/poll.js.map +1 -0
- package/dist/server/serve.d.ts +19 -0
- package/dist/server/serve.d.ts.map +1 -0
- package/dist/server/serve.js +238 -0
- package/dist/server/serve.js.map +1 -0
- package/dist/server/sessions.d.ts +14 -0
- package/dist/server/sessions.d.ts.map +1 -0
- package/dist/server/sessions.js +54 -0
- package/dist/server/sessions.js.map +1 -0
- package/dist/server/stream.d.ts +11 -0
- package/dist/server/stream.d.ts.map +1 -0
- package/dist/server/stream.js +216 -0
- package/dist/server/stream.js.map +1 -0
- package/dist/server/tools.d.ts +22 -0
- package/dist/server/tools.d.ts.map +1 -0
- package/dist/server/tools.js +55 -0
- package/dist/server/tools.js.map +1 -0
- package/dist/server/types.d.ts +351 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +5 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +10 -0
- package/dist/server.js.map +1 -0
- package/dist/shell/budget.d.ts +4 -0
- package/dist/shell/budget.d.ts.map +1 -0
- package/dist/shell/budget.js +5 -0
- package/dist/shell/budget.js.map +1 -0
- package/dist/shell/copy.d.ts +17 -0
- package/dist/shell/copy.d.ts.map +1 -0
- package/dist/shell/copy.js +67 -0
- package/dist/shell/copy.js.map +1 -0
- package/dist/shell/decisions.d.ts +6 -0
- package/dist/shell/decisions.d.ts.map +1 -0
- package/dist/shell/decisions.js +42 -0
- package/dist/shell/decisions.js.map +1 -0
- package/dist/shell/read.d.ts +7 -0
- package/dist/shell/read.d.ts.map +1 -0
- package/dist/shell/read.js +21 -0
- package/dist/shell/read.js.map +1 -0
- package/dist/shell/scheduler.d.ts +4 -0
- package/dist/shell/scheduler.d.ts.map +1 -0
- package/dist/shell/scheduler.js +90 -0
- package/dist/shell/scheduler.js.map +1 -0
- package/dist/shell/scope.d.ts +27 -0
- package/dist/shell/scope.d.ts.map +1 -0
- package/dist/shell/scope.js +343 -0
- package/dist/shell/scope.js.map +1 -0
- package/dist/shell/types.d.ts +156 -0
- package/dist/shell/types.d.ts.map +1 -0
- package/dist/shell/types.js +2 -0
- package/dist/shell/types.js.map +1 -0
- package/dist/sqlite.d.ts +14 -0
- package/dist/sqlite.d.ts.map +1 -0
- package/dist/sqlite.js +39 -0
- package/dist/sqlite.js.map +1 -0
- package/dist/step.d.ts +18 -0
- package/dist/step.d.ts.map +1 -0
- package/dist/step.js +14 -0
- package/dist/step.js.map +1 -0
- package/dist/store/memory.d.ts +12 -0
- package/dist/store/memory.d.ts.map +1 -0
- package/dist/store/memory.js +264 -0
- package/dist/store/memory.js.map +1 -0
- package/dist/store/postgres.d.ts +24 -0
- package/dist/store/postgres.d.ts.map +1 -0
- package/dist/store/postgres.js +364 -0
- package/dist/store/postgres.js.map +1 -0
- package/dist/store/redis.d.ts +12 -0
- package/dist/store/redis.d.ts.map +1 -0
- package/dist/store/redis.js +540 -0
- package/dist/store/redis.js.map +1 -0
- package/dist/store/sqlite-worker.d.ts +91 -0
- package/dist/store/sqlite-worker.d.ts.map +1 -0
- package/dist/store/sqlite-worker.js +322 -0
- package/dist/store/sqlite-worker.js.map +1 -0
- package/dist/store/sqlite.d.ts +34 -0
- package/dist/store/sqlite.d.ts.map +1 -0
- package/dist/store/sqlite.js +238 -0
- package/dist/store/sqlite.js.map +1 -0
- package/dist/store/types.d.ts +137 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +2 -0
- package/dist/store/types.js.map +1 -0
- package/dist/testing/conformance.d.ts +13 -0
- package/dist/testing/conformance.d.ts.map +1 -0
- package/dist/testing/conformance.js +542 -0
- package/dist/testing/conformance.js.map +1 -0
- package/dist/testing/wire.d.ts +16 -0
- package/dist/testing/wire.d.ts.map +1 -0
- package/dist/testing/wire.js +304 -0
- package/dist/testing/wire.js.map +1 -0
- package/dist/testing.d.ts +113 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +655 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +130 -4
- package/skills/attend/SKILL.md +231 -0
- package/skills/attend/references/judges.md +283 -0
- package/skills/attend/references/kinds.md +62 -0
- package/skills/attend/references/mcp.md +132 -0
- package/skills/attend/references/sinks.md +73 -0
- package/skills/attend/references/stores.md +90 -0
- package/skills/attend/references/testing.md +120 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
`@acquiredintelligence/attend/testing` makes settling deterministic and runs with any test runner. These examples use `node:test`.
|
|
4
|
+
|
|
5
|
+
## Set up a scope on a fake clock
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import assert from 'node:assert/strict';
|
|
9
|
+
import { test } from 'node:test';
|
|
10
|
+
import { createAttend, DEFAULT_POLICY, memoryStore, type DeadLetter, type Event, type Kind } from '@acquiredintelligence/attend';
|
|
11
|
+
import { fakeClock, recordingSink, scriptedJudge, type ScriptedAnswer } from '@acquiredintelligence/attend/testing';
|
|
12
|
+
|
|
13
|
+
const kind = (event: Event): Kind => {
|
|
14
|
+
if (event.name === 'user.message') return 'flush';
|
|
15
|
+
if (event.name === 'build.finished') return 'closing';
|
|
16
|
+
return 'consequence';
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function setup(script: ScriptedAnswer[]) {
|
|
20
|
+
const clock = fakeClock();
|
|
21
|
+
const judge = scriptedJudge(script);
|
|
22
|
+
const sink = recordingSink();
|
|
23
|
+
const letters: DeadLetter[] = [];
|
|
24
|
+
const attend = createAttend({ store: memoryStore(), kind, judge, sink, clock, deadLetter: (letter) => void letters.push(letter) });
|
|
25
|
+
const controller = new AbortController();
|
|
26
|
+
const running = attend.run({ signal: controller.signal });
|
|
27
|
+
const stop = async () => {
|
|
28
|
+
controller.abort();
|
|
29
|
+
await running;
|
|
30
|
+
};
|
|
31
|
+
return { clock, judge, sink, letters, attend, stop };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test('a closing event settles exactly at the grace', async () => {
|
|
35
|
+
const { clock, judge, sink, attend, stop } = setup([{ route: 'queue' }]);
|
|
36
|
+
await attend.push('repo', [{ id: 'done-1', at: 0, name: 'build.finished', data: {} }]);
|
|
37
|
+
await clock.advance(DEFAULT_POLICY.closingGraceMs - 1);
|
|
38
|
+
assert.equal(judge.calls.length, 0);
|
|
39
|
+
await clock.advance(1);
|
|
40
|
+
assert.deepEqual(sink.delivered.map((d) => d.route), ['queue']);
|
|
41
|
+
await stop();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('a flush settles at once and is never offered wait', async () => {
|
|
45
|
+
const { clock, judge, attend, stop } = setup([{ route: 'interrupt' }]);
|
|
46
|
+
await attend.push('repo', [{ id: 'msg-1', at: 0, name: 'user.message', data: {} }]);
|
|
47
|
+
await clock.advance(0);
|
|
48
|
+
assert.deepEqual(judge.calls[0]?.routes.map((r) => r.route), ['interrupt', 'queue', 'note', 'ask', 'skip']);
|
|
49
|
+
await stop();
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- Start `run()` before you push. Timers arm only while the runner runs.
|
|
54
|
+
- `clock.advance(ms)` fires every timer due on the way, in order, and lets each one's work finish. A flush is due at once, so `advance(0)` fires it.
|
|
55
|
+
- Take deadlines from `DEFAULT_POLICY`, or from your own `settle` values, and assert that nothing happens one millisecond early.
|
|
56
|
+
- Pass the same clock to `mcpEventsSource` and `logSource` through their `clock` option.
|
|
57
|
+
|
|
58
|
+
## Script the judge
|
|
59
|
+
|
|
60
|
+
`scriptedJudge(script)` answers in order, repeats its last answer, and records every `JudgeInput` in `calls`. An answer is one of:
|
|
61
|
+
|
|
62
|
+
- a judgment, such as `{ route: 'wait' }` or `{ route: 'note', predicates: { asked: 0.9 } }`;
|
|
63
|
+
- `{ fail: 'message' }`, which throws a retryable error;
|
|
64
|
+
- `{ fail: 'message', permanent: true }`, which throws `PermanentError`;
|
|
65
|
+
- `{ hang: true }`, which never answers and rejects when attend aborts it at `judgeTimeoutMs`.
|
|
66
|
+
|
|
67
|
+
Assert on `calls[i].window.events`, `calls[i].window.reason` and `calls[i].routes`.
|
|
68
|
+
|
|
69
|
+
## Record the sink
|
|
70
|
+
|
|
71
|
+
`recordingSink()` collects `delivered` decisions and counts `attempts` per decision id. `failNext(n)` makes the next `n` deliveries throw, and `failNext(n, true)` makes them throw `PermanentError`. A decision gets `1 + retryBackoffMs.length` attempts before it becomes a dead letter.
|
|
72
|
+
|
|
73
|
+
## Restarts and crashes
|
|
74
|
+
|
|
75
|
+
`harness(config, options)` runs attend on one scope with a fake clock over a store it can crash at any commit. `config` is the `createAttend` config without `store`, `clock` and `deadLetter`. The harness records dead letters in `h.deadLetters` and traces in `h.traces`.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import assert from 'node:assert/strict';
|
|
79
|
+
import { test } from 'node:test';
|
|
80
|
+
import { DEFAULT_POLICY, type Event, type Kind } from '@acquiredintelligence/attend';
|
|
81
|
+
import { CrashError, harness, recordingSink, scriptedJudge } from '@acquiredintelligence/attend/testing';
|
|
82
|
+
|
|
83
|
+
const kind = (event: Event): Kind => (event.name === 'build.finished' ? 'closing' : 'consequence');
|
|
84
|
+
|
|
85
|
+
test('a decision stranded by a crash is delivered once after the restart', async () => {
|
|
86
|
+
const { deliveryTimeoutMs, retryBackoffMs: [firstRetry = 0] } = DEFAULT_POLICY;
|
|
87
|
+
const sink = recordingSink();
|
|
88
|
+
const h = harness({ judge: scriptedJudge([{ route: 'queue' }]), sink, kind });
|
|
89
|
+
await h.push([{ id: 'done-1', at: 0, name: 'build.finished', data: {} }]);
|
|
90
|
+
h.crashAt(2, 'after');
|
|
91
|
+
await assert.rejects(h.advance(DEFAULT_POLICY.closingGraceMs), CrashError);
|
|
92
|
+
await h.restart();
|
|
93
|
+
assert.equal(sink.delivered.length, 0);
|
|
94
|
+
await h.advance(deliveryTimeoutMs + firstRetry);
|
|
95
|
+
assert.deepEqual(sink.delivered.map((d) => d.route), ['queue']);
|
|
96
|
+
await h.stop();
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `h.push` and `h.advance` wait until the runner is idle, and `h.flush` also lets the runner's tick settle the flush. All three throw `CrashError` when a scheduled crash strikes.
|
|
101
|
+
- `h.crashAt(n, 'before' | 'after')` crashes at the nth commit from now. `before` writes nothing. `after` writes the record but runs none of its effects.
|
|
102
|
+
- `h.crash()` kills the runner now. `h.restart()` starts a fresh runner over the same store and lets it recover.
|
|
103
|
+
- The judge, the sink and `context` must resolve without real time, or `h.idle()` cannot see them finish.
|
|
104
|
+
|
|
105
|
+
## Adapter suites
|
|
106
|
+
|
|
107
|
+
To test your own `ScopeStore` or `EventLog`, register the suites with your runner:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { test } from 'node:test';
|
|
111
|
+
import { memoryStore } from '@acquiredintelligence/attend';
|
|
112
|
+
import { crashSuite, multiScopeCrashSuite, scopeStoreConformance, decisionLogConformance } from '@acquiredintelligence/attend/testing';
|
|
113
|
+
|
|
114
|
+
for (const c of scopeStoreConformance(() => memoryStore())) test(c.name, c.run);
|
|
115
|
+
for (const c of crashSuite(() => memoryStore())) test(c.name, c.run);
|
|
116
|
+
for (const c of decisionLogConformance(() => memoryStore())) test(c.name, c.run);
|
|
117
|
+
for (const c of multiScopeCrashSuite(() => memoryStore())) test(c.name, c.run);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Replace the memory adapters with factories for yours. Each factory call must return a fresh, empty adapter: for a database, use a new table or key prefix per call. Set `canBlock` to whether `read` waits for `blockMs`.
|