setup-agent 0.0.3 → 0.0.5
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 +11 -5
- package/dist/cli.d.ts +51 -46
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +81 -22
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +3 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +12 -4
- package/dist/client.js.map +1 -1
- package/dist/host.d.ts +7 -9
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +82 -75
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -2
- package/dist/presets.d.ts +23 -25
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +4 -3
- package/dist/presets.js.map +1 -1
- package/dist/protocol.d.ts +279 -262
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +35 -4
- package/dist/protocol.js.map +1 -1
- package/package.json +20 -13
- package/src/cli.test.ts +615 -0
- package/src/cli.ts +118 -29
- package/src/client.test.ts +54 -0
- package/src/client.ts +12 -4
- package/src/host.test.ts +1030 -0
- package/src/host.ts +52 -9
- package/src/index.ts +3 -0
- package/src/presets.ts +4 -3
- package/src/protocol.test.ts +128 -0
- package/src/protocol.ts +103 -5
package/dist/host.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { classifyChoiceAnswer, currentPlan, initialCheckoutState, isAgentPresent, isClosed, isValidModelAnswer, parsePreviewUrl } from "./protocol.js";
|
|
1
|
+
import { classifyChoiceAnswer, currentPlan, initialCheckoutState, isAgentPresent, isClosed, isValidEntryPointAnswer, isValidEntryPointInput, isValidModelAnswer, parseEntryPointOptions, parsePreviewUrl } from "./protocol.js";
|
|
2
2
|
import { resource } from "@assistant-ui/tap";
|
|
3
3
|
import { StatewireReject, getStateHost, useStatewireCommands, useStatewireState } from "statewire/host";
|
|
4
4
|
//#region src/host.ts
|
|
@@ -36,7 +36,7 @@ const useCheckoutHost = (restored) => {
|
|
|
36
36
|
return step;
|
|
37
37
|
};
|
|
38
38
|
const findInput = (inputId) => {
|
|
39
|
-
const input = state.inputs.find((
|
|
39
|
+
const input = state.inputs.find((candidate) => candidate.id === inputId);
|
|
40
40
|
if (!input) throw reject("unknown-input", `no input "${inputId}"`);
|
|
41
41
|
if (input.status !== "open") throw reject("input-closed", `input "${inputId}" is ${input.status}`);
|
|
42
42
|
return input;
|
|
@@ -47,15 +47,20 @@ const useCheckoutHost = (restored) => {
|
|
|
47
47
|
};
|
|
48
48
|
const ask = (seed) => {
|
|
49
49
|
const kind = seed.kind ?? "text";
|
|
50
|
+
const options = kind === "entry-point" ? parseEntryPointOptions(seed.options) : seed.options;
|
|
51
|
+
if (kind === "entry-point" && !isValidEntryPointInput(seed)) throw reject("invalid-input", "an entry-point input needs a prompt and 1–3 distinct options with descriptions, form factors, placements and triggers; at most one is recommended");
|
|
50
52
|
if (kind === "product") {
|
|
51
53
|
if (!seed.product) throw reject("invalid-input", "a product input names a product");
|
|
52
54
|
if (state.products.some((product) => product.slug === seed.product)) throw reject("invalid-input", `"${seed.product}" is already part of this setup`);
|
|
53
55
|
} else if (kind !== "text" && !seed.options?.length) throw reject("invalid-input", `a ${kind} input needs options`);
|
|
54
|
-
if (seed.multiple && (kind !== "choice" || seed.options?.some((option) => option.variants))) throw reject("invalid-input", "only a choice whose options have no variants accepts several answers");
|
|
55
|
-
|
|
56
|
+
if (seed.multiple && (kind !== "choice" || seed.options?.some((option) => option.variants?.length))) throw reject("invalid-input", "only a choice whose options have no variants accepts several answers");
|
|
57
|
+
const unique = (ids) => new Set(ids).size === ids.length;
|
|
58
|
+
if (!unique(seed.options?.map((option) => option.id) ?? []) || seed.options?.some((option) => !unique(option.variants?.map((variant) => variant.id) ?? []))) throw reject("invalid-input", "option and variant ids must be unique");
|
|
59
|
+
if (seed.options?.some((option) => option.id.includes(":") || option.variants?.some((variant) => variant.id.includes(":")))) throw reject("invalid-input", "option and variant ids cannot contain \":\", which separates them in an answer");
|
|
60
|
+
if (seed.default !== void 0 && !seed.options?.some((option) => option.id === seed.default)) throw reject("invalid-input", `no option "${seed.default}" to default to`);
|
|
56
61
|
if (seed.stepId !== void 0) findStep(seed.stepId);
|
|
57
62
|
const id = `q${state.inputs.length + 1}`;
|
|
58
|
-
const
|
|
63
|
+
const stepId = seed.stepId ?? state.steps.find((step) => step.status === "active")?.id;
|
|
59
64
|
state.inputs.push({
|
|
60
65
|
id,
|
|
61
66
|
phase: state.status,
|
|
@@ -63,46 +68,46 @@ const useCheckoutHost = (restored) => {
|
|
|
63
68
|
...seed.preset !== void 0 && { preset: seed.preset },
|
|
64
69
|
prompt: seed.prompt,
|
|
65
70
|
...seed.placeholder !== void 0 && { placeholder: seed.placeholder },
|
|
66
|
-
...
|
|
71
|
+
...options !== void 0 && { options },
|
|
67
72
|
...seed.multiple && { multiple: true },
|
|
68
73
|
...kind === "product" && { product: seed.product },
|
|
69
74
|
...seed.default !== void 0 && { default: seed.default },
|
|
70
75
|
...seed.help !== void 0 && { help: seed.help },
|
|
71
76
|
optional: seed.optional ?? false,
|
|
72
77
|
status: "open",
|
|
73
|
-
...
|
|
78
|
+
...stepId !== void 0 && { stepId },
|
|
74
79
|
createdAt: Date.now()
|
|
75
80
|
});
|
|
76
81
|
return { inputId: id };
|
|
77
82
|
};
|
|
78
83
|
const dismissOpenInputs = () => {
|
|
79
|
-
for (const
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
for (const input of state.inputs) if (input.status === "open") {
|
|
85
|
+
input.status = "dismissed";
|
|
86
|
+
input.answeredAt = Date.now();
|
|
82
87
|
}
|
|
83
88
|
};
|
|
84
|
-
const log = (text,
|
|
89
|
+
const log = (text, stepId) => {
|
|
85
90
|
state.log.push({
|
|
86
91
|
id: `l${state.log.length + 1}`,
|
|
87
92
|
role: "agent",
|
|
88
93
|
phase: state.status,
|
|
89
94
|
at: Date.now(),
|
|
90
95
|
text,
|
|
91
|
-
...
|
|
96
|
+
...stepId !== void 0 && { stepId }
|
|
92
97
|
});
|
|
93
98
|
};
|
|
94
|
-
const updateStep = (
|
|
95
|
-
const changed =
|
|
96
|
-
|
|
97
|
-
if (note !== void 0)
|
|
98
|
-
if (changed && (status === "done" || status === "skipped")) log(`${status === "done" ? "Completed" : "Skipped"}: ${
|
|
99
|
+
const updateStep = (step, status, note) => {
|
|
100
|
+
const changed = step.status !== status;
|
|
101
|
+
step.status = status;
|
|
102
|
+
if (note !== void 0) step.note = note;
|
|
103
|
+
if (changed && (status === "done" || status === "skipped")) log(`${status === "done" ? "Completed" : "Skipped"}: ${step.title}${step.note ? `\n\n${step.note}` : ""}`, step.id);
|
|
99
104
|
};
|
|
100
105
|
const commands = useStatewireCommands({
|
|
101
|
-
"checkout/create": ({ id
|
|
102
|
-
if (state.id ===
|
|
106
|
+
"checkout/create": ({ id, products, instructions }) => {
|
|
107
|
+
if (state.id === id) throw reject("already-created", "the setup already exists");
|
|
103
108
|
const fresh = initialCheckoutState();
|
|
104
109
|
if (!isAgentPresent(state)) state.agent = fresh.agent;
|
|
105
|
-
state.id =
|
|
110
|
+
state.id = id;
|
|
106
111
|
state.status = fresh.status;
|
|
107
112
|
delete state.completion;
|
|
108
113
|
state.plans = fresh.plans;
|
|
@@ -111,65 +116,66 @@ const useCheckoutHost = (restored) => {
|
|
|
111
116
|
state.log = fresh.log;
|
|
112
117
|
state.createdAt = Date.now();
|
|
113
118
|
state.instructions = instructions?.trim() ?? "";
|
|
114
|
-
state.products = products.map((
|
|
115
|
-
slug:
|
|
116
|
-
name:
|
|
117
|
-
...
|
|
119
|
+
state.products = products.map((product) => ({
|
|
120
|
+
slug: product.slug,
|
|
121
|
+
name: product.name,
|
|
122
|
+
...product.guide !== void 0 && { guide: product.guide }
|
|
118
123
|
}));
|
|
119
124
|
},
|
|
120
125
|
"checkout/begin-plan": () => {
|
|
121
126
|
requireOpen();
|
|
122
127
|
if (state.status === "waiting") state.status = "planning";
|
|
123
128
|
},
|
|
124
|
-
"checkout/answer": ({ inputId
|
|
129
|
+
"checkout/answer": ({ inputId, answer, note }) => {
|
|
125
130
|
requireOpen();
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
128
|
-
if (
|
|
129
|
-
if (
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
const input = findInput(inputId);
|
|
132
|
+
if (input.kind === "choice" && classifyChoiceAnswer(input, answer) === "invalid") throw reject("invalid-answer", input.multiple ? `"${answer}" is not a JSON array of option ids` : `"${answer}" names an option without a valid variant`);
|
|
133
|
+
if (input.kind === "entry-point" && !isValidEntryPointAnswer(input, answer)) throw reject("invalid-answer", "an entry-point answer is an option id");
|
|
134
|
+
if (input.kind === "model" && !isValidModelAnswer(input, answer)) throw reject("invalid-answer", "a model answer is JSON with provider and model");
|
|
135
|
+
if (input.kind === "product") throw reject("invalid-answer", "a product input is answered with checkout/add-product");
|
|
136
|
+
if (input.kind === "text" && answer.trim() === "") throw reject("invalid-answer", "an answer cannot be empty");
|
|
137
|
+
input.status = "answered";
|
|
138
|
+
input.answeredAt = Date.now();
|
|
139
|
+
input.answer = answer;
|
|
140
|
+
if (note !== void 0 && note.trim() !== "") input.note = note.trim();
|
|
135
141
|
},
|
|
136
|
-
"checkout/add-product": ({ inputId
|
|
142
|
+
"checkout/add-product": ({ inputId, product }) => {
|
|
137
143
|
requireOpen();
|
|
138
|
-
const
|
|
139
|
-
if (
|
|
140
|
-
if (!state.products.some((entry) => entry.slug ===
|
|
141
|
-
slug:
|
|
142
|
-
name:
|
|
143
|
-
...
|
|
144
|
+
const input = findInput(inputId);
|
|
145
|
+
if (input.kind !== "product" || input.product !== product.slug) throw reject("invalid-answer", `input "${inputId}" does not propose "${product.slug}"`);
|
|
146
|
+
if (!state.products.some((entry) => entry.slug === product.slug)) state.products.push({
|
|
147
|
+
slug: product.slug,
|
|
148
|
+
name: product.name,
|
|
149
|
+
...product.guide !== void 0 && { guide: product.guide }
|
|
144
150
|
});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
151
|
+
input.status = "answered";
|
|
152
|
+
input.answer = "added";
|
|
153
|
+
input.answeredAt = Date.now();
|
|
148
154
|
},
|
|
149
|
-
"checkout/message": ({ text
|
|
155
|
+
"checkout/message": ({ text }) => {
|
|
150
156
|
requireOpen();
|
|
151
|
-
if (
|
|
152
|
-
const
|
|
157
|
+
if (text.trim() === "") throw reject("empty-message", "a message cannot be empty");
|
|
158
|
+
const stepId = state.steps.find((step) => step.status === "active")?.id;
|
|
153
159
|
state.log.push({
|
|
154
160
|
id: `l${state.log.length + 1}`,
|
|
155
161
|
role: "user",
|
|
156
162
|
phase: state.status,
|
|
157
|
-
...
|
|
163
|
+
...stepId !== void 0 && { stepId },
|
|
158
164
|
at: Date.now(),
|
|
159
|
-
text:
|
|
165
|
+
text: text.trim()
|
|
160
166
|
});
|
|
161
167
|
},
|
|
162
168
|
"agent/ack": ({ messageId }) => {
|
|
163
169
|
requireOpen();
|
|
164
|
-
const message = state.log.find((
|
|
170
|
+
const message = state.log.find((entry) => entry.id === messageId && entry.role === "user");
|
|
165
171
|
if (!message) throw reject("unknown-message", `no user message "${messageId}"`);
|
|
166
172
|
message.acknowledgedAt ??= Date.now();
|
|
167
173
|
},
|
|
168
|
-
"checkout/dismiss": ({ inputId
|
|
174
|
+
"checkout/dismiss": ({ inputId }) => {
|
|
169
175
|
requireOpen();
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
176
|
+
const input = findInput(inputId);
|
|
177
|
+
input.status = "dismissed";
|
|
178
|
+
input.answeredAt = Date.now();
|
|
173
179
|
},
|
|
174
180
|
"checkout/plan": (params) => {
|
|
175
181
|
requireOpen();
|
|
@@ -187,8 +193,9 @@ const useCheckoutHost = (restored) => {
|
|
|
187
193
|
plan.decidedAt = Date.now();
|
|
188
194
|
plan.feedback = params.feedback.trim();
|
|
189
195
|
},
|
|
190
|
-
"checkout/cancel": () => {
|
|
191
|
-
if (state.
|
|
196
|
+
"checkout/cancel": ({ id } = {}) => {
|
|
197
|
+
if (state.createdAt === null || isClosed(state)) return;
|
|
198
|
+
if (id !== void 0 && id !== state.id) return;
|
|
192
199
|
state.status = "cancelled";
|
|
193
200
|
dismissOpenInputs();
|
|
194
201
|
},
|
|
@@ -199,15 +206,15 @@ const useCheckoutHost = (restored) => {
|
|
|
199
206
|
state.status = "done";
|
|
200
207
|
log("Setup complete.");
|
|
201
208
|
},
|
|
202
|
-
"agent/intro": ({ kind
|
|
209
|
+
"agent/intro": ({ kind } = {}) => {
|
|
203
210
|
requireOpen();
|
|
204
211
|
if (state.agent.introducedAt === null) state.agent.introducedAt = Date.now();
|
|
205
|
-
if (
|
|
212
|
+
if (kind !== void 0) state.agent.kind = kind;
|
|
206
213
|
},
|
|
207
|
-
"agent/hello": ({ cwd, kind
|
|
214
|
+
"agent/hello": ({ cwd, kind } = {}) => {
|
|
208
215
|
touchAgent();
|
|
209
216
|
if (cwd !== void 0) state.agent.cwd = cwd;
|
|
210
|
-
if (
|
|
217
|
+
if (kind !== void 0) state.agent.kind = kind;
|
|
211
218
|
},
|
|
212
219
|
"agent/heartbeat": () => {
|
|
213
220
|
touchAgent();
|
|
@@ -229,42 +236,42 @@ const useCheckoutHost = (restored) => {
|
|
|
229
236
|
});
|
|
230
237
|
return { revision };
|
|
231
238
|
},
|
|
232
|
-
"agent/add-step": ({ title, detail, product
|
|
239
|
+
"agent/add-step": ({ title, detail, product, active }) => {
|
|
233
240
|
requireInstalling();
|
|
234
|
-
if (
|
|
235
|
-
const
|
|
241
|
+
if (product !== void 0 && product.trim() === "") throw reject("invalid-input", "a step's product identifier cannot be empty");
|
|
242
|
+
const id = `s${state.steps.length + 1}`;
|
|
236
243
|
state.steps.push({
|
|
237
|
-
id
|
|
244
|
+
id,
|
|
238
245
|
title,
|
|
239
246
|
...detail !== void 0 && { detail },
|
|
240
247
|
status: active ? "active" : "pending",
|
|
241
|
-
...
|
|
248
|
+
...product !== void 0 && { product },
|
|
242
249
|
createdAt: Date.now()
|
|
243
250
|
});
|
|
244
|
-
return { stepId:
|
|
251
|
+
return { stepId: id };
|
|
245
252
|
},
|
|
246
|
-
"agent/step": ({ stepId
|
|
253
|
+
"agent/step": ({ stepId, status, note }) => {
|
|
247
254
|
requireInstalling();
|
|
248
|
-
const
|
|
249
|
-
updateStep(
|
|
255
|
+
const step = findStep(stepId);
|
|
256
|
+
updateStep(step, status, note);
|
|
250
257
|
},
|
|
251
|
-
"agent/ask": (
|
|
258
|
+
"agent/ask": (seed) => {
|
|
252
259
|
requireStarted();
|
|
253
260
|
touchAgent();
|
|
254
|
-
return ask(
|
|
261
|
+
return ask(seed);
|
|
255
262
|
},
|
|
256
|
-
"agent/log": ({ text
|
|
263
|
+
"agent/log": ({ text }) => {
|
|
257
264
|
requireOpen();
|
|
258
265
|
touchAgent();
|
|
259
|
-
const
|
|
260
|
-
log(
|
|
266
|
+
const stepId = state.steps.find((step) => step.status === "active")?.id;
|
|
267
|
+
log(text, stepId);
|
|
261
268
|
},
|
|
262
269
|
"agent/done": ({ summary, preview } = {}) => {
|
|
263
270
|
requireInstalling();
|
|
264
271
|
const previewUrl = parsePreviewUrl(preview?.trim());
|
|
265
272
|
if (preview !== void 0 && !previewUrl) throw reject("invalid-preview", "a preview is an http URL on localhost, such as http://localhost:3000");
|
|
266
273
|
touchAgent();
|
|
267
|
-
for (const
|
|
274
|
+
for (const step of state.steps) if (step.status === "pending" || step.status === "active") updateStep(step, "done");
|
|
268
275
|
log(summary?.trim() || "Everything is installed.");
|
|
269
276
|
state.completion = {
|
|
270
277
|
proposedAt: Date.now(),
|
package/dist/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","names":["resource","StatewireReject","getStateHost","useStatewireCommands","useStatewireState","classifyChoiceAnswer","currentPlan","initialCheckoutState","isAgentPresent","isClosed","isValidModelAnswer","parsePreviewUrl","Checkout","reject","reason","RejectionReason","message","payload","useCheckoutHost","restored","state","State","initial","undefined","snapshot","Partial","version","agent","requireOpen","createdAt","status","requireStarted","requireInstalling","findStep","stepId","step","steps","find","candidate","id","findInput","inputId","input","inputs","touchAgent","lastSeenAt","Date","now","connected","ask","seed","InputSeed","kind","product","products","some","slug","options","length","multiple","option","variants","default","push","phase","preset","prompt","placeholder","help","optional","dismissOpenInputs","answeredAt","log","text","role","at","updateStep","Step","StepStatus","note","changed","title","commands","Commands","checkout/create","instructions","fresh","completion","plans","trim","map","name","guide","checkout/begin-plan","checkout/answer","answer","checkout/add-product","entry","checkout/message","agent/ack","messageId","acknowledgedAt","checkout/dismiss","params","plan","decision","decidedAt","feedback","checkout/cancel","checkout/finish","agent/intro","introducedAt","agent/hello","cwd","agent/heartbeat","agent/bye","agent/plan","markdown","revision","submittedAt","agent/add-step","detail","active","agent/step","agent/log","agent/done","summary","preview","previewUrl","proposedAt","href","source","subscribe","listener","setOnChange","CheckoutHost"],"sources":["../src/host.ts"],"sourcesContent":["import { resource } from \"@assistant-ui/tap\";\nimport {\n StatewireReject,\n getStateHost,\n useStatewireCommands,\n useStatewireState,\n} from \"statewire/host\";\nimport {\n classifyChoiceAnswer,\n currentPlan,\n initialCheckoutState,\n isAgentPresent,\n isClosed,\n isValidModelAnswer,\n parsePreviewUrl,\n} from \"./protocol\";\nimport type { Checkout } from \"./protocol\";\n\nconst reject = (reason: Checkout.RejectionReason, message: string) =>\n new StatewireReject(message, { payload: { reason } });\n\nconst useCheckoutHost = (restored: unknown) => {\n const [state] = useStatewireState<Checkout.State>(() => {\n const initial = initialCheckoutState();\n if (restored === undefined) return initial;\n const snapshot = restored as Partial<Checkout.State>;\n if (snapshot.version !== 2) return initial;\n return {\n ...initial,\n ...snapshot,\n agent: { ...initial.agent, ...snapshot.agent },\n };\n });\n\n const requireOpen = () => {\n if (state.createdAt === null) {\n throw reject(\"not-created\", \"the setup has not been created yet\");\n }\n if (isClosed(state)) {\n throw reject(\"closed\", `the setup is ${state.status}`);\n }\n };\n\n const requireStarted = () => {\n requireOpen();\n if (state.status === \"waiting\") {\n throw reject(\n \"planning-not-started\",\n 'ask the user to return to their browser and click \"Begin plan\"; wait with \"wait start\"',\n );\n }\n };\n\n const requireInstalling = () => {\n requireOpen();\n if (state.status !== \"installing\") {\n throw reject(\n \"plan-required\",\n \"steps run once the user has approved a plan; submit one with plan\",\n );\n }\n };\n\n const findStep = (stepId: string) => {\n const step = state.steps.find((candidate) => candidate.id === stepId);\n if (!step) throw reject(\"unknown-step\", `no step \"${stepId}\"`);\n return step;\n };\n\n const findInput = (inputId: string) => {\n const input = state.inputs.find((candidate) => candidate.id === inputId);\n if (!input) throw reject(\"unknown-input\", `no input \"${inputId}\"`);\n if (input.status !== \"open\") {\n throw reject(\"input-closed\", `input \"${inputId}\" is ${input.status}`);\n }\n return input;\n };\n\n const touchAgent = () => {\n state.agent.lastSeenAt = Date.now();\n state.agent.connected = true;\n };\n\n const ask = (seed: Checkout.InputSeed & { stepId?: string }) => {\n const kind = seed.kind ?? \"text\";\n if (kind === \"product\") {\n if (!seed.product) {\n throw reject(\"invalid-input\", \"a product input names a product\");\n }\n if (state.products.some((product) => product.slug === seed.product)) {\n throw reject(\n \"invalid-input\",\n `\"${seed.product}\" is already part of this setup`,\n );\n }\n } else if (kind !== \"text\" && !seed.options?.length) {\n throw reject(\"invalid-input\", `a ${kind} input needs options`);\n }\n if (\n seed.multiple &&\n (kind !== \"choice\" || seed.options?.some((option) => option.variants))\n ) {\n throw reject(\n \"invalid-input\",\n \"only a choice whose options have no variants accepts several answers\",\n );\n }\n if (\n seed.default !== undefined &&\n !seed.options?.some((option) => option.id === seed.default)\n ) {\n throw reject(\n \"invalid-input\",\n `no option \"${seed.default}\" to default to`,\n );\n }\n if (seed.stepId !== undefined) findStep(seed.stepId);\n const id = `q${state.inputs.length + 1}`;\n const stepId =\n seed.stepId ?? state.steps.find((step) => step.status === \"active\")?.id;\n state.inputs.push({\n id,\n phase: state.status,\n kind,\n ...(seed.preset !== undefined && { preset: seed.preset }),\n prompt: seed.prompt,\n ...(seed.placeholder !== undefined && { placeholder: seed.placeholder }),\n ...(seed.options !== undefined && { options: seed.options }),\n ...(seed.multiple && { multiple: true }),\n ...(kind === \"product\" && { product: seed.product }),\n ...(seed.default !== undefined && { default: seed.default }),\n ...(seed.help !== undefined && { help: seed.help }),\n optional: seed.optional ?? false,\n status: \"open\",\n ...(stepId !== undefined && { stepId }),\n createdAt: Date.now(),\n });\n return { inputId: id };\n };\n\n const dismissOpenInputs = () => {\n for (const input of state.inputs) {\n if (input.status === \"open\") {\n input.status = \"dismissed\";\n input.answeredAt = Date.now();\n }\n }\n };\n\n const log = (text: string, stepId?: string) => {\n state.log.push({\n id: `l${state.log.length + 1}`,\n role: \"agent\",\n phase: state.status,\n at: Date.now(),\n text,\n ...(stepId !== undefined && { stepId }),\n });\n };\n\n const updateStep = (\n step: Checkout.Step,\n status: Checkout.StepStatus,\n note?: string,\n ) => {\n const changed = step.status !== status;\n step.status = status;\n if (note !== undefined) step.note = note;\n if (changed && (status === \"done\" || status === \"skipped\")) {\n log(\n `${status === \"done\" ? \"Completed\" : \"Skipped\"}: ${step.title}${step.note ? `\\n\\n${step.note}` : \"\"}`,\n step.id,\n );\n }\n };\n\n const commands = useStatewireCommands<Checkout.Commands>({\n \"checkout/create\": ({ id, products, instructions }) => {\n if (state.id === id) {\n throw reject(\"already-created\", \"the setup already exists\");\n }\n const fresh = initialCheckoutState();\n if (!isAgentPresent(state)) state.agent = fresh.agent;\n state.id = id;\n state.status = fresh.status;\n delete state.completion;\n state.plans = fresh.plans;\n state.steps = fresh.steps;\n state.inputs = fresh.inputs;\n state.log = fresh.log;\n state.createdAt = Date.now();\n state.instructions = instructions?.trim() ?? \"\";\n state.products = products.map((product) => ({\n slug: product.slug,\n name: product.name,\n ...(product.guide !== undefined && { guide: product.guide }),\n }));\n },\n \"checkout/begin-plan\": () => {\n requireOpen();\n if (state.status === \"waiting\") state.status = \"planning\";\n },\n \"checkout/answer\": ({ inputId, answer, note }) => {\n requireOpen();\n const input = findInput(inputId);\n if (\n input.kind === \"choice\" &&\n classifyChoiceAnswer(input, answer) === \"invalid\"\n ) {\n throw reject(\n \"invalid-answer\",\n input.multiple\n ? `\"${answer}\" is not a JSON array of option ids`\n : `\"${answer}\" names an option without a valid variant`,\n );\n }\n if (input.kind === \"model\" && !isValidModelAnswer(input, answer)) {\n throw reject(\n \"invalid-answer\",\n \"a model answer is JSON with provider and model\",\n );\n }\n if (input.kind === \"product\") {\n throw reject(\n \"invalid-answer\",\n \"a product input is answered with checkout/add-product\",\n );\n }\n if (input.kind === \"text\" && answer.trim() === \"\") {\n throw reject(\"invalid-answer\", \"an answer cannot be empty\");\n }\n input.status = \"answered\";\n input.answeredAt = Date.now();\n input.answer = answer;\n if (note !== undefined && note.trim() !== \"\") input.note = note.trim();\n },\n \"checkout/add-product\": ({ inputId, product }) => {\n requireOpen();\n const input = findInput(inputId);\n if (input.kind !== \"product\" || input.product !== product.slug) {\n throw reject(\n \"invalid-answer\",\n `input \"${inputId}\" does not propose \"${product.slug}\"`,\n );\n }\n if (!state.products.some((entry) => entry.slug === product.slug)) {\n state.products.push({\n slug: product.slug,\n name: product.name,\n ...(product.guide !== undefined && { guide: product.guide }),\n });\n }\n input.status = \"answered\";\n input.answer = \"added\";\n input.answeredAt = Date.now();\n },\n \"checkout/message\": ({ text }) => {\n requireOpen();\n if (text.trim() === \"\")\n throw reject(\"empty-message\", \"a message cannot be empty\");\n const stepId = state.steps.find((step) => step.status === \"active\")?.id;\n state.log.push({\n id: `l${state.log.length + 1}`,\n role: \"user\",\n phase: state.status,\n ...(stepId !== undefined && { stepId }),\n at: Date.now(),\n text: text.trim(),\n });\n },\n \"agent/ack\": ({ messageId }) => {\n requireOpen();\n const message = state.log.find(\n (entry) => entry.id === messageId && entry.role === \"user\",\n );\n if (!message)\n throw reject(\"unknown-message\", `no user message \"${messageId}\"`);\n message.acknowledgedAt ??= Date.now();\n },\n \"checkout/dismiss\": ({ inputId }) => {\n requireOpen();\n const input = findInput(inputId);\n input.status = \"dismissed\";\n input.answeredAt = Date.now();\n },\n \"checkout/plan\": (params) => {\n requireOpen();\n const plan = currentPlan(state);\n if (plan === undefined) {\n throw reject(\"no-plan\", \"the agent has not proposed a plan yet\");\n }\n if (plan.status !== \"proposed\") {\n throw reject(\"plan-decided\", `the plan is already ${plan.status}`);\n }\n if (params.decision === \"approve\") {\n plan.status = \"approved\";\n plan.decidedAt = Date.now();\n state.status = \"installing\";\n return;\n }\n if (params.feedback.trim() === \"\") {\n throw reject(\"feedback-required\", \"say what should change\");\n }\n plan.status = \"changes-requested\";\n plan.decidedAt = Date.now();\n plan.feedback = params.feedback.trim();\n },\n \"checkout/cancel\": () => {\n if (state.status === \"cancelled\") return;\n state.status = \"cancelled\";\n dismissOpenInputs();\n },\n \"checkout/finish\": () => {\n requireOpen();\n if (state.completion === undefined) {\n throw reject(\n \"finish-not-proposed\",\n \"the agent has not proposed to finish yet\",\n );\n }\n dismissOpenInputs();\n state.status = \"done\";\n log(\"Setup complete.\");\n },\n \"agent/intro\": ({ kind } = {}) => {\n requireOpen();\n if (state.agent.introducedAt === null) {\n state.agent.introducedAt = Date.now();\n }\n if (kind !== undefined) state.agent.kind = kind;\n },\n \"agent/hello\": ({ cwd, kind } = {}) => {\n touchAgent();\n if (cwd !== undefined) state.agent.cwd = cwd;\n if (kind !== undefined) state.agent.kind = kind;\n },\n \"agent/heartbeat\": () => {\n touchAgent();\n },\n \"agent/bye\": () => {\n state.agent.connected = false;\n },\n \"agent/plan\": ({ markdown }) => {\n requireStarted();\n touchAgent();\n if (markdown.trim() === \"\") {\n throw reject(\"empty-plan\", \"the plan is empty\");\n }\n if (state.status === \"installing\") {\n throw reject(\"plan-decided\", \"the plan is already approved\");\n }\n const revision = state.plans.length + 1;\n state.plans.push({\n revision,\n markdown: markdown.trim(),\n status: \"proposed\",\n submittedAt: Date.now(),\n });\n return { revision };\n },\n \"agent/add-step\": ({ title, detail, product, active }) => {\n requireInstalling();\n if (\n product !== undefined &&\n !state.products.some((candidate) => candidate.slug === product)\n ) {\n throw reject(\"unknown-product\", `no product \"${product}\"`);\n }\n const id = `s${state.steps.length + 1}`;\n state.steps.push({\n id,\n title,\n ...(detail !== undefined && { detail }),\n status: active ? \"active\" : \"pending\",\n ...(product !== undefined && { product }),\n createdAt: Date.now(),\n });\n return { stepId: id };\n },\n \"agent/step\": ({ stepId, status, note }) => {\n requireInstalling();\n const step = findStep(stepId);\n updateStep(step, status, note);\n },\n \"agent/ask\": (seed) => {\n requireStarted();\n touchAgent();\n return ask(seed);\n },\n \"agent/log\": ({ text }) => {\n requireOpen();\n touchAgent();\n const stepId = state.steps.find((step) => step.status === \"active\")?.id;\n log(text, stepId);\n },\n \"agent/done\": ({ summary, preview } = {}) => {\n requireInstalling();\n const previewUrl = parsePreviewUrl(preview?.trim());\n if (preview !== undefined && !previewUrl) {\n throw reject(\n \"invalid-preview\",\n \"a preview is an http URL on localhost, such as http://localhost:3000\",\n );\n }\n touchAgent();\n for (const step of state.steps) {\n if (step.status === \"pending\" || step.status === \"active\") {\n updateStep(step, \"done\");\n }\n }\n log(summary?.trim() || \"Everything is installed.\");\n state.completion = {\n proposedAt: Date.now(),\n ...(previewUrl && { preview: previewUrl.href }),\n };\n },\n });\n\n const source = getStateHost(state)!;\n return {\n state,\n commands,\n snapshot: source.snapshot,\n subscribe: (listener: () => void) => {\n source.setOnChange(listener);\n return () => source.setOnChange(null);\n },\n };\n};\n\n/** The setup statewire host; pass the durable object's restored snapshot. */\nexport const CheckoutHost = (restored?: unknown) =>\n resource(useCheckoutHost)(restored);\n\nexport type { Checkout } from \"./protocol\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"host.js","names":[],"sources":["../src/host.ts"],"sourcesContent":["import { resource } from \"@assistant-ui/tap\";\nimport {\n StatewireReject,\n getStateHost,\n useStatewireCommands,\n useStatewireState,\n} from \"statewire/host\";\nimport {\n classifyChoiceAnswer,\n currentPlan,\n initialCheckoutState,\n isAgentPresent,\n isClosed,\n isValidModelAnswer,\n isValidEntryPointAnswer,\n isValidEntryPointInput,\n parseEntryPointOptions,\n parsePreviewUrl,\n} from \"./protocol\";\nimport type { Checkout } from \"./protocol\";\n\nconst reject = (reason: Checkout.RejectionReason, message: string) =>\n new StatewireReject(message, { payload: { reason } });\n\nconst useCheckoutHost = (restored: unknown) => {\n const [state] = useStatewireState<Checkout.State>(() => {\n const initial = initialCheckoutState();\n if (restored === undefined) return initial;\n const snapshot = restored as Partial<Checkout.State>;\n if (snapshot.version !== 2) return initial;\n return {\n ...initial,\n ...snapshot,\n agent: { ...initial.agent, ...snapshot.agent },\n };\n });\n\n const requireOpen = () => {\n if (state.createdAt === null) {\n throw reject(\"not-created\", \"the setup has not been created yet\");\n }\n if (isClosed(state)) {\n throw reject(\"closed\", `the setup is ${state.status}`);\n }\n };\n\n const requireStarted = () => {\n requireOpen();\n if (state.status === \"waiting\") {\n throw reject(\n \"planning-not-started\",\n 'ask the user to return to their browser and click \"Begin plan\"; wait with \"wait start\"',\n );\n }\n };\n\n const requireInstalling = () => {\n requireOpen();\n if (state.status !== \"installing\") {\n throw reject(\n \"plan-required\",\n \"steps run once the user has approved a plan; submit one with plan\",\n );\n }\n };\n\n const findStep = (stepId: string) => {\n const step = state.steps.find((candidate) => candidate.id === stepId);\n if (!step) throw reject(\"unknown-step\", `no step \"${stepId}\"`);\n return step;\n };\n\n const findInput = (inputId: string) => {\n const input = state.inputs.find((candidate) => candidate.id === inputId);\n if (!input) throw reject(\"unknown-input\", `no input \"${inputId}\"`);\n if (input.status !== \"open\") {\n throw reject(\"input-closed\", `input \"${inputId}\" is ${input.status}`);\n }\n return input;\n };\n\n const touchAgent = () => {\n state.agent.lastSeenAt = Date.now();\n state.agent.connected = true;\n };\n\n const ask = (seed: Checkout.InputSeed & { stepId?: string }) => {\n const kind = seed.kind ?? \"text\";\n const options =\n kind === \"entry-point\"\n ? parseEntryPointOptions(seed.options)\n : seed.options;\n if (kind === \"entry-point\" && !isValidEntryPointInput(seed)) {\n throw reject(\n \"invalid-input\",\n \"an entry-point input needs a prompt and 1–3 distinct options with descriptions, form factors, placements and triggers; at most one is recommended\",\n );\n }\n if (kind === \"product\") {\n if (!seed.product) {\n throw reject(\"invalid-input\", \"a product input names a product\");\n }\n if (state.products.some((product) => product.slug === seed.product)) {\n throw reject(\n \"invalid-input\",\n `\"${seed.product}\" is already part of this setup`,\n );\n }\n } else if (kind !== \"text\" && !seed.options?.length) {\n throw reject(\"invalid-input\", `a ${kind} input needs options`);\n }\n if (\n seed.multiple &&\n (kind !== \"choice\" ||\n seed.options?.some((option) => option.variants?.length))\n ) {\n throw reject(\n \"invalid-input\",\n \"only a choice whose options have no variants accepts several answers\",\n );\n }\n const unique = (ids: readonly string[]) => new Set(ids).size === ids.length;\n if (\n !unique(seed.options?.map((option) => option.id) ?? []) ||\n seed.options?.some(\n (option) =>\n !unique(option.variants?.map((variant) => variant.id) ?? []),\n )\n ) {\n throw reject(\"invalid-input\", \"option and variant ids must be unique\");\n }\n if (\n seed.options?.some(\n (option) =>\n option.id.includes(\":\") ||\n option.variants?.some((variant) => variant.id.includes(\":\")),\n )\n ) {\n throw reject(\n \"invalid-input\",\n 'option and variant ids cannot contain \":\", which separates them in an answer',\n );\n }\n if (\n seed.default !== undefined &&\n !seed.options?.some((option) => option.id === seed.default)\n ) {\n throw reject(\n \"invalid-input\",\n `no option \"${seed.default}\" to default to`,\n );\n }\n if (seed.stepId !== undefined) findStep(seed.stepId);\n const id = `q${state.inputs.length + 1}`;\n const stepId =\n seed.stepId ?? state.steps.find((step) => step.status === \"active\")?.id;\n state.inputs.push({\n id,\n phase: state.status,\n kind,\n ...(seed.preset !== undefined && { preset: seed.preset }),\n prompt: seed.prompt,\n ...(seed.placeholder !== undefined && { placeholder: seed.placeholder }),\n ...(options !== undefined && { options }),\n ...(seed.multiple && { multiple: true }),\n ...(kind === \"product\" && { product: seed.product }),\n ...(seed.default !== undefined && { default: seed.default }),\n ...(seed.help !== undefined && { help: seed.help }),\n optional: seed.optional ?? false,\n status: \"open\",\n ...(stepId !== undefined && { stepId }),\n createdAt: Date.now(),\n });\n return { inputId: id };\n };\n\n const dismissOpenInputs = () => {\n for (const input of state.inputs) {\n if (input.status === \"open\") {\n input.status = \"dismissed\";\n input.answeredAt = Date.now();\n }\n }\n };\n\n const log = (text: string, stepId?: string) => {\n state.log.push({\n id: `l${state.log.length + 1}`,\n role: \"agent\",\n phase: state.status,\n at: Date.now(),\n text,\n ...(stepId !== undefined && { stepId }),\n });\n };\n\n const updateStep = (\n step: Checkout.Step,\n status: Checkout.StepStatus,\n note?: string,\n ) => {\n const changed = step.status !== status;\n step.status = status;\n if (note !== undefined) step.note = note;\n if (changed && (status === \"done\" || status === \"skipped\")) {\n log(\n `${status === \"done\" ? \"Completed\" : \"Skipped\"}: ${step.title}${step.note ? `\\n\\n${step.note}` : \"\"}`,\n step.id,\n );\n }\n };\n\n const commands = useStatewireCommands<Checkout.Commands>({\n \"checkout/create\": ({ id, products, instructions }) => {\n if (state.id === id) {\n throw reject(\"already-created\", \"the setup already exists\");\n }\n const fresh = initialCheckoutState();\n if (!isAgentPresent(state)) state.agent = fresh.agent;\n state.id = id;\n state.status = fresh.status;\n delete state.completion;\n state.plans = fresh.plans;\n state.steps = fresh.steps;\n state.inputs = fresh.inputs;\n state.log = fresh.log;\n state.createdAt = Date.now();\n state.instructions = instructions?.trim() ?? \"\";\n state.products = products.map((product) => ({\n slug: product.slug,\n name: product.name,\n ...(product.guide !== undefined && { guide: product.guide }),\n }));\n },\n \"checkout/begin-plan\": () => {\n requireOpen();\n if (state.status === \"waiting\") state.status = \"planning\";\n },\n \"checkout/answer\": ({ inputId, answer, note }) => {\n requireOpen();\n const input = findInput(inputId);\n if (\n input.kind === \"choice\" &&\n classifyChoiceAnswer(input, answer) === \"invalid\"\n ) {\n throw reject(\n \"invalid-answer\",\n input.multiple\n ? `\"${answer}\" is not a JSON array of option ids`\n : `\"${answer}\" names an option without a valid variant`,\n );\n }\n if (\n input.kind === \"entry-point\" &&\n !isValidEntryPointAnswer(input, answer)\n ) {\n throw reject(\"invalid-answer\", \"an entry-point answer is an option id\");\n }\n if (input.kind === \"model\" && !isValidModelAnswer(input, answer)) {\n throw reject(\n \"invalid-answer\",\n \"a model answer is JSON with provider and model\",\n );\n }\n if (input.kind === \"product\") {\n throw reject(\n \"invalid-answer\",\n \"a product input is answered with checkout/add-product\",\n );\n }\n if (input.kind === \"text\" && answer.trim() === \"\") {\n throw reject(\"invalid-answer\", \"an answer cannot be empty\");\n }\n input.status = \"answered\";\n input.answeredAt = Date.now();\n input.answer = answer;\n if (note !== undefined && note.trim() !== \"\") input.note = note.trim();\n },\n \"checkout/add-product\": ({ inputId, product }) => {\n requireOpen();\n const input = findInput(inputId);\n if (input.kind !== \"product\" || input.product !== product.slug) {\n throw reject(\n \"invalid-answer\",\n `input \"${inputId}\" does not propose \"${product.slug}\"`,\n );\n }\n if (!state.products.some((entry) => entry.slug === product.slug)) {\n state.products.push({\n slug: product.slug,\n name: product.name,\n ...(product.guide !== undefined && { guide: product.guide }),\n });\n }\n input.status = \"answered\";\n input.answer = \"added\";\n input.answeredAt = Date.now();\n },\n \"checkout/message\": ({ text }) => {\n requireOpen();\n if (text.trim() === \"\")\n throw reject(\"empty-message\", \"a message cannot be empty\");\n const stepId = state.steps.find((step) => step.status === \"active\")?.id;\n state.log.push({\n id: `l${state.log.length + 1}`,\n role: \"user\",\n phase: state.status,\n ...(stepId !== undefined && { stepId }),\n at: Date.now(),\n text: text.trim(),\n });\n },\n \"agent/ack\": ({ messageId }) => {\n requireOpen();\n const message = state.log.find(\n (entry) => entry.id === messageId && entry.role === \"user\",\n );\n if (!message)\n throw reject(\"unknown-message\", `no user message \"${messageId}\"`);\n message.acknowledgedAt ??= Date.now();\n },\n \"checkout/dismiss\": ({ inputId }) => {\n requireOpen();\n const input = findInput(inputId);\n input.status = \"dismissed\";\n input.answeredAt = Date.now();\n },\n \"checkout/plan\": (params) => {\n requireOpen();\n const plan = currentPlan(state);\n if (plan === undefined) {\n throw reject(\"no-plan\", \"the agent has not proposed a plan yet\");\n }\n if (plan.status !== \"proposed\") {\n throw reject(\"plan-decided\", `the plan is already ${plan.status}`);\n }\n if (params.decision === \"approve\") {\n plan.status = \"approved\";\n plan.decidedAt = Date.now();\n state.status = \"installing\";\n return;\n }\n if (params.feedback.trim() === \"\") {\n throw reject(\"feedback-required\", \"say what should change\");\n }\n plan.status = \"changes-requested\";\n plan.decidedAt = Date.now();\n plan.feedback = params.feedback.trim();\n },\n \"checkout/cancel\": ({ id } = {}) => {\n if (state.createdAt === null || isClosed(state)) return;\n if (id !== undefined && id !== state.id) return;\n state.status = \"cancelled\";\n dismissOpenInputs();\n },\n \"checkout/finish\": () => {\n requireOpen();\n if (state.completion === undefined) {\n throw reject(\n \"finish-not-proposed\",\n \"the agent has not proposed to finish yet\",\n );\n }\n dismissOpenInputs();\n state.status = \"done\";\n log(\"Setup complete.\");\n },\n \"agent/intro\": ({ kind } = {}) => {\n requireOpen();\n if (state.agent.introducedAt === null) {\n state.agent.introducedAt = Date.now();\n }\n if (kind !== undefined) state.agent.kind = kind;\n },\n \"agent/hello\": ({ cwd, kind } = {}) => {\n touchAgent();\n if (cwd !== undefined) state.agent.cwd = cwd;\n if (kind !== undefined) state.agent.kind = kind;\n },\n \"agent/heartbeat\": () => {\n touchAgent();\n },\n \"agent/bye\": () => {\n state.agent.connected = false;\n },\n \"agent/plan\": ({ markdown }) => {\n requireStarted();\n touchAgent();\n if (markdown.trim() === \"\") {\n throw reject(\"empty-plan\", \"the plan is empty\");\n }\n if (state.status === \"installing\") {\n throw reject(\"plan-decided\", \"the plan is already approved\");\n }\n const revision = state.plans.length + 1;\n state.plans.push({\n revision,\n markdown: markdown.trim(),\n status: \"proposed\",\n submittedAt: Date.now(),\n });\n return { revision };\n },\n \"agent/add-step\": ({ title, detail, product, active }) => {\n requireInstalling();\n if (product !== undefined && product.trim() === \"\") {\n throw reject(\n \"invalid-input\",\n \"a step's product identifier cannot be empty\",\n );\n }\n const id = `s${state.steps.length + 1}`;\n state.steps.push({\n id,\n title,\n ...(detail !== undefined && { detail }),\n status: active ? \"active\" : \"pending\",\n ...(product !== undefined && { product }),\n createdAt: Date.now(),\n });\n return { stepId: id };\n },\n \"agent/step\": ({ stepId, status, note }) => {\n requireInstalling();\n const step = findStep(stepId);\n updateStep(step, status, note);\n },\n \"agent/ask\": (seed) => {\n requireStarted();\n touchAgent();\n return ask(seed);\n },\n \"agent/log\": ({ text }) => {\n requireOpen();\n touchAgent();\n const stepId = state.steps.find((step) => step.status === \"active\")?.id;\n log(text, stepId);\n },\n \"agent/done\": ({ summary, preview } = {}) => {\n requireInstalling();\n const previewUrl = parsePreviewUrl(preview?.trim());\n if (preview !== undefined && !previewUrl) {\n throw reject(\n \"invalid-preview\",\n \"a preview is an http URL on localhost, such as http://localhost:3000\",\n );\n }\n touchAgent();\n for (const step of state.steps) {\n if (step.status === \"pending\" || step.status === \"active\") {\n updateStep(step, \"done\");\n }\n }\n log(summary?.trim() || \"Everything is installed.\");\n state.completion = {\n proposedAt: Date.now(),\n ...(previewUrl && { preview: previewUrl.href }),\n };\n },\n });\n\n const source = getStateHost(state)!;\n return {\n state,\n commands,\n snapshot: source.snapshot,\n subscribe: (listener: () => void) => {\n source.setOnChange(listener);\n return () => source.setOnChange(null);\n },\n };\n};\n\n/** The setup statewire host; pass the durable object's restored snapshot. */\nexport const CheckoutHost = (restored?: unknown) =>\n resource(useCheckoutHost)(restored);\n\nexport type { Checkout } from \"./protocol\";\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
1
|
+
export type { Checkout, OptionIcon } from "./protocol.js";
|
|
2
|
+
export { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, OPTION_ICONS, classifyChoiceAnswer, currentPlan, initialCheckoutState, finishProposed, followedUpSinceProposal, isAgentPresent, isClosed, isOptionIcon, isValidModelAnswer, isValidEntryPointAnswer, isValidEntryPointInput, openInputs, parseChoiceAnswer, parseEntryPointOptions, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, stepProgress, } from "./protocol.js";
|
|
3
|
+
export { INPUT_PRESETS, isPresetId, presetInput } from "./presets.js";
|
|
4
|
+
export type { PresetId, PresetOverrides } from "./presets.js";
|
|
5
|
+
export { connectCheckout } from "./client.js";
|
|
6
|
+
export type { CheckoutClient } from "./client.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,sBAAmB;AACvD,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,uBAAuB,EACvB,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,kBAAkB,EAClB,uBAAuB,EACvB,sBAAsB,EACtB,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,YAAY,GACb,sBAAmB;AACpB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,qBAAkB;AACnE,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,qBAAkB;AAC3D,OAAO,EAAE,eAAe,EAAE,oBAAiB;AAC3C,YAAY,EAAE,cAAc,EAAE,oBAAiB"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { connectCheckout } from "./client.js";
|
|
2
2
|
import { INPUT_PRESETS, isPresetId, presetInput } from "./presets.js";
|
|
3
|
-
import { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, OPTION_ICONS, classifyChoiceAnswer, currentPlan, finishProposed, followedUpSinceProposal, initialCheckoutState, isAgentPresent, isClosed, isOptionIcon, isValidModelAnswer, openInputs, parseChoiceAnswer, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, stepProgress } from "./protocol.js";
|
|
4
|
-
export { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, INPUT_PRESETS, OPTION_ICONS, classifyChoiceAnswer, connectCheckout, currentPlan, finishProposed, followedUpSinceProposal, initialCheckoutState, isAgentPresent, isClosed, isOptionIcon, isPresetId, isValidModelAnswer, openInputs, parseChoiceAnswer, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, presetInput, stepProgress };
|
|
3
|
+
import { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, OPTION_ICONS, classifyChoiceAnswer, currentPlan, finishProposed, followedUpSinceProposal, initialCheckoutState, isAgentPresent, isClosed, isOptionIcon, isValidEntryPointAnswer, isValidEntryPointInput, isValidModelAnswer, openInputs, parseChoiceAnswer, parseEntryPointOptions, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, stepProgress } from "./protocol.js";
|
|
4
|
+
export { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, INPUT_PRESETS, OPTION_ICONS, classifyChoiceAnswer, connectCheckout, currentPlan, finishProposed, followedUpSinceProposal, initialCheckoutState, isAgentPresent, isClosed, isOptionIcon, isPresetId, isValidEntryPointAnswer, isValidEntryPointInput, isValidModelAnswer, openInputs, parseChoiceAnswer, parseEntryPointOptions, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, presetInput, stepProgress };
|
package/dist/presets.d.ts
CHANGED
|
@@ -1,32 +1,30 @@
|
|
|
1
|
-
import { Checkout } from "./protocol.js";
|
|
2
|
-
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
stepId?: string;
|
|
1
|
+
import type { Checkout } from "./protocol.js";
|
|
2
|
+
export type PresetId = "framework" | "llm-provider" | "project";
|
|
3
|
+
export type PresetOverrides = {
|
|
4
|
+
/** Restrict the options to these ids, in this order; one id locks the choice in. */
|
|
5
|
+
only?: readonly string[];
|
|
6
|
+
/** Options the agent found itself, listed before the preset's own; `only` leaves them out. */
|
|
7
|
+
found?: readonly {
|
|
8
|
+
id: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
}[];
|
|
11
|
+
default?: string;
|
|
12
|
+
optional?: boolean;
|
|
13
|
+
stepId?: string;
|
|
15
14
|
};
|
|
16
15
|
type Preset = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
kind: Checkout.InputKind;
|
|
17
|
+
prompt: string;
|
|
18
|
+
options: Checkout.ChoiceOption[];
|
|
19
|
+
default?: string;
|
|
20
|
+
help: Checkout.InputHelp;
|
|
22
21
|
};
|
|
23
22
|
/** The standard inputs an agent can ask with `ask --preset <id>`. */
|
|
24
|
-
declare const INPUT_PRESETS: Record<PresetId, Preset>;
|
|
25
|
-
declare const isPresetId: (id: string) => id is PresetId;
|
|
23
|
+
export declare const INPUT_PRESETS: Record<PresetId, Preset>;
|
|
24
|
+
export declare const isPresetId: (id: string) => id is PresetId;
|
|
26
25
|
/** Build the seed for a standard input, throwing on an unknown preset, option or default. */
|
|
27
|
-
declare const presetInput: (id: PresetId, overrides?: PresetOverrides) => Checkout.InputSeed & {
|
|
28
|
-
|
|
26
|
+
export declare const presetInput: (id: PresetId, overrides?: PresetOverrides) => Checkout.InputSeed & {
|
|
27
|
+
stepId?: string;
|
|
29
28
|
};
|
|
30
|
-
|
|
31
|
-
export { INPUT_PRESETS, PresetId, PresetOverrides, isPresetId, presetInput };
|
|
29
|
+
export {};
|
|
32
30
|
//# sourceMappingURL=presets.d.ts.map
|
package/dist/presets.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presets.d.ts","
|
|
1
|
+
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../src/presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,sBAAmB;AAO3C,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,cAAc,GAAG,SAAS,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG;IAC5B,oFAAoF;IACpF,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,8FAA8F;IAC9F,KAAK,CAAC,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,KAAK,MAAM,GAAG;IACZ,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC;CAC1B,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CA+GlD,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,IAAI,MAAM,KAAG,EAAE,IAAI,QACZ,CAAC;AAEnC,6FAA6F;AAC7F,eAAO,MAAM,WAAW,GACtB,IAAI,QAAQ,EACZ,YAAW,eAAoB,KAC9B,QAAQ,CAAC,SAAS,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAqCxC,CAAC"}
|
package/dist/presets.js
CHANGED
|
@@ -139,7 +139,7 @@ const INPUT_PRESETS = {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
};
|
|
142
|
-
const isPresetId = (id) => id
|
|
142
|
+
const isPresetId = (id) => Object.hasOwn(INPUT_PRESETS, id);
|
|
143
143
|
/** Build the seed for a standard input, throwing on an unknown preset, option or default. */
|
|
144
144
|
const presetInput = (id, overrides = {}) => {
|
|
145
145
|
const preset = INPUT_PRESETS[id];
|
|
@@ -148,13 +148,14 @@ const presetInput = (id, overrides = {}) => {
|
|
|
148
148
|
if (!option) throw new Error(`preset "${id}" has no option "${optionId}"`);
|
|
149
149
|
return option;
|
|
150
150
|
}) : preset.options;
|
|
151
|
-
const
|
|
151
|
+
const found = (overrides.found ?? []).map((option) => {
|
|
152
152
|
if (option.id.includes(":") || preset.options.some((o) => o.id === option.id)) throw new Error(`preset "${id}" cannot list "${option.id}" as an option`);
|
|
153
153
|
return {
|
|
154
154
|
...option,
|
|
155
155
|
label: option.id
|
|
156
156
|
};
|
|
157
|
-
})
|
|
157
|
+
});
|
|
158
|
+
const options = overrides.only ? listed : [...found, ...listed];
|
|
158
159
|
const fallback = options.some((o) => o.id === preset.default) ? preset.default : void 0;
|
|
159
160
|
const chosen = overrides.default ?? fallback;
|
|
160
161
|
if (chosen !== void 0 && !options.some((o) => o.id === chosen)) throw new Error(`preset "${id}" has no option "${chosen}" to default to`);
|
package/dist/presets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presets.js","names":[
|
|
1
|
+
{"version":3,"file":"presets.js","names":[],"sources":["../src/presets.ts"],"sourcesContent":["import type { Checkout } from \"./protocol\";\n\nconst TYPESCRIPT = { id: \"typescript\", label: \"TypeScript\" };\nconst PYTHON = { id: \"python\", label: \"Python\" };\n\nconst DOCS = \"https://www.assistant-ui.com\";\n\nexport type PresetId = \"framework\" | \"llm-provider\" | \"project\";\n\nexport type PresetOverrides = {\n /** Restrict the options to these ids, in this order; one id locks the choice in. */\n only?: readonly string[];\n /** Options the agent found itself, listed before the preset's own; `only` leaves them out. */\n found?: readonly { id: string; description?: string }[];\n default?: string;\n optional?: boolean;\n stepId?: string;\n};\n\ntype Preset = {\n kind: Checkout.InputKind;\n prompt: string;\n options: Checkout.ChoiceOption[];\n default?: string;\n help: Checkout.InputHelp;\n};\n\n/** The standard inputs an agent can ask with `ask --preset <id>`. */\nexport const INPUT_PRESETS: Record<PresetId, Preset> = {\n framework: {\n kind: \"choice\",\n prompt: \"Which agent framework runs your backend?\",\n options: [\n {\n id: \"ai-sdk\",\n label: \"Vercel AI SDK\",\n description: \"Shortest path for Next.js and React\",\n icon: \"vercel\",\n variants: [TYPESCRIPT],\n },\n {\n id: \"mastra\",\n label: \"Mastra\",\n description: \"Workflows, memory and tools\",\n icon: \"mastra\",\n variants: [TYPESCRIPT],\n },\n {\n id: \"langgraph\",\n label: \"LangGraph\",\n description: \"Stateful graphs, Python or TypeScript\",\n icon: \"langgraph\",\n variants: [PYTHON, TYPESCRIPT],\n },\n ],\n help: {\n summary:\n \"Already on one of these? Pick it. Starting fresh in Next.js, the AI SDK is the shortest path. Mastra adds workflows and memory in TypeScript. LangGraph fits a Python backend or multi-step graphs that pause for a human.\",\n href: `${DOCS}/docs/runtimes/pick-a-runtime`,\n },\n },\n \"llm-provider\": {\n kind: \"model\",\n prompt: \"Which model should the assistant use?\",\n options: [\n {\n id: \"openai\",\n label: \"OpenAI\",\n description: \"OPENAI_API_KEY\",\n icon: \"openai\",\n },\n {\n id: \"anthropic\",\n label: \"Anthropic\",\n description: \"ANTHROPIC_API_KEY\",\n icon: \"anthropic\",\n },\n {\n id: \"google\",\n label: \"Google Gemini\",\n description: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n icon: \"google\",\n },\n {\n id: \"openrouter\",\n label: \"OpenRouter\",\n description: \"OPENROUTER_API_KEY\",\n icon: \"openrouter\",\n },\n { id: \"xai\", label: \"xAI\", description: \"XAI_API_KEY\", icon: \"xai\" },\n {\n id: \"mistral\",\n label: \"Mistral\",\n description: \"MISTRAL_API_KEY\",\n icon: \"mistral\",\n },\n {\n id: \"deepseek\",\n label: \"DeepSeek\",\n description: \"DEEPSEEK_API_KEY\",\n icon: \"deepseek\",\n },\n { id: \"groq\", label: \"Groq\", description: \"GROQ_API_KEY\", icon: \"groq\" },\n {\n id: \"fireworks\",\n label: \"Fireworks AI\",\n description: \"FIREWORKS_API_KEY\",\n icon: \"fireworks\",\n },\n ],\n default: \"openai\",\n help: {\n summary:\n \"Any of them works with every framework here. Pick the one whose API key you already have, or OpenRouter to reach many models with one key.\",\n },\n },\n project: {\n kind: \"choice\",\n prompt: \"Which project should this go into?\",\n options: [\n {\n id: \"new\",\n label: \"New project\",\n description: \"Start a fresh React app here\",\n variants: [\n { id: \"next\", label: \"Next.js\" },\n { id: \"vite\", label: \"Vite\" },\n { id: \"react-router\", label: \"React Router\" },\n { id: \"tanstack-start\", label: \"TanStack Start\" },\n { id: \"expo\", label: \"Expo\" },\n ],\n },\n ],\n help: {\n summary:\n \"Pick the app the assistant should live in. For a new project, Next.js is the shortest path: the assistant-ui CLI scaffolds the chat route with it. Pick Vite, React Router or TanStack Start when the rest of your stack already uses them, and Expo for a native app.\",\n href: `${DOCS}/docs/installation`,\n },\n },\n};\n\nexport const isPresetId = (id: string): id is PresetId =>\n Object.hasOwn(INPUT_PRESETS, id);\n\n/** Build the seed for a standard input, throwing on an unknown preset, option or default. */\nexport const presetInput = (\n id: PresetId,\n overrides: PresetOverrides = {},\n): Checkout.InputSeed & { stepId?: string } => {\n const preset = INPUT_PRESETS[id];\n const listed = overrides.only\n ? overrides.only.map((optionId) => {\n const option = preset.options.find((c) => c.id === optionId);\n if (!option)\n throw new Error(`preset \"${id}\" has no option \"${optionId}\"`);\n return option;\n })\n : preset.options;\n const found = (overrides.found ?? []).map((option) => {\n if (\n option.id.includes(\":\") ||\n preset.options.some((o) => o.id === option.id)\n ) {\n throw new Error(`preset \"${id}\" cannot list \"${option.id}\" as an option`);\n }\n return { ...option, label: option.id };\n });\n const options = overrides.only ? listed : [...found, ...listed];\n const fallback = options.some((o) => o.id === preset.default)\n ? preset.default\n : undefined;\n const chosen = overrides.default ?? fallback;\n if (chosen !== undefined && !options.some((o) => o.id === chosen)) {\n throw new Error(`preset \"${id}\" has no option \"${chosen}\" to default to`);\n }\n return {\n kind: preset.kind,\n preset: id,\n prompt: preset.prompt,\n options,\n ...(chosen !== undefined && { default: chosen }),\n help: preset.help,\n optional: overrides.optional ?? false,\n ...(overrides.stepId !== undefined && { stepId: overrides.stepId }),\n };\n};\n"],"mappings":"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"}
|