@earendil-works/pi-agent-core 0.85.1 → 0.86.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.
- package/README.md +16 -6
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +64 -22
- package/dist/agent-loop.js.map +1 -1
- package/dist/agent.d.ts +5 -3
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +15 -6
- package/dist/agent.js.map +1 -1
- package/dist/harness/config.d.ts +1 -1
- package/dist/harness/config.d.ts.map +1 -1
- package/dist/harness/config.js +10 -2
- package/dist/harness/config.js.map +1 -1
- package/dist/harness/env/nodejs.d.ts +2 -1
- package/dist/harness/env/nodejs.d.ts.map +1 -1
- package/dist/harness/env/nodejs.js +98 -27
- package/dist/harness/env/nodejs.js.map +1 -1
- package/dist/harness/execution/tools.d.ts +1 -1
- package/dist/harness/execution/tools.d.ts.map +1 -1
- package/dist/harness/execution/tools.js +0 -2
- package/dist/harness/execution/tools.js.map +1 -1
- package/dist/harness/messages.d.ts.map +1 -1
- package/dist/harness/messages.js +1 -0
- package/dist/harness/messages.js.map +1 -1
- package/dist/harness/pico3/bash.d.ts +10 -0
- package/dist/harness/pico3/bash.d.ts.map +1 -0
- package/dist/harness/pico3/bash.js +25 -0
- package/dist/harness/pico3/bash.js.map +1 -0
- package/dist/harness/pico3/bounded.d.ts +21 -0
- package/dist/harness/pico3/bounded.d.ts.map +1 -0
- package/dist/harness/pico3/bounded.js +98 -0
- package/dist/harness/pico3/bounded.js.map +1 -0
- package/dist/harness/pico3/chord.d.ts +41 -0
- package/dist/harness/pico3/chord.d.ts.map +1 -0
- package/dist/harness/pico3/chord.js +130 -0
- package/dist/harness/pico3/chord.js.map +1 -0
- package/dist/harness/pico3/context.d.ts +16 -0
- package/dist/harness/pico3/context.d.ts.map +1 -0
- package/dist/harness/pico3/context.js +88 -0
- package/dist/harness/pico3/context.js.map +1 -0
- package/dist/harness/pico3/harness.d.ts +197 -0
- package/dist/harness/pico3/harness.d.ts.map +1 -0
- package/dist/harness/pico3/harness.js +654 -0
- package/dist/harness/pico3/harness.js.map +1 -0
- package/dist/harness/pico3/hooks.d.ts +10 -0
- package/dist/harness/pico3/hooks.d.ts.map +1 -0
- package/dist/harness/pico3/hooks.js +33 -0
- package/dist/harness/pico3/hooks.js.map +1 -0
- package/dist/harness/pico3/index.d.ts +19 -0
- package/dist/harness/pico3/index.d.ts.map +1 -0
- package/dist/harness/pico3/index.js +15 -0
- package/dist/harness/pico3/index.js.map +1 -0
- package/dist/harness/pico3/jsonl.d.ts +58 -0
- package/dist/harness/pico3/jsonl.d.ts.map +1 -0
- package/dist/harness/pico3/jsonl.js +325 -0
- package/dist/harness/pico3/jsonl.js.map +1 -0
- package/dist/harness/pico3/kinds/collapse.d.ts +49 -0
- package/dist/harness/pico3/kinds/collapse.d.ts.map +1 -0
- package/dist/harness/pico3/kinds/collapse.js +191 -0
- package/dist/harness/pico3/kinds/collapse.js.map +1 -0
- package/dist/harness/pico3/kinds/entries.d.ts +78 -0
- package/dist/harness/pico3/kinds/entries.d.ts.map +1 -0
- package/dist/harness/pico3/kinds/entries.js +13 -0
- package/dist/harness/pico3/kinds/entries.js.map +1 -0
- package/dist/harness/pico3/kinds/frames.d.ts +9 -0
- package/dist/harness/pico3/kinds/frames.d.ts.map +1 -0
- package/dist/harness/pico3/kinds/frames.js +75 -0
- package/dist/harness/pico3/kinds/frames.js.map +1 -0
- package/dist/harness/pico3/kinds/generation.d.ts +94 -0
- package/dist/harness/pico3/kinds/generation.d.ts.map +1 -0
- package/dist/harness/pico3/kinds/generation.js +504 -0
- package/dist/harness/pico3/kinds/generation.js.map +1 -0
- package/dist/harness/pico3/kinds/job.d.ts +44 -0
- package/dist/harness/pico3/kinds/job.d.ts.map +1 -0
- package/dist/harness/pico3/kinds/job.js +143 -0
- package/dist/harness/pico3/kinds/job.js.map +1 -0
- package/dist/harness/pico3/kinds/plugin.d.ts +15 -0
- package/dist/harness/pico3/kinds/plugin.d.ts.map +1 -0
- package/dist/harness/pico3/kinds/plugin.js +35 -0
- package/dist/harness/pico3/kinds/plugin.js.map +1 -0
- package/dist/harness/pico3/kinds/post-tools.d.ts +22 -0
- package/dist/harness/pico3/kinds/post-tools.d.ts.map +1 -0
- package/dist/harness/pico3/kinds/post-tools.js +145 -0
- package/dist/harness/pico3/kinds/post-tools.js.map +1 -0
- package/dist/harness/pico3/kinds/task-api.d.ts +3 -0
- package/dist/harness/pico3/kinds/task-api.d.ts.map +1 -0
- package/dist/harness/pico3/kinds/task-api.js +45 -0
- package/dist/harness/pico3/kinds/task-api.js.map +1 -0
- package/dist/harness/pico3/kinds/tool.d.ts +43 -0
- package/dist/harness/pico3/kinds/tool.d.ts.map +1 -0
- package/dist/harness/pico3/kinds/tool.js +373 -0
- package/dist/harness/pico3/kinds/tool.js.map +1 -0
- package/dist/harness/pico3/membrane.d.ts +23 -0
- package/dist/harness/pico3/membrane.d.ts.map +1 -0
- package/dist/harness/pico3/membrane.js +141 -0
- package/dist/harness/pico3/membrane.js.map +1 -0
- package/dist/harness/pico3/memory.d.ts +53 -0
- package/dist/harness/pico3/memory.d.ts.map +1 -0
- package/dist/harness/pico3/memory.js +264 -0
- package/dist/harness/pico3/memory.js.map +1 -0
- package/dist/harness/pico3/scheduler.d.ts +46 -0
- package/dist/harness/pico3/scheduler.d.ts.map +1 -0
- package/dist/harness/pico3/scheduler.js +438 -0
- package/dist/harness/pico3/scheduler.js.map +1 -0
- package/dist/harness/pico3/session.d.ts +263 -0
- package/dist/harness/pico3/session.d.ts.map +1 -0
- package/dist/harness/pico3/session.js +1347 -0
- package/dist/harness/pico3/session.js.map +1 -0
- package/dist/harness/pico3/system.d.ts +126 -0
- package/dist/harness/pico3/system.d.ts.map +1 -0
- package/dist/harness/pico3/system.js +244 -0
- package/dist/harness/pico3/system.js.map +1 -0
- package/dist/harness/pico3/types.d.ts +973 -0
- package/dist/harness/pico3/types.d.ts.map +1 -0
- package/dist/harness/pico3/types.js +96 -0
- package/dist/harness/pico3/types.js.map +1 -0
- package/dist/harness/pico3/view.d.ts +34 -0
- package/dist/harness/pico3/view.d.ts.map +1 -0
- package/dist/harness/pico3/view.js +417 -0
- package/dist/harness/pico3/view.js.map +1 -0
- package/dist/harness/runtime/drive/boundary.d.ts.map +1 -1
- package/dist/harness/runtime/drive/boundary.js +6 -3
- package/dist/harness/runtime/drive/boundary.js.map +1 -1
- package/dist/harness/runtime/drive/response.d.ts.map +1 -1
- package/dist/harness/runtime/drive/response.js +4 -4
- package/dist/harness/runtime/drive/response.js.map +1 -1
- package/dist/harness/runtime/drive/retry.d.ts +2 -2
- package/dist/harness/runtime/drive/retry.d.ts.map +1 -1
- package/dist/harness/runtime/drive/retry.js +3 -6
- package/dist/harness/runtime/drive/retry.js.map +1 -1
- package/dist/harness/runtime/drive/structural.d.ts.map +1 -1
- package/dist/harness/runtime/drive/structural.js +5 -5
- package/dist/harness/runtime/drive/structural.js.map +1 -1
- package/dist/harness/runtime/drive/tool-placement.d.ts.map +1 -1
- package/dist/harness/runtime/drive/tool-placement.js +2 -27
- package/dist/harness/runtime/drive/tool-placement.js.map +1 -1
- package/dist/harness/runtime/drive/tools.d.ts.map +1 -1
- package/dist/harness/runtime/drive/tools.js.map +1 -1
- package/dist/harness/session/fork-policy.d.ts +20 -12
- package/dist/harness/session/fork-policy.d.ts.map +1 -1
- package/dist/harness/session/fork-policy.js +45 -12
- package/dist/harness/session/fork-policy.js.map +1 -1
- package/dist/harness/session/fork.d.ts +0 -2
- package/dist/harness/session/fork.d.ts.map +1 -1
- package/dist/harness/session/fork.js +20 -68
- package/dist/harness/session/fork.js.map +1 -1
- package/dist/harness/session/in-memory-storage-state.d.ts +5 -5
- package/dist/harness/session/in-memory-storage-state.d.ts.map +1 -1
- package/dist/harness/session/in-memory-storage-state.js +99 -39
- package/dist/harness/session/in-memory-storage-state.js.map +1 -1
- package/dist/harness/session/index.d.ts +1 -1
- package/dist/harness/session/index.d.ts.map +1 -1
- package/dist/harness/session/index.js +1 -1
- package/dist/harness/session/index.js.map +1 -1
- package/dist/harness/session/jsonl/fork.d.ts +38 -0
- package/dist/harness/session/jsonl/fork.d.ts.map +1 -0
- package/dist/harness/session/jsonl/fork.js +264 -0
- package/dist/harness/session/jsonl/fork.js.map +1 -0
- package/dist/harness/session/jsonl/io.d.ts +14 -0
- package/dist/harness/session/jsonl/io.d.ts.map +1 -0
- package/dist/harness/session/jsonl/io.js +86 -0
- package/dist/harness/session/jsonl/io.js.map +1 -0
- package/dist/harness/session/jsonl/legacy-v3.d.ts +33 -8
- package/dist/harness/session/jsonl/legacy-v3.d.ts.map +1 -1
- package/dist/harness/session/jsonl/legacy-v3.js +285 -206
- package/dist/harness/session/jsonl/legacy-v3.js.map +1 -1
- package/dist/harness/session/jsonl/repo.d.ts +2 -1
- package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
- package/dist/harness/session/jsonl/repo.js +36 -20
- package/dist/harness/session/jsonl/repo.js.map +1 -1
- package/dist/harness/session/jsonl/storage.d.ts +4 -5
- package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
- package/dist/harness/session/jsonl/storage.js +39 -113
- package/dist/harness/session/jsonl/storage.js.map +1 -1
- package/dist/harness/session/memory.d.ts +3 -5
- package/dist/harness/session/memory.d.ts.map +1 -1
- package/dist/harness/session/memory.js +9 -14
- package/dist/harness/session/memory.js.map +1 -1
- package/dist/harness/session/testing/conformance/session-repo.d.ts +2 -0
- package/dist/harness/session/testing/conformance/session-repo.d.ts.map +1 -1
- package/dist/harness/session/testing/conformance/session-repo.js +220 -1
- package/dist/harness/session/testing/conformance/session-repo.js.map +1 -1
- package/dist/harness/session/testing/index.d.ts +1 -1
- package/dist/harness/session/testing/index.d.ts.map +1 -1
- package/dist/harness/session/testing/index.js +1 -1
- package/dist/harness/session/testing/index.js.map +1 -1
- package/dist/harness/session/types.d.ts +1 -0
- package/dist/harness/session/types.d.ts.map +1 -1
- package/dist/harness/session/types.js.map +1 -1
- package/dist/harness/types.d.ts +14 -0
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/proxy.d.ts +2 -2
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js.map +1 -1
- package/dist/types.d.ts +30 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +10 -6
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { Context } from "@earendil-works/chord";
|
|
2
|
+
import { withAbortSignal } from "@earendil-works/chord/context";
|
|
3
|
+
import type { TSchema } from "typebox";
|
|
4
|
+
import { collapse } from "./kinds/collapse.ts";
|
|
5
|
+
import { generation } from "./kinds/generation.ts";
|
|
6
|
+
import { job } from "./kinds/job.ts";
|
|
7
|
+
import { plugin } from "./kinds/plugin.ts";
|
|
8
|
+
import { postTools } from "./kinds/post-tools.ts";
|
|
9
|
+
import { tool } from "./kinds/tool.ts";
|
|
10
|
+
import { isCoreKind } from "./session.ts";
|
|
11
|
+
import { type SystemSection } from "./system.ts";
|
|
12
|
+
import { type AnyKind, type AnyToolDeclaration, type ConfigFacade, type ConfigOfKinds, type ContextView, type ConversationSpec, type DisjointConfig, type Entry, type EntryKind, type EntryScan, type HooksOf, type HostTx, type Id, type Input, type JsonObject, type JsonValue, type Models, type Namespace, type NamespaceDefaults, type NewEntry, type PluginHandler, type ProcessHost, type RewindableState, type SendInput, type StickyState, type Storage, type Task, type ToolDeclaration, type UserInput } from "./types.ts";
|
|
13
|
+
import { type Watch } from "./view.ts";
|
|
14
|
+
export interface HarnessOptions<Ks extends readonly AnyKind[] = []> {
|
|
15
|
+
models: Models;
|
|
16
|
+
tools?: AnyToolDeclaration[];
|
|
17
|
+
/** Ordinary kinds authored with `defineTask`. Their `config` merges onto `c.config`; keys must be disjoint. */
|
|
18
|
+
taskKinds?: Ks;
|
|
19
|
+
sections?: SystemSection[];
|
|
20
|
+
plugins?: {
|
|
21
|
+
[name: string]: PluginHandler;
|
|
22
|
+
};
|
|
23
|
+
processHost?: ProcessHost;
|
|
24
|
+
/** Clock for durable kernel timestamps and retry scheduling. */
|
|
25
|
+
now?: () => number;
|
|
26
|
+
root?: {
|
|
27
|
+
rewindable?: Partial<RewindableState>;
|
|
28
|
+
sticky?: Partial<StickyState>;
|
|
29
|
+
};
|
|
30
|
+
/** Errors from listeners, hooks, watches, and the scheduler. Never delivered as commit failures. */
|
|
31
|
+
onReport?: (error: unknown) => void;
|
|
32
|
+
}
|
|
33
|
+
export type BuiltinKinds = readonly [
|
|
34
|
+
typeof generation,
|
|
35
|
+
typeof tool,
|
|
36
|
+
typeof postTools,
|
|
37
|
+
typeof collapse,
|
|
38
|
+
typeof job,
|
|
39
|
+
typeof plugin
|
|
40
|
+
];
|
|
41
|
+
export type ConfigFor<Ks extends readonly AnyKind[]> = ConfigOfKinds<[...BuiltinKinds, ...Ks]>;
|
|
42
|
+
export interface ConversationHandle<Cfg extends object = ConfigFor<[]>> {
|
|
43
|
+
readonly id: Id;
|
|
44
|
+
readonly config: ConfigFacade<Cfg>;
|
|
45
|
+
send(input: SendInput, ctx: Context): Promise<InputHandle>;
|
|
46
|
+
write(entry: NewEntry, ctx: Context): Promise<Id>;
|
|
47
|
+
commit<T>(fn: (tx: HostTx, ctx: Context) => T | Promise<T>, ctx: Context): Promise<T>;
|
|
48
|
+
rewindable(ctx: Context): Promise<RewindableState>;
|
|
49
|
+
sticky(ctx: Context): Promise<StickyState>;
|
|
50
|
+
context(ctx: Context): Promise<ContextView>;
|
|
51
|
+
fork(at: Id | "start", spec: Omit<ConversationSpec, "parent">, ctx: Context): Promise<ConversationHandle<Cfg>>;
|
|
52
|
+
collapse(instructions: string | undefined, ctx: Context): Promise<Id>;
|
|
53
|
+
reset(handoff: string | undefined, ctx: Context): Promise<void>;
|
|
54
|
+
abort(ctx: Context): Promise<void>;
|
|
55
|
+
waitForIdle(ctx: Context): Promise<void>;
|
|
56
|
+
hooks<T extends JsonObject, K extends AnyKind>(namespace: Namespace<T>, kind: K, handlers: Partial<HooksOf<K>>, opts?: {
|
|
57
|
+
subtree?: boolean;
|
|
58
|
+
}): () => void;
|
|
59
|
+
watch(ctx: Context): Promise<Watch>;
|
|
60
|
+
}
|
|
61
|
+
export interface InputHandle {
|
|
62
|
+
readonly id: Id;
|
|
63
|
+
result(ctx: Context): Promise<Input | undefined>;
|
|
64
|
+
wait(ctx: Context): Promise<Input>;
|
|
65
|
+
abort(ctx: Context): Promise<"aborted" | "already_placed" | "not_found">;
|
|
66
|
+
}
|
|
67
|
+
export declare class Harness<Ks extends readonly AnyKind[] = []> {
|
|
68
|
+
private readonly session;
|
|
69
|
+
private readonly scheduler;
|
|
70
|
+
private readonly views;
|
|
71
|
+
private readonly kinds;
|
|
72
|
+
private readonly namespaces;
|
|
73
|
+
private readonly tools;
|
|
74
|
+
private readonly sectionMap;
|
|
75
|
+
private readonly entryKinds;
|
|
76
|
+
private readonly revisions;
|
|
77
|
+
private readonly plugins;
|
|
78
|
+
private readonly hookRegistrations;
|
|
79
|
+
private readonly conversationListeners;
|
|
80
|
+
private readonly onReport;
|
|
81
|
+
private readonly now;
|
|
82
|
+
private readonly ctx;
|
|
83
|
+
private readonly options;
|
|
84
|
+
private resumed;
|
|
85
|
+
private suspended;
|
|
86
|
+
static open<Ks extends readonly AnyKind[] = []>(storage: Storage, options: HarnessOptions<Ks> & (DisjointConfig<[...BuiltinKinds, ...Ks]> extends true ? unknown : {
|
|
87
|
+
readonly __error: "config keys collide across kinds";
|
|
88
|
+
}), ctx: Context): Promise<Harness<Ks>>;
|
|
89
|
+
private constructor();
|
|
90
|
+
private notifyConversation;
|
|
91
|
+
private assertInvocation;
|
|
92
|
+
private assertTaskConversationScope;
|
|
93
|
+
private assertOwnedConversation;
|
|
94
|
+
private init;
|
|
95
|
+
resume(): void;
|
|
96
|
+
private reconcileOrphans;
|
|
97
|
+
quiescent(): boolean;
|
|
98
|
+
hold(): () => void;
|
|
99
|
+
/** Cancel and join in-process invocations, clear transient waits, then close without terminalizing tasks. */
|
|
100
|
+
suspend(ctx: Context): Promise<void>;
|
|
101
|
+
registerTaskKind<K extends AnyKind>(kind: K): () => void;
|
|
102
|
+
namespace<T extends JsonObject>(id: string, defaults: NamespaceDefaults<T>, opts?: {
|
|
103
|
+
view?: (slice: Readonly<T>) => JsonValue;
|
|
104
|
+
}): Namespace<T>;
|
|
105
|
+
/** Register a tool. A duplicate name rejects. Unregister removes only this exact declaration; idempotent. */
|
|
106
|
+
registerTool(tool: AnyToolDeclaration, at?: "open" | "runtime"): () => void;
|
|
107
|
+
registerSection(section: SystemSection, at?: "open" | "runtime"): () => void;
|
|
108
|
+
registerEntryKind(kind: EntryKind): () => void;
|
|
109
|
+
/** Register namespace-bound handlers for one kind's hook points, harness-wide. Both tokens must be current. */
|
|
110
|
+
hooks<T extends JsonObject, K extends AnyKind>(namespace: Namespace<T>, kind: K, handlers: Partial<HooksOf<K>>, _opts?: Readonly<Record<string, never>>): () => void;
|
|
111
|
+
private checkKind;
|
|
112
|
+
private checkNamespace;
|
|
113
|
+
private addHooks;
|
|
114
|
+
root(ctx: Context): Promise<ConversationHandle<ConfigFor<Ks>>>;
|
|
115
|
+
onConversation(listener: (conversation: ConversationHandle<ConfigFor<Ks>>) => void): () => void;
|
|
116
|
+
conversation(id: Id, ctx: Context): Promise<ConversationHandle<ConfigFor<Ks>> | undefined>;
|
|
117
|
+
createConversation(spec: ConversationSpec & {
|
|
118
|
+
input?: UserInput;
|
|
119
|
+
}, ctx: Context): Promise<ConversationHandle<ConfigFor<Ks>>>;
|
|
120
|
+
entries(scan: EntryScan, ctx: Context): Promise<Entry[]>;
|
|
121
|
+
getTask(id: Id, ctx: Context): Promise<Task | undefined>;
|
|
122
|
+
abortInput(id: Id, ctx: Context, conversationId?: Id): Promise<"aborted" | "already_placed" | "not_found">;
|
|
123
|
+
abortTask(id: Id, ctx: Context): Promise<"marked" | "terminal">;
|
|
124
|
+
/** Durably mark a task for abort without signalling its invocation; the scheduler aborts it when it next drains. */
|
|
125
|
+
markTask(id: Id, ctx: Context): Promise<"marked" | "terminal">;
|
|
126
|
+
waitForIdle(ctx: Context): Promise<void>;
|
|
127
|
+
waitForTask(id: Id, ctx: Context): Promise<Task>;
|
|
128
|
+
/** Signals every invocation, waits for them, closes storage. Writes nothing. */
|
|
129
|
+
close(ctx: Context): Promise<void>;
|
|
130
|
+
private handle;
|
|
131
|
+
private watch;
|
|
132
|
+
private inputHandle;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* §9: H = newest fork-visible entry with a head. Active transcript = H plus every fork-visible entry
|
|
136
|
+
* with id ≥ H.head, chronological, nothing dropped inside the range; the whole transcript if no head.
|
|
137
|
+
*/
|
|
138
|
+
export declare function captureActiveTranscript(scan: (s: EntryScan) => Promise<Entry[]>, conversationId: Id): Promise<Entry[]>;
|
|
139
|
+
export { withAbortSignal, isCoreKind };
|
|
140
|
+
export type { ConversationView, Envelope, ViewEvent } from "./types.ts";
|
|
141
|
+
export type { Watch } from "./view.ts";
|
|
142
|
+
export { applyEnvelope, WATCH_CAPACITY } from "./view.ts";
|
|
143
|
+
/** The built-in kinds, as typed witnesses for `api.task`, `waitForTask`, and `hooks(kind, …)`. */
|
|
144
|
+
export declare const kinds: {
|
|
145
|
+
readonly generation: import("./types.ts").CoreKind<import("./kinds/generation.ts").GenerationInput, import("./kinds/generation.ts").GenerationCheckpoint, import("./kinds/generation.ts").GenerationResult, import("./kinds/generation.ts").GenerationFailure, {
|
|
146
|
+
assistant?: number | undefined;
|
|
147
|
+
}, import("./kinds/generation.ts").GenerationHooks, {
|
|
148
|
+
readonly rewindable: {
|
|
149
|
+
readonly model: import("./types.ts").ModelRef | undefined;
|
|
150
|
+
readonly thinkingLevel: import("./types.ts").ThinkingLevel;
|
|
151
|
+
readonly selectedTools: string[];
|
|
152
|
+
readonly profile: string;
|
|
153
|
+
};
|
|
154
|
+
readonly sticky: {
|
|
155
|
+
readonly retry: import("./types.ts").RetryPolicy;
|
|
156
|
+
};
|
|
157
|
+
}>;
|
|
158
|
+
readonly tool: import("./types.ts").CoreKind<import("./kinds/tool.ts").ToolInput, import("./kinds/tool.ts").ToolCheckpoint, import("./kinds/tool.ts").ToolTaskResult, never, {
|
|
159
|
+
entry: number;
|
|
160
|
+
}, import("./kinds/tool.ts").ToolHooks>;
|
|
161
|
+
readonly postTools: import("./types.ts").CoreKind<import("./kinds/post-tools.ts").PostToolsInput, never, import("./kinds/post-tools.ts").PostToolsResult, never, null, import("./kinds/post-tools.ts").PostToolsHooks, {
|
|
162
|
+
readonly sticky: {
|
|
163
|
+
readonly steeringMode: "all" | "one-at-a-time";
|
|
164
|
+
readonly followUpMode: "all" | "one-at-a-time";
|
|
165
|
+
};
|
|
166
|
+
}>;
|
|
167
|
+
readonly collapse: import("./types.ts").CoreKind<import("./kinds/collapse.ts").CollapseInput, import("./kinds/collapse.ts").CollapseCheckpoint, {
|
|
168
|
+
summary: number;
|
|
169
|
+
}, import("./kinds/collapse.ts").CollapseFailure, null, import("./kinds/collapse.ts").CollapseHooks, {
|
|
170
|
+
readonly rewindable: {
|
|
171
|
+
readonly threshold: number;
|
|
172
|
+
readonly keepRecent: number;
|
|
173
|
+
};
|
|
174
|
+
}>;
|
|
175
|
+
readonly job: Readonly<import("./types.ts").Kind<import("./kinds/job.ts").JobInput, import("./kinds/job.ts").JobCheckpoint, import("./kinds/job.ts").JobResult, import("./kinds/job.ts").JobFailure, {
|
|
176
|
+
killed: boolean;
|
|
177
|
+
}, object, import("./types.ts").KindConfig<import("./types.ts").ConfigShape, import("./types.ts").ConfigShape>, import("./kinds/job.ts").JobOutput, import("./types.ts").TaskTx>>;
|
|
178
|
+
readonly plugin: Readonly<import("./types.ts").Kind<import("./kinds/plugin.ts").PluginInput, {
|
|
179
|
+
phase: "started";
|
|
180
|
+
}, import("@earendil-works/chord").JsonValue, {
|
|
181
|
+
reason: "missing_handler" | "threw";
|
|
182
|
+
detail: string;
|
|
183
|
+
}, null, object, import("./types.ts").KindConfig<import("./types.ts").ConfigShape, import("./types.ts").ConfigShape>, JsonObject, import("./types.ts").TaskTx>>;
|
|
184
|
+
};
|
|
185
|
+
export declare const entries: {
|
|
186
|
+
readonly user: EntryKind<import("./kinds/entries.ts").UserEntry>;
|
|
187
|
+
readonly assistant: EntryKind<import("./kinds/entries.ts").AssistantEntry>;
|
|
188
|
+
readonly toolResult: EntryKind<import("./kinds/entries.ts").ToolResultEntry>;
|
|
189
|
+
readonly system: EntryKind<import("./kinds/entries.ts").SystemEntry>;
|
|
190
|
+
readonly notice: EntryKind<import("./kinds/entries.ts").NoticeEntry>;
|
|
191
|
+
readonly usage: EntryKind<import("./kinds/entries.ts").UsageEntry>;
|
|
192
|
+
readonly summary: EntryKind<import("./kinds/entries.ts").SummaryEntry>;
|
|
193
|
+
readonly handoff: EntryKind<import("./kinds/entries.ts").HandoffEntry>;
|
|
194
|
+
readonly reset: EntryKind<import("./kinds/entries.ts").ResetEntry>;
|
|
195
|
+
};
|
|
196
|
+
export type { ToolDeclaration, TSchema };
|
|
197
|
+
//# sourceMappingURL=harness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../../src/harness/pico3/harness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAiB,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,OAAO,EAAE,UAAU,EAAwB,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,aAAa,CAAC;AACjE,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,WAAW,EAEhB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EAEnB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,SAAS,EAEd,KAAK,OAAO,EACZ,KAAK,MAAM,EACX,KAAK,EAAE,EACP,KAAK,KAAK,EAEV,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,MAAM,EACX,KAAK,SAAS,EACd,KAAK,iBAAiB,EAEtB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,eAAe,EAEpB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,IAAI,EACT,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,WAAW,CAAC;AAMpD,MAAM,WAAW,cAAc,CAAC,EAAE,SAAS,SAAS,OAAO,EAAE,GAAG,EAAE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC7B,+GAA+G;IAC/G,SAAS,CAAC,EAAE,EAAE,CAAC;IACf,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAA;KAAE,CAAC;IAC5C,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,gEAAgE;IAChE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;KAAE,CAAC;IAChF,oGAAoG;IACpG,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,MAAM,YAAY,GAAG,SAAS;IACnC,OAAO,UAAU;IACjB,OAAO,IAAI;IACX,OAAO,SAAS;IAChB,OAAO,QAAQ;IACf,OAAO,GAAG;IACV,OAAO,MAAM;CACb,CAAC;AACF,MAAM,MAAM,SAAS,CAAC,EAAE,SAAS,SAAS,OAAO,EAAE,IAAI,aAAa,CAAC,CAAC,GAAG,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;AAE/F,MAAM,WAAW,kBAAkB,CAAC,GAAG,SAAS,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;IACrE,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC3D,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;IAClD,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtF,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACnD,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5C,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/G,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;IACtE,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,KAAK,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO,EAC5C,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EACvB,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAC7B,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAC1B,MAAM,IAAI,CAAC;IACd,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;CACpC;AACD,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACnC,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,gBAAgB,GAAG,WAAW,CAAC,CAAC;CACzE;AAID,qBAAa,OAAO,CAAC,EAAE,SAAS,SAAS,OAAO,EAAE,GAAG,EAAE;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA4C;IACvE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgC;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IACvD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0B;IAC5D,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAwE;IAC9G,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2B;IACpD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAU;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAE1B,OAAa,IAAI,CAAC,EAAE,SAAS,SAAS,OAAO,EAAE,GAAG,EAAE,EACnD,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,CAAC,EAAE,CAAC,GAC1B,CAAC,cAAc,CAAC,CAAC,GAAG,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,SAAS,IAAI,GACnD,OAAO,GACP;QAAE,QAAQ,CAAC,OAAO,EAAE,kCAAkC,CAAA;KAAE,CAAC,EAC7D,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAItB;IAED,OAAO,eAiKN;IAED,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,uBAAuB;YAOjB,IAAI;IAuBlB,MAAM,SASL;YACa,gBAAgB;IAe9B,SAAS,IAAI,OAAO,CAEnB;IACD,IAAI,IAAI,MAAM,IAAI,CAGjB;IACD,6GAA6G;IACvG,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBzC;IAID,gBAAgB,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,IAAI,CAYvD;IAED,SAAS,CAAC,CAAC,SAAS,UAAU,EAC7B,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC9B,IAAI,GAAE;QAAE,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,SAAS,CAAA;KAAO,GACrD,SAAS,CAAC,CAAC,CAAC,CAwCd;IAED,6GAA6G;IAC7G,YAAY,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,GAAE,MAAM,GAAG,SAAqB,GAAG,MAAM,IAAI,CAWrF;IACD,eAAe,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,GAAE,MAAM,GAAG,SAAqB,GAAG,MAAM,IAAI,CAWtF;IACD,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,IAAI,CAQ7C;IACD,+GAA+G;IAC/G,KAAK,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO,EAC5C,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EACvB,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAC7B,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,GACrC,MAAM,IAAI,CAEZ;IACD,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,QAAQ;IAaV,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAEnE;IACD,cAAc,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAE,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAU9F;IACK,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,CAG/F;IACK,kBAAkB,CACvB,IAAI,EAAE,gBAAgB,GAAG;QAAE,KAAK,CAAC,EAAE,SAAS,CAAA;KAAE,EAC9C,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAc5C;IACD,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAEvD;IACD,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAEvD;IACK,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,EAAE,GAAG,OAAO,CAAC,SAAS,GAAG,gBAAgB,GAAG,WAAW,CAAC,CAO/G;IACD,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC,CAE9D;IACD,oHAAoH;IACpH,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC,CAc7D;IACD,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvC;IACD,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;IAED,gFAAgF;IAC1E,KAAK,CAAC,GAAG,EAAE,OAAO,iBAEvB;IAED,OAAO,CAAC,MAAM;YAkHA,KAAK;IAmBnB,OAAO,CAAC,WAAW;CASnB;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC5C,IAAI,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,EACxC,cAAc,EAAE,EAAE,GAChB,OAAO,CAAC,KAAK,EAAE,CAAC,CAmBlB;AAED,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC;AACvC,YAAY,EAAE,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACxE,YAAY,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC1D,oGAAkG;AAClG,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAkE,CAAC;AACrF,eAAO,MAAM,OAAO;;;;;;;;;;CAAiB,CAAC;AACtC,YAAY,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport { withAbortSignal } from \"@earendil-works/chord/context\";\nimport type { TSchema } from \"typebox\";\nimport { createHookRunners, type HookRegistration } from \"./hooks.ts\";\nimport { chooseThrough, collapse } from \"./kinds/collapse.ts\";\nimport { entries as builtinEntries } from \"./kinds/entries.ts\";\nimport { generation } from \"./kinds/generation.ts\";\nimport { job } from \"./kinds/job.ts\";\nimport { plugin } from \"./kinds/plugin.ts\";\nimport { postTools } from \"./kinds/post-tools.ts\";\nimport { tool } from \"./kinds/tool.ts\";\nimport { Scheduler } from \"./scheduler.ts\";\nimport { isCoreKind, Session, type TxImpl } from \"./session.ts\";\nimport { type SystemSection, systemSections } from \"./system.ts\";\nimport {\n\ttype AnyKind,\n\ttype AnyToolDeclaration,\n\ttype ConfigFacade,\n\ttype ConfigOfKinds,\n\ttype ContextView,\n\ttype Conversation,\n\ttype ConversationSpec,\n\ttype DisjointConfig,\n\ttype DocRef,\n\ttype Entry,\n\ttype EntryKind,\n\ttype EntryScan,\n\tForbidden,\n\ttype HooksOf,\n\ttype HostTx,\n\ttype Id,\n\ttype Input,\n\ttype Invoker,\n\ttype JsonObject,\n\ttype JsonValue,\n\ttype Models,\n\ttype Namespace,\n\ttype NamespaceDefaults,\n\ttype NamespaceRegistration,\n\ttype NewEntry,\n\ttype PluginHandler,\n\ttype ProcessHost,\n\ttype RewindableState,\n\ttype Runtime,\n\ttype SendInput,\n\ttype StickyState,\n\ttype Storage,\n\ttype Task,\n\ttype ToolDeclaration,\n\ttype UserInput,\n} from \"./types.ts\";\nimport { ViewManager, type Watch } from \"./view.ts\";\n\n// ---------------------------------------------------------------------------\n// Harness\n// ---------------------------------------------------------------------------\n\nexport interface HarnessOptions<Ks extends readonly AnyKind[] = []> {\n\tmodels: Models;\n\ttools?: AnyToolDeclaration[];\n\t/** Ordinary kinds authored with `defineTask`. Their `config` merges onto `c.config`; keys must be disjoint. */\n\ttaskKinds?: Ks;\n\tsections?: SystemSection[];\n\tplugins?: { [name: string]: PluginHandler };\n\tprocessHost?: ProcessHost;\n\t/** Clock for durable kernel timestamps and retry scheduling. */\n\tnow?: () => number;\n\troot?: { rewindable?: Partial<RewindableState>; sticky?: Partial<StickyState> };\n\t/** Errors from listeners, hooks, watches, and the scheduler. Never delivered as commit failures. */\n\tonReport?: (error: unknown) => void;\n}\n\nexport type BuiltinKinds = readonly [\n\ttypeof generation,\n\ttypeof tool,\n\ttypeof postTools,\n\ttypeof collapse,\n\ttypeof job,\n\ttypeof plugin,\n];\nexport type ConfigFor<Ks extends readonly AnyKind[]> = ConfigOfKinds<[...BuiltinKinds, ...Ks]>;\n\nexport interface ConversationHandle<Cfg extends object = ConfigFor<[]>> {\n\treadonly id: Id;\n\treadonly config: ConfigFacade<Cfg>;\n\tsend(input: SendInput, ctx: Context): Promise<InputHandle>;\n\twrite(entry: NewEntry, ctx: Context): Promise<Id>;\n\tcommit<T>(fn: (tx: HostTx, ctx: Context) => T | Promise<T>, ctx: Context): Promise<T>;\n\trewindable(ctx: Context): Promise<RewindableState>;\n\tsticky(ctx: Context): Promise<StickyState>;\n\tcontext(ctx: Context): Promise<ContextView>;\n\tfork(at: Id | \"start\", spec: Omit<ConversationSpec, \"parent\">, ctx: Context): Promise<ConversationHandle<Cfg>>;\n\tcollapse(instructions: string | undefined, ctx: Context): Promise<Id>;\n\treset(handoff: string | undefined, ctx: Context): Promise<void>;\n\tabort(ctx: Context): Promise<void>;\n\twaitForIdle(ctx: Context): Promise<void>;\n\thooks<T extends JsonObject, K extends AnyKind>(\n\t\tnamespace: Namespace<T>,\n\t\tkind: K,\n\t\thandlers: Partial<HooksOf<K>>,\n\t\topts?: { subtree?: boolean },\n\t): () => void;\n\twatch(ctx: Context): Promise<Watch>;\n}\nexport interface InputHandle {\n\treadonly id: Id;\n\tresult(ctx: Context): Promise<Input | undefined>;\n\twait(ctx: Context): Promise<Input>;\n\tabort(ctx: Context): Promise<\"aborted\" | \"already_placed\" | \"not_found\">;\n}\n\nconst KERNEL: Invoker = { type: \"kernel\" };\n\nexport class Harness<Ks extends readonly AnyKind[] = []> {\n\tprivate readonly session: Session;\n\tprivate readonly scheduler: Scheduler;\n\tprivate readonly views: ViewManager;\n\tprivate readonly kinds: Map<string, AnyKind>;\n\tprivate readonly namespaces = new Map<string, NamespaceRegistration>();\n\tprivate readonly tools = new Map<string, AnyToolDeclaration>();\n\tprivate readonly sectionMap = new Map<string, SystemSection>();\n\tprivate readonly entryKinds = new Map<string, EntryKind>();\n\tprivate readonly revisions = { sections: 0, tools: 0 };\n\tprivate readonly plugins: Map<string, PluginHandler>;\n\tprivate readonly hookRegistrations: HookRegistration[] = [];\n\tprivate readonly conversationListeners = new Set<(conversation: ConversationHandle<ConfigFor<Ks>>) => void>();\n\tprivate readonly onReport: (error: unknown) => void;\n\tprivate readonly now: () => number;\n\tprivate readonly ctx: Context;\n\tprivate readonly options: HarnessOptions<Ks>;\n\tprivate resumed = false;\n\tprivate suspended = false;\n\n\tstatic async open<Ks extends readonly AnyKind[] = []>(\n\t\tstorage: Storage,\n\t\toptions: HarnessOptions<Ks> &\n\t\t\t(DisjointConfig<[...BuiltinKinds, ...Ks]> extends true\n\t\t\t\t? unknown\n\t\t\t\t: { readonly __error: \"config keys collide across kinds\" }),\n\t\tctx: Context,\n\t): Promise<Harness<Ks>> {\n\t\tconst h = new Harness<Ks>(storage, options, ctx);\n\t\tawait h.init();\n\t\treturn h;\n\t}\n\n\tprivate constructor(storage: Storage, options: HarnessOptions<Ks>, ctx: Context) {\n\t\tthis.ctx = ctx;\n\t\tthis.options = options;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.onReport = (error) => {\n\t\t\ttry {\n\t\t\t\toptions.onReport?.(error);\n\t\t\t} catch {}\n\t\t};\n\t\t// Fixed core, installed internally. User kinds may not replace or shadow a built-in.\n\t\tconst builtins: AnyKind[] = [generation, tool, postTools, collapse, job, plugin];\n\t\tthis.kinds = new Map(builtins.map((k) => [k.name, k]));\n\t\tfor (const k of options.taskKinds ?? []) {\n\t\t\tif (k.name.startsWith(\"pi.\"))\n\t\t\t\tthrow new Error(`task kind \"${k.name}\": names beginning with \"pi.\" are reserved`);\n\t\t\tif (this.kinds.has(k.name)) throw new Error(`task kind \"${k.name}\" registered twice`);\n\t\t\tthis.kinds.set(k.name, k);\n\t\t}\n\t\tfor (const t of options.tools ?? []) this.registerTool(t, \"open\");\n\t\tfor (const s of Object.values(systemSections)) this.sectionMap.set(s.key, s as SystemSection);\n\t\tfor (const s of options.sections ?? []) this.registerSection(s, \"open\");\n\t\tfor (const e of Object.values(builtinEntries)) this.entryKinds.set(e.kind, e as EntryKind);\n\t\tthis.plugins = new Map(Object.entries(options.plugins ?? {}));\n\t\tthis.session = new Session(storage, this.kinds, this.namespaces, this.now); // throws if the Storage already has an owner; validates config disjointness\n\t\tthis.session.onReport = this.onReport;\n\t\tthis.views = new ViewManager(this.session, this.onReport);\n\t\tthis.session.lineListeners.add((result) => this.views.update(result));\n\t\tthis.session.listeners.add((result) => {\n\t\t\tthis.views.deliver();\n\t\t\tfor (const conversation of result.changes.conversations) this.notifyConversation(conversation);\n\t\t});\n\t\tconst self = this;\n\t\tconst hooksFor = createHookRunners(\n\t\t\t() => this.hookRegistrations,\n\t\t\t(c) => this.session.index.ancestors(c),\n\t\t\tthis.onReport,\n\t\t);\n\t\tthis.scheduler = new Scheduler({\n\t\t\tsession: this.session,\n\t\t\tkinds: this.kinds,\n\t\t\tonReport: this.onReport,\n\t\t\tctx,\n\t\t\truntime(task, invoker, _ictx): Runtime {\n\t\t\t\treturn {\n\t\t\t\t\ttaskId: task.id,\n\t\t\t\t\tconversationId: task.conversationId,\n\t\t\t\t\tkind: invoker.kind,\n\t\t\t\t\tcommit: (fn, c) => {\n\t\t\t\t\t\tconst owns = self.session.liveTasks.get(task.id)?.owns ?? [];\n\t\t\t\t\t\tconst docs = owns.flatMap((id): DocRef[] => [\n\t\t\t\t\t\t\t{ doc: \"rewindable\", conversationId: id },\n\t\t\t\t\t\t\t{ doc: \"sticky\", conversationId: id },\n\t\t\t\t\t\t]);\n\t\t\t\t\t\treturn self.session\n\t\t\t\t\t\t\t.commit(invoker, (tx, lineCtx) => fn(tx, self.session.liveTasks.get(task.id)!, lineCtx), c, {\n\t\t\t\t\t\t\t\tdocs,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.then((r) => r.value);\n\t\t\t\t\t},\n\t\t\t\t\thooks: hooksFor(invoker.kind, { taskId: task.id, conversationId: task.conversationId }),\n\t\t\t\t\tmodels: options.models,\n\t\t\t\t\ttools: self.tools,\n\t\t\t\t\tregistries: {\n\t\t\t\t\t\tsections: {\n\t\t\t\t\t\t\tmap: self.sectionMap,\n\t\t\t\t\t\t\tget revision() {\n\t\t\t\t\t\t\t\treturn self.revisions.sections;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttools: {\n\t\t\t\t\t\t\tmap: self.tools,\n\t\t\t\t\t\t\tget revision() {\n\t\t\t\t\t\t\t\treturn self.revisions.tools;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tkinds: self.kinds,\n\t\t\t\t\tprocessHost: options.processHost,\n\t\t\t\t\tplugins: self.plugins,\n\t\t\t\t\tnow: self.now,\n\t\t\t\t\tsleep: (untilMs, c) =>\n\t\t\t\t\t\tnew Promise((resolve, reject) => {\n\t\t\t\t\t\t\tconst ms = Math.max(0, untilMs - Date.now());\n\t\t\t\t\t\t\tconst t = setTimeout(() => {\n\t\t\t\t\t\t\t\tc.abortSignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t}, ms);\n\t\t\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\t\t\tclearTimeout(t);\n\t\t\t\t\t\t\t\treject(c.abortSignal?.reason ?? new Error(\"aborted\"));\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tc.abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t\t\t}),\n\t\t\t\t\twaitForInput: (id, c) => self.scheduler.waitForInput(id, c),\n\t\t\t\t\twaitForTask: (id, c) => self.scheduler.waitForTask(id, c),\n\t\t\t\t\tabortTask: async (id, c) => {\n\t\t\t\t\t\tawait self.session.onLine(async (lineCtx) => {\n\t\t\t\t\t\t\tself.assertInvocation(invoker);\n\t\t\t\t\t\t\tconst target = self.session.liveTasks.get(id) ?? (await self.session.storage.task(id, lineCtx));\n\t\t\t\t\t\t\tif (target === undefined) throw new Error(`task ${id} not found`);\n\t\t\t\t\t\t\tself.assertTaskConversationScope(task.id, target.conversationId);\n\t\t\t\t\t\t}, c);\n\t\t\t\t\t\treturn self.scheduler.abortTask(id, c);\n\t\t\t\t\t},\n\t\t\t\t\tabortConversation: async (id, c) => {\n\t\t\t\t\t\tawait self.session.onLine(() => {\n\t\t\t\t\t\t\tself.assertInvocation(invoker);\n\t\t\t\t\t\t\tself.assertOwnedConversation(task.id, id);\n\t\t\t\t\t\t}, c);\n\t\t\t\t\t\tconst h = await self.conversation(id, c);\n\t\t\t\t\t\tif (h) await h.abort(c);\n\t\t\t\t\t},\n\t\t\t\t\tcreateOwnedConversation: (spec, c) =>\n\t\t\t\t\t\tself.session\n\t\t\t\t\t\t\t.commit(\n\t\t\t\t\t\t\t\tKERNEL,\n\t\t\t\t\t\t\t\t(tx) => {\n\t\t\t\t\t\t\t\t\tself.assertInvocation(invoker);\n\t\t\t\t\t\t\t\t\treturn tx.createOwnedConversation(task.id, task.conversationId, spec);\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tc,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.then((result) => result.value),\n\t\t\t\t\tsendOwned: (id, input, c) =>\n\t\t\t\t\t\tself.session\n\t\t\t\t\t\t\t.commit(\n\t\t\t\t\t\t\t\tKERNEL,\n\t\t\t\t\t\t\t\t(tx) => {\n\t\t\t\t\t\t\t\t\tself.assertInvocation(invoker);\n\t\t\t\t\t\t\t\t\tself.assertOwnedConversation(task.id, id);\n\t\t\t\t\t\t\t\t\treturn tx.send(id, input);\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tc,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tdocs: [\n\t\t\t\t\t\t\t\t\t\t{ doc: \"rewindable\", conversationId: id },\n\t\t\t\t\t\t\t\t\t\t{ doc: \"sticky\", conversationId: id },\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.then((r) => r.value),\n\t\t\t\t\tcontext: (c, at, cx) => self.session.commit(invoker, (tx) => tx.context(c, at), cx).then((r) => r.value),\n\t\t\t\t\tnewestEntry: (c, opts, cx) =>\n\t\t\t\t\t\tself.session.commit(invoker, (tx) => tx.newestEntry(c, opts), cx).then((r) => r.value),\n\t\t\t\t\trewindable: (c, cx) =>\n\t\t\t\t\t\tself.session\n\t\t\t\t\t\t\t.commit(invoker, (tx) => tx.snapshot({ doc: \"rewindable\", conversationId: c }), cx, {\n\t\t\t\t\t\t\t\tdocs: [{ doc: \"rewindable\", conversationId: c }],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.then((r) => r.value),\n\t\t\t\t\tsticky: (c, cx) =>\n\t\t\t\t\t\tself.session\n\t\t\t\t\t\t\t.commit(invoker, (tx) => tx.snapshot({ doc: \"sticky\", conversationId: c }), cx, {\n\t\t\t\t\t\t\t\tdocs: [{ doc: \"sticky\", conversationId: c }],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.then((r) => r.value),\n\t\t\t\t\trewindableAsOf: (c, at, cx) =>\n\t\t\t\t\t\tself.session.commit(invoker, (tx) => tx.rewindableAsOf(c, at), cx).then((r) => r.value),\n\t\t\t\t};\n\t\t\t},\n\t\t});\n\t}\n\n\tprivate notifyConversation(conversation: Conversation): void {\n\t\tif (this.conversationListeners.size === 0) return;\n\t\tconst handle = this.handle(conversation);\n\t\tfor (const listener of [...this.conversationListeners]) {\n\t\t\ttry {\n\t\t\t\tlistener(handle);\n\t\t\t} catch (error) {\n\t\t\t\tthis.onReport(error);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate assertInvocation(invoker: Extract<Invoker, { type: \"task\" }>): void {\n\t\tif (!invoker.token.alive) throw new Forbidden(\"operation from a finished invocation\");\n\t\tconst live = this.session.liveTasks.get(invoker.id);\n\t\tif (live === undefined) throw new Forbidden(\"operation from a task that is not live\");\n\t\tif (invoker.mode === \"run\" && live.abort === true) throw new Forbidden(\"operation from a marked run invocation\");\n\t}\n\n\tprivate assertTaskConversationScope(taskId: Id, conversationId: Id): void {\n\t\tconst task = this.session.liveTasks.get(taskId);\n\t\tif (task?.conversationId === conversationId) return;\n\t\tif (task?.owns.some((root) => this.session.index.subtree(root).has(conversationId))) return;\n\t\tthrow new Forbidden(`conversation ${conversationId} is outside task ${taskId}'s subtree`);\n\t}\n\n\tprivate assertOwnedConversation(taskId: Id, conversationId: Id): void {\n\t\tconst task = this.session.liveTasks.get(taskId);\n\t\tif (task === undefined || !task.owns.some((root) => this.session.index.subtree(root).has(conversationId))) {\n\t\t\tthrow new Forbidden(`conversation ${conversationId} is not owned by task ${taskId}`);\n\t\t}\n\t}\n\n\tprivate async init() {\n\t\tconst { session } = this;\n\t\tconst convs = await session.read((s, ctx) => s.conversations(ctx), this.ctx);\n\t\tfor (const c of convs) session.conversationRecords.set(c.id, c);\n\t\tconst live = await session.read((s, ctx) => s.scanTasks({ status: [\"pending\", \"running\"] }, ctx), this.ctx);\n\t\tfor (const task of live) session.liveTasks.set(task.id, task);\n\t\t// Owner tasks that are terminal but still own existing conversations: needed for ancestry.\n\t\tconst owners = new Set(convs.map((c) => c.owner).filter((o): o is Id => o !== undefined));\n\t\tfor (const id of owners)\n\t\t\tif (!session.liveTasks.has(id)) {\n\t\t\t\tconst t = await session.read((s, ctx) => s.task(id, ctx), this.ctx);\n\t\t\t\tif (t) session.ownerTaskCache.set(id, t);\n\t\t\t}\n\t\tif (convs.length === 0) {\n\t\t\tawait session.commit(\n\t\t\t\tKERNEL,\n\t\t\t\t(tx) =>\n\t\t\t\t\ttx.createConversation({ rewindable: this.options.root?.rewindable, sticky: this.options.root?.sticky }),\n\t\t\t\tthis.ctx,\n\t\t\t);\n\t\t}\n\t}\n\n\tresume() {\n\t\tif (this.suspended) throw new Error(\"cannot resume a suspended harness; reopen storage with a new harness\");\n\t\tif (this.resumed) return;\n\t\tthis.resumed = true;\n\t\tvoid this.reconcileOrphans()\n\t\t\t.then(() => {\n\t\t\t\tif (!this.suspended) this.scheduler.resume();\n\t\t\t})\n\t\t\t.catch((error) => this.onReport(error));\n\t}\n\tprivate async reconcileOrphans(): Promise<void> {\n\t\tconst orphaned = [...this.session.liveTasks.values()].filter((task) => !this.kinds.has(task.kind));\n\t\tif (orphaned.length === 0) return;\n\t\tconst docs = [...new Set(orphaned.map((task) => task.conversationId))].map(\n\t\t\t(conversationId): DocRef => ({ doc: \"sticky\", conversationId }),\n\t\t);\n\t\tawait this.session.commit(\n\t\t\tKERNEL,\n\t\t\t(tx) => {\n\t\t\t\tfor (const task of orphaned) tx.setTask({ ...task, status: \"terminal\", outcome: { status: \"orphaned\" } });\n\t\t\t},\n\t\t\tthis.ctx,\n\t\t\t{ docs },\n\t\t);\n\t}\n\tquiescent(): boolean {\n\t\treturn this.scheduler.quiescent();\n\t}\n\thold(): () => void {\n\t\tif (!this.scheduler.quiescent()) throw new Error(\"cannot hold a non-quiescent harness; suspend it instead\");\n\t\treturn this.scheduler.hold();\n\t}\n\t/** Cancel and join in-process invocations, clear transient waits, then close without terminalizing tasks. */\n\tasync suspend(ctx: Context): Promise<void> {\n\t\tif (this.suspended) return;\n\t\tthis.suspended = true;\n\t\tawait this.scheduler.joinAll();\n\t\tconst tools = [...this.session.liveTasks.values()].filter((task) => task.kind === \"pi.tool\");\n\t\tif (tools.length > 0) {\n\t\t\tconst docs = [...new Set(tools.map((task) => task.conversationId))].map(\n\t\t\t\t(conversationId): DocRef => ({ doc: \"sticky\", conversationId }),\n\t\t\t);\n\t\t\tawait this.session.commit(\n\t\t\t\tKERNEL,\n\t\t\t\t(tx) => {\n\t\t\t\t\tfor (const task of tools) {\n\t\t\t\t\t\tconst index = (task.input as { index: number }).index;\n\t\t\t\t\t\tconst slot = tx.sticky(task.conversationId).turn.tools[index];\n\t\t\t\t\t\tif (slot?.waitingOn !== undefined) delete slot.waitingOn;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t\t{ docs },\n\t\t\t);\n\t\t}\n\t\tthis.views.close();\n\t\tawait this.session.close(ctx);\n\t}\n\n\t// --- registries --------------------------------------------------------------\n\n\tregisterTaskKind<K extends AnyKind>(kind: K): () => void {\n\t\tif (kind.name.startsWith(\"pi.\"))\n\t\t\tthrow new Error(`task kind \"${kind.name}\": names beginning with \"pi.\" are reserved`);\n\t\tif (this.kinds.has(kind.name)) throw new Error(`task kind \"${kind.name}\" already registered`);\n\t\tthis.session.defaults.register(kind);\n\t\tthis.kinds.set(kind.name, kind);\n\t\tthis.scheduler.kick();\n\t\treturn () => {\n\t\t\tif (this.kinds.get(kind.name) !== kind) return;\n\t\t\tthis.kinds.delete(kind.name);\n\t\t\tthis.session.defaults.unregister(kind);\n\t\t};\n\t}\n\n\tnamespace<T extends JsonObject>(\n\t\tid: string,\n\t\tdefaults: NamespaceDefaults<T>,\n\t\topts: { view?: (slice: Readonly<T>) => JsonValue } = {},\n\t): Namespace<T> {\n\t\tif (!/^[a-z][a-z0-9_.-]*$/i.test(id) || id.startsWith(\"pi.\")) throw new Error(`invalid namespace \"${id}\"`);\n\t\tif (this.namespaces.has(id)) throw new Error(`namespace \"${id}\" already registered`);\n\t\tconst stored = JSON.parse(JSON.stringify(defaults)) as {\n\t\t\trewindable?: JsonObject;\n\t\t\tsticky?: JsonObject;\n\t\t\tsession?: JsonObject;\n\t\t};\n\t\tconst routes = new Map<string, \"rewindable\" | \"sticky\" | \"session\">();\n\t\tfor (const doc of [\"rewindable\", \"sticky\", \"session\"] as const) {\n\t\t\tfor (const key of Object.keys(stored[doc] ?? {})) {\n\t\t\t\tif (routes.has(key))\n\t\t\t\t\tthrow new Error(`namespace \"${id}\" key \"${key}\" is declared in more than one document`);\n\t\t\t\troutes.set(key, doc);\n\t\t\t}\n\t\t}\n\t\tlet token!: Namespace<T>;\n\t\ttoken = {\n\t\t\tid,\n\t\t\tunregister: () => {\n\t\t\t\tif (this.namespaces.get(id)?.token !== token) return;\n\t\t\t\tthis.namespaces.delete(id);\n\t\t\t\tfor (let index = this.hookRegistrations.length - 1; index >= 0; index--) {\n\t\t\t\t\tif ((this.hookRegistrations[index]!.namespace as object) === token)\n\t\t\t\t\t\tthis.hookRegistrations.splice(index, 1);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t\tconst registration: NamespaceRegistration = {\n\t\t\ttoken,\n\t\t\tdefaults: {\n\t\t\t\trewindable: stored.rewindable ?? {},\n\t\t\t\tsticky: stored.sticky ?? {},\n\t\t\t\tsession: stored.session ?? {},\n\t\t\t},\n\t\t\troutes,\n\t\t\tproject: opts.view === undefined ? undefined : (slice) => opts.view!(slice as Readonly<T>),\n\t\t};\n\t\tthis.namespaces.set(id, registration);\n\t\treturn token;\n\t}\n\n\t/** Register a tool. A duplicate name rejects. Unregister removes only this exact declaration; idempotent. */\n\tregisterTool(tool: AnyToolDeclaration, at: \"open\" | \"runtime\" = \"runtime\"): () => void {\n\t\tif (this.tools.has(tool.name)) throw new Error(`tool \"${tool.name}\" already registered`);\n\t\tthis.tools.set(tool.name, tool);\n\t\tthis.revisions.tools++;\n\t\tvoid at;\n\t\treturn () => {\n\t\t\tif (this.tools.get(tool.name) === tool) {\n\t\t\t\tthis.tools.delete(tool.name);\n\t\t\t\tthis.revisions.tools++;\n\t\t\t}\n\t\t};\n\t}\n\tregisterSection(section: SystemSection, at: \"open\" | \"runtime\" = \"runtime\"): () => void {\n\t\tif (this.sectionMap.has(section.key)) throw new Error(`section \"${section.key}\" already registered`);\n\t\tthis.sectionMap.set(section.key, section);\n\t\tthis.revisions.sections++;\n\t\tvoid at;\n\t\treturn () => {\n\t\t\tif (this.sectionMap.get(section.key) === section) {\n\t\t\t\tthis.sectionMap.delete(section.key);\n\t\t\t\tthis.revisions.sections++;\n\t\t\t}\n\t\t};\n\t}\n\tregisterEntryKind(kind: EntryKind): () => void {\n\t\tif (kind.kind.startsWith(\"pi.\"))\n\t\t\tthrow new Error(`entry kind \"${kind.kind}\": names beginning with \"pi.\" are reserved`);\n\t\tif (this.entryKinds.has(kind.kind)) throw new Error(`entry kind \"${kind.kind}\" already registered`);\n\t\tthis.entryKinds.set(kind.kind, kind);\n\t\treturn () => {\n\t\t\tif (this.entryKinds.get(kind.kind) === kind) this.entryKinds.delete(kind.kind);\n\t\t};\n\t}\n\t/** Register namespace-bound handlers for one kind's hook points, harness-wide. Both tokens must be current. */\n\thooks<T extends JsonObject, K extends AnyKind>(\n\t\tnamespace: Namespace<T>,\n\t\tkind: K,\n\t\thandlers: Partial<HooksOf<K>>,\n\t\t_opts?: Readonly<Record<string, never>>,\n\t): () => void {\n\t\treturn this.addHooks({ namespace: this.checkNamespace(namespace), kind: this.checkKind(kind), handlers });\n\t}\n\tprivate checkKind(kind: AnyKind): AnyKind {\n\t\tif (this.kinds.get(kind.name) !== kind) throw new Error(`kind \"${kind.name}\" is not the registered token`);\n\t\treturn kind;\n\t}\n\tprivate checkNamespace<T extends JsonObject>(namespace: Namespace<T>): Namespace<JsonObject> {\n\t\tif (this.namespaces.get(namespace.id)?.token !== namespace)\n\t\t\tthrow new Forbidden(`namespace \"${namespace.id}\" is stale`);\n\t\treturn namespace as unknown as Namespace<JsonObject>;\n\t}\n\tprivate addHooks(reg: HookRegistration): () => void {\n\t\tthis.hookRegistrations.push(reg);\n\t\tlet done = false;\n\t\treturn () => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tconst index = this.hookRegistrations.indexOf(reg);\n\t\t\tif (index >= 0) this.hookRegistrations.splice(index, 1);\n\t\t};\n\t}\n\n\t// --- conversations -------------------------------------------------------------\n\n\tasync root(ctx: Context): Promise<ConversationHandle<ConfigFor<Ks>>> {\n\t\treturn this.conversation(1, ctx).then((c) => c!);\n\t}\n\tonConversation(listener: (conversation: ConversationHandle<ConfigFor<Ks>>) => void): () => void {\n\t\tthis.conversationListeners.add(listener);\n\t\tfor (const conversation of this.session.conversationRecords.values()) {\n\t\t\ttry {\n\t\t\t\tlistener(this.handle(conversation));\n\t\t\t} catch (error) {\n\t\t\t\tthis.onReport(error);\n\t\t\t}\n\t\t}\n\t\treturn () => this.conversationListeners.delete(listener);\n\t}\n\tasync conversation(id: Id, ctx: Context): Promise<ConversationHandle<ConfigFor<Ks>> | undefined> {\n\t\tconst c = await this.session.read((s, lineCtx) => s.conversation(id, lineCtx), ctx);\n\t\treturn c === undefined ? undefined : this.handle(c);\n\t}\n\tasync createConversation(\n\t\tspec: ConversationSpec & { input?: UserInput },\n\t\tctx: Context,\n\t): Promise<ConversationHandle<ConfigFor<Ks>>> {\n\t\tconst { input, ...rest } = spec;\n\t\tconst id = await this.session\n\t\t\t.commit(\n\t\t\t\tKERNEL,\n\t\t\t\tasync (tx) => {\n\t\t\t\t\tconst id = tx.createConversation(rest);\n\t\t\t\t\tif (input !== undefined) await tx.send(id, { content: input });\n\t\t\t\t\treturn id;\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t)\n\t\t\t.then((r) => r.value);\n\t\treturn (await this.conversation(id, ctx))!;\n\t}\n\tentries(scan: EntryScan, ctx: Context): Promise<Entry[]> {\n\t\treturn this.session.read((s, lineCtx) => s.scanEntries(scan, lineCtx), ctx);\n\t}\n\tgetTask(id: Id, ctx: Context): Promise<Task | undefined> {\n\t\treturn this.session.read((s, lineCtx) => s.task(id, lineCtx), ctx);\n\t}\n\tasync abortInput(id: Id, ctx: Context, conversationId?: Id): Promise<\"aborted\" | \"already_placed\" | \"not_found\"> {\n\t\tif (conversationId !== undefined) {\n\t\t\tconst input = await this.session.read((storage, lineCtx) => storage.input(id, lineCtx), ctx);\n\t\t\tif (input !== undefined && input.conversationId !== conversationId)\n\t\t\t\tthrow new Forbidden(`input ${id} is outside conversation ${conversationId}`);\n\t\t}\n\t\treturn this.inputHandle(id).abort(ctx);\n\t}\n\tabortTask(id: Id, ctx: Context): Promise<\"marked\" | \"terminal\"> {\n\t\treturn this.scheduler.abortTask(id, ctx);\n\t}\n\t/** Durably mark a task for abort without signalling its invocation; the scheduler aborts it when it next drains. */\n\tmarkTask(id: Id, ctx: Context): Promise<\"marked\" | \"terminal\"> {\n\t\treturn this.session\n\t\t\t.commit(\n\t\t\t\tKERNEL,\n\t\t\t\tasync (tx) => {\n\t\t\t\t\tconst t = await tx.task(id);\n\t\t\t\t\tif (t === undefined) throw new Error(`task ${id} not found`);\n\t\t\t\t\tif (t.status === \"terminal\") return \"terminal\" as const;\n\t\t\t\t\ttx.markTask(id);\n\t\t\t\t\treturn \"marked\" as const;\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t)\n\t\t\t.then((r) => r.value);\n\t}\n\twaitForIdle(ctx: Context): Promise<void> {\n\t\treturn this.scheduler.waitForIdle(undefined, ctx);\n\t}\n\twaitForTask(id: Id, ctx: Context): Promise<Task> {\n\t\treturn this.scheduler.waitForTask(id, ctx);\n\t}\n\n\t/** Signals every invocation, waits for them, closes storage. Writes nothing. */\n\tasync close(ctx: Context) {\n\t\tawait this.suspend(ctx);\n\t}\n\n\tprivate handle(c: Conversation): ConversationHandle<ConfigFor<Ks>> {\n\t\tconst self = this;\n\t\tconst docs: DocRef[] = [\n\t\t\t{ doc: \"rewindable\", conversationId: c.id },\n\t\t\t{ doc: \"sticky\", conversationId: c.id },\n\t\t];\n\t\tconst hostInvoker: Invoker = { type: \"host\", conversationId: c.id };\n\t\tconst kernelInvoker: Invoker = { type: \"kernel\", conversationId: c.id };\n\t\tconst host = <T>(fn: (tx: HostTx, ctx: Context) => T | Promise<T>, ctx: Context) =>\n\t\t\tself.session.commit(hostInvoker, (tx, lineCtx) => fn(tx, lineCtx), ctx, { docs }).then((r) => r.value);\n\t\tconst kernel = <T>(fn: (tx: TxImpl, ctx: Context) => T | Promise<T>, ctx: Context) =>\n\t\t\tself.session.commit(kernelInvoker, fn, ctx, { docs }).then((r) => r.value);\n\t\tconst config: ConfigFacade<ConfigFor<Ks>> = {\n\t\t\tget: (ctx) =>\n\t\t\t\thost((tx) => {\n\t\t\t\t\tconst cfg = tx.config(c.id);\n\t\t\t\t\tconst out: { [k: string]: JsonValue | undefined } = {};\n\t\t\t\t\tfor (const key of self.session.defaults.route.keys()) out[key] = cfg.get(key);\n\t\t\t\t\treturn out as ConfigFor<Ks>;\n\t\t\t\t}, ctx),\n\t\t\tset: (patch, ctx) =>\n\t\t\t\thost((tx) => {\n\t\t\t\t\tconst cfg = tx.config(c.id);\n\t\t\t\t\tfor (const [key, value] of Object.entries(patch)) {\n\t\t\t\t\t\tif (value === undefined) throw new Error(`config.set(${key}): use config.reset()`);\n\t\t\t\t\t\tcfg.set(key, value as JsonValue);\n\t\t\t\t\t}\n\t\t\t\t}, ctx),\n\t\t\treset: (keys, ctx) =>\n\t\t\t\thost((tx) => {\n\t\t\t\t\tconst cfg = tx.config(c.id);\n\t\t\t\t\tfor (const key of keys) cfg.reset(String(key));\n\t\t\t\t}, ctx),\n\t\t};\n\t\treturn {\n\t\t\tid: c.id,\n\t\t\tconfig,\n\t\t\tasync send(input, ctx) {\n\t\t\t\tconst id = await kernel((tx) => tx.send(c.id, input), ctx);\n\t\t\t\treturn self.inputHandle(id);\n\t\t\t},\n\t\t\twrite: (entry, ctx) => host((tx) => tx.write(c.id, entry), ctx),\n\t\t\tcommit: host,\n\t\t\trewindable: (ctx) => host((tx) => tx.snapshot({ doc: \"rewindable\", conversationId: c.id }), ctx),\n\t\t\tsticky: (ctx) => host((tx) => tx.snapshot({ doc: \"sticky\", conversationId: c.id }), ctx),\n\t\t\tcontext: (ctx) => host((tx) => tx.context(c.id), ctx),\n\t\t\tasync fork(at, spec, ctx) {\n\t\t\t\tconst id = await self.session.fork(c.id, at, spec, ctx);\n\t\t\t\treturn (await self.conversation(id, ctx))!;\n\t\t\t},\n\t\t\tcollapse: (instructions, ctx) =>\n\t\t\t\tkernel(async (tx) => {\n\t\t\t\t\tconst { entries } = await tx.context(c.id);\n\t\t\t\t\tconst state = tx.rewindable(c.id);\n\t\t\t\t\tconst through = chooseThrough(entries, state.keepRecent);\n\t\t\t\t\tif (through === undefined) throw new Error(\"nothing to collapse\");\n\t\t\t\t\treturn tx.createTask({\n\t\t\t\t\t\tkind: \"pi.collapse\",\n\t\t\t\t\t\tconversationId: c.id,\n\t\t\t\t\t\tbackground: true,\n\t\t\t\t\t\tinput: { reason: \"manual\", through, ...(instructions === undefined ? {} : { instructions }) },\n\t\t\t\t\t});\n\t\t\t\t}, ctx),\n\t\t\treset: async (handoff, ctx) => {\n\t\t\t\tawait kernel(\n\t\t\t\t\t(tx) =>\n\t\t\t\t\t\ttx.write(\n\t\t\t\t\t\t\tc.id,\n\t\t\t\t\t\t\thandoff === undefined\n\t\t\t\t\t\t\t\t? { kind: \"pi.reset\", head: \"self\" }\n\t\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\t\tkind: \"pi.handoff\",\n\t\t\t\t\t\t\t\t\t\thead: \"self\",\n\t\t\t\t\t\t\t\t\t\tmodel: [{ role: \"user\", content: handoff, timestamp: self.now() }],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t),\n\t\t\t\t\tctx,\n\t\t\t\t);\n\t\t\t},\n\t\t\tasync abort(ctx) {\n\t\t\t\tconst owned: Id[] = [];\n\t\t\t\tconst marked = await kernel(async (tx) => {\n\t\t\t\t\tconst s = tx.sticky(c.id);\n\t\t\t\t\tconst withdrawn = s.inbox.filter((q) => q.mode !== \"write\").map((q) => q.id);\n\t\t\t\t\tfor (let i = s.inbox.length - 1; i >= 0; i--) if (s.inbox[i]!.mode !== \"write\") s.inbox.splice(i, 1);\n\t\t\t\t\tawait tx.resolveInputs(withdrawn, { status: \"unanswered\", reason: \"aborted\" });\n\t\t\t\t\tfor (const input of withdrawn) tx.emit({ type: \"input.aborted\", input });\n\t\t\t\t\tconst ids: Id[] = [];\n\t\t\t\t\tfor (const t of [...self.session.liveTasks.values()]) {\n\t\t\t\t\t\tif (t.conversationId !== c.id || t.background) continue;\n\t\t\t\t\t\tfor (const o of t.owns) owned.push(o);\n\t\t\t\t\t\tif (t.abort !== true) tx.markTask(t.id);\n\t\t\t\t\t\tids.push(t.id);\n\t\t\t\t\t}\n\t\t\t\t\treturn ids;\n\t\t\t\t}, ctx);\n\t\t\t\tfor (const id of marked) await self.scheduler.abortTask(id, ctx);\n\t\t\t\tawait self.scheduler.waitForIdle(c.id, ctx);\n\t\t\t\tawait Promise.all(owned.map((o) => self.scheduler.waitForIdle(o, ctx)));\n\t\t\t},\n\t\t\twaitForIdle: (ctx) => self.scheduler.waitForIdle(c.id, ctx),\n\t\t\thooks: (namespace, kind, handlers, opts) =>\n\t\t\t\tself.addHooks({\n\t\t\t\t\tnamespace: self.checkNamespace(namespace),\n\t\t\t\t\tkind: self.checkKind(kind),\n\t\t\t\t\thandlers,\n\t\t\t\t\tconversationId: c.id,\n\t\t\t\t\tsubtree: opts?.subtree,\n\t\t\t\t}),\n\t\t\twatch: (ctx) => self.watch(c, ctx),\n\t\t};\n\t}\n\n\t/** Capture and subscribe in one line operation. */\n\tprivate async watch(conversation: Conversation, ctx: Context): Promise<Watch> {\n\t\treturn this.session\n\t\t\t.commit(\n\t\t\t\t{ type: \"host\", conversationId: conversation.id },\n\t\t\t\tasync (tx) => {\n\t\t\t\t\tconst entries = await captureActiveTranscript((scan) => tx.scanEntries(scan), conversation.id);\n\t\t\t\t\treturn this.views.watch(conversation, entries);\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t\t{\n\t\t\t\t\tdocs: [\n\t\t\t\t\t\t{ doc: \"rewindable\", conversationId: conversation.id },\n\t\t\t\t\t\t{ doc: \"sticky\", conversationId: conversation.id },\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t)\n\t\t\t.then((result) => result.value);\n\t}\n\n\tprivate inputHandle(id: Id): InputHandle {\n\t\treturn {\n\t\t\tid,\n\t\t\tresult: (ctx) => this.session.read((s, lineCtx) => s.input(id, lineCtx), ctx),\n\t\t\twait: (ctx) => this.scheduler.waitForInput(id, ctx),\n\t\t\tabort: (ctx) =>\n\t\t\t\tthis.session.commit(KERNEL, (tx) => tx.withdrawInput(id), ctx, { docs: [] }).then((r) => r.value),\n\t\t};\n\t}\n}\n\n/**\n * §9: H = newest fork-visible entry with a head. Active transcript = H plus every fork-visible entry\n * with id ≥ H.head, chronological, nothing dropped inside the range; the whole transcript if no head.\n */\nexport async function captureActiveTranscript(\n\tscan: (s: EntryScan) => Promise<Entry[]>,\n\tconversationId: Id,\n): Promise<Entry[]> {\n\tconst [h] = await scan({ conversationId, withHead: true, limit: 1 });\n\tconst from = h?.head;\n\tconst out: Entry[] = [];\n\tlet before: Id | undefined;\n\tfor (;;) {\n\t\tconst page = await scan({ conversationId, ...(before === undefined ? {} : { before }), limit: 256 });\n\t\tlet done = page.length < 256;\n\t\tfor (const e of page) {\n\t\t\tif (from !== undefined && e.id < from) {\n\t\t\t\tdone = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tout.push(e);\n\t\t}\n\t\tif (done) break;\n\t\tbefore = page[page.length - 1]!.id;\n\t}\n\treturn out.reverse();\n}\n\nexport { withAbortSignal, isCoreKind };\nexport type { ConversationView, Envelope, ViewEvent } from \"./types.ts\";\nexport type { Watch } from \"./view.ts\";\nexport { applyEnvelope, WATCH_CAPACITY } from \"./view.ts\";\n/** The built-in kinds, as typed witnesses for `api.task`, `waitForTask`, and `hooks(kind, …)`. */\nexport const kinds = { generation, tool, postTools, collapse, job, plugin } as const;\nexport const entries = builtinEntries;\nexport type { ToolDeclaration, TSchema };\n"]}
|