-
Notifications
You must be signed in to change notification settings - Fork 58
Expand file tree
/
Copy pathpolicy_doc.ts
More file actions
93 lines (82 loc) · 3.81 KB
/
Copy pathpolicy_doc.ts
File metadata and controls
93 lines (82 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import { chidori, run, type Signal } from "chidori:agent";
/**
* Multiplayer policy-doc drafting agent (the worked example in docs/signals.md).
*
* Three participants collaborate on ONE live run:
* - a human editor (e.g. Mara) reviews drafts and asks for changes,
* - a compliance-checker agent pushes an approve/changes verdict,
* - a human lead (e.g. Sam) re-scopes the doc mid-run.
*
* None of these are agent-initiated `input()` questions. The reviewers PUSH a
* `review` signal when they have it; the lead STEERS with a `steer` signal
* whenever he wants. The agent only consumes those pushes at the points it
* declares safe:
* - `chidori.signal("review")` BLOCKS — nothing to do until a review lands;
* the run idles cheaply on disk and resumes when a review is delivered (or
* was already queued in the durable mailbox).
* - `chidori.pollSignal("steer")` is NON-BLOCKING — steering is optional; the
* agent checks the mailbox and moves on if it is empty.
*
* Every signal is recorded in the call log, so the whole multiplayer session
* replays deterministically (`chidori resume <run-id>`): the reviews, the
* verdict, and the steering are replayed from their recorded CallRecords with
* no human re-contacted and no compliance agent re-run.
*
* Deliver signals with:
* POST /sessions/{id}/signal { name, payload, from }
* (see the README for full curl examples).
*
* `writeDraft`/`revise` are kept as local helpers so the example is runnable
* offline with no LLM provider; swap them for `chidori.prompt(...)` calls to
* get real model spans in the trace.
*/
type Brief = { topic?: string; audience?: string; priority?: string; scope?: string };
type Review = { decision: "approve" | "changes"; notes: string };
type Steer = { priority?: string; scope?: string };
run(async (input: Brief) => {
let brief: Brief = {
topic: input.topic ?? "data-retention policy",
audience: input.audience ?? "all staff",
};
let draft = await writeDraft(brief); // "expensive": stands in for LLM + retrieval
let round = 0;
while (true) {
round++;
await chidori.log(`draft round ${round} ready`, { words: draft.length });
// Open this run to reviewers. The compliance agent AND the human editor both
// send a "review" signal; whichever lands first (or is already queued in the
// mailbox) is consumed here. `from` tells us who reviewed.
const review: Signal<Review> = await chidori.signal<Review>("review");
await chidori.log("review received", {
from: review.from,
decision: review.payload.decision,
});
if (review.payload.decision === "approve") {
return {
status: "published",
rounds: round,
approvedBy: review.from,
draft,
};
}
// A reviewer asked for changes — revise and loop. Before revising,
// opportunistically pick up any steering the lead pushed (non-blocking;
// null if none waiting).
const steer = await chidori.pollSignal<Steer>("steer");
if (steer) {
await chidori.log("scope changed mid-run", { from: steer.from, ...steer.payload });
brief = { ...brief, ...steer.payload }; // re-scope without restarting
}
draft = await revise(draft, review.payload.notes, brief);
}
});
/** Stand-in for the expensive drafting step (LLM + retrieval). Local + offline. */
async function writeDraft(brief: Brief): Promise<string> {
await chidori.log("writing draft", { topic: brief.topic, scope: brief.scope });
return `# ${brief.topic} (for ${brief.audience})\n\nInitial draft body.`;
}
/** Stand-in for the revision step. Appends the reviewer's notes to the draft. */
async function revise(draft: string, notes: string, brief: Brief): Promise<string> {
await chidori.log("revising draft", { notes, scope: brief.scope });
return `${draft}\n\n## Revision\nAddressed: ${notes}`;
}