-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathquickstart.ts
More file actions
94 lines (88 loc) · 3.32 KB
/
Copy pathquickstart.ts
File metadata and controls
94 lines (88 loc) · 3.32 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
94
/**
* quickstart — the smallest complete refine loop. Offline, deterministic, no API keys.
*
* One driver runs a worker, reads the worker's real output, and writes the next prompt
* from it until a check passes. The worker here is a scripted stand-in so the loop runs
* anywhere; swap `inProcessSandboxClient` for a real sandbox, CLI-harness, or router
* backend without changing the driver. This is the same shape as examples/driver-loop,
* which annotates every seam.
*
* Run: pnpm build && pnpm tsx examples/quickstart/quickstart.ts
*/
import type { AgentProfile } from '@tangle-network/agent-interface'
import {
inProcessSandboxClient,
runAgentRounds,
type TerminalDecision,
} from '@tangle-network/agent-runtime/kernel'
import type { SandboxEvent } from '@tangle-network/sandbox'
type Task = { prompt: string }
type Note = { note: string }
const noteWriterProfile = {
name: 'note-writer',
harness: 'cli-base',
model: { provider: 'scripted', default: 'scripted/note-writer' },
} satisfies AgentProfile
// A stand-in worker: it obeys the prompt it is given. Swap for a real backend later.
const worker = inProcessSandboxClient({
onPrompt: (prompt): SandboxEvent[] => [
{
type: 'result',
data: {
result: {
note: prompt.includes('rollback')
? 'Shipped one-click restore with an instant rollback path.'
: 'Shipped one-click restore.',
},
},
},
{ type: 'done', data: { outcome: { type: 'completed' } } },
],
})
const result = await runAgentRounds({
task: { prompt: 'Write a one-line release note for one-click restore.' } as Task,
agentRun: { profile: noteWriterProfile, taskToPrompt: (t) => t.prompt },
output: {
parse: (events): Note => {
for (const ev of events) {
if (ev.type === 'result') {
const r = (ev as { data?: { result?: unknown } }).data?.result
if (r && typeof r === 'object' && 'note' in r) return r as Note
}
}
return { note: '' }
},
},
validator: {
validate: async (out) => ({
valid: out.note.includes('rollback'),
score: out.note.includes('rollback') ? 1 : 0,
}),
},
driver: {
// Trace label only; it never selects a strategy or a decision path.
name: 'release-note-driver',
plan: async (task, history) => {
const last = history[history.length - 1]
if (!last) return [task] // shot 0: run the task as written
if (last.verdict?.valid || history.length >= 3) return [] // done, or out of shots
// The core move: read the last worker's real output, write the next prompt FROM it.
return [{ prompt: `Rewrite "${last.output?.note}" to mention the rollback path.` }]
},
// 'refine' is this driver's own word — any non-terminal value continues the
// loop. 'pick-winner' and 'fail' are kernel keywords from TERMINAL_DECISIONS.
decide: (history): 'refine' | TerminalDecision =>
history.some((shot) => shot.verdict?.valid)
? 'pick-winner'
: history.length < 3
? 'refine'
: 'fail',
},
ctx: { sandboxClient: worker },
})
for (const shot of result.iterations) {
console.log(
`shot ${shot.index}: ${shot.verdict?.valid ? 'PASS' : 'reject'} — "${shot.output?.note}"`,
)
}
console.log(`decision: ${result.decision} — winner: shot ${result.winner?.iterationIndex}`)