setup-agent 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/dist/cli.d.ts +0 -20
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +40 -88
- package/dist/cli.js.map +1 -1
- package/dist/guide.d.ts +45 -0
- package/dist/guide.d.ts.map +1 -0
- package/dist/guide.js +133 -0
- package/dist/guide.js.map +1 -0
- package/dist/host.js +1 -1
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/needs.d.ts +3 -1
- package/dist/needs.d.ts.map +1 -1
- package/dist/needs.js +20 -17
- package/dist/needs.js.map +1 -1
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +19 -24
- package/dist/sdk.js.map +1 -1
- package/dist/stream.d.ts +6 -2
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +23 -16
- package/dist/stream.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.test.ts +23 -150
- package/src/cli.ts +51 -135
- package/src/guide.test.ts +246 -0
- package/src/guide.ts +177 -0
- package/src/host.ts +1 -1
- package/src/index.ts +2 -0
- package/src/needs.test.ts +4 -5
- package/src/needs.ts +38 -23
- package/src/sdk.test.ts +8 -14
- package/src/sdk.ts +21 -31
- package/src/stream.test.ts +8 -5
- package/src/stream.ts +32 -26
- package/src/tests/guide-fixture.ts +39 -0
- package/src/tests/setup.ts +15 -0
package/dist/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 browser: { ...initial.browser!, ...snapshot.browser },\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 (\n seed.redeemKeyHash !== undefined &&\n (kind !== \"model\" || !/^[0-9a-f]{64}$/.test(seed.redeemKeyHash))\n ) {\n throw reject(\"invalid-input\", \"invalid model redeem key hash\");\n }\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 ...(seed.redeemKeyHash !== undefined && {\n redeemKeyHash: seed.redeemKeyHash,\n }),\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 (\n (params.id !== undefined && params.id !== state.id) ||\n (params.revision !== undefined && params.revision !== plan.revision)\n ) {\n throw reject(\n \"stale-plan\",\n \"review the current setup plan before deciding\",\n );\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 \"browser/heartbeat\": () => {\n state.browser = { lastSeenAt: Date.now(), connected: true };\n },\n \"browser/bye\": () => {\n if (state.browser) state.browser.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":";;;;AAqBA,MAAM,UAAU,QAAkC,YAChD,IAAI,gBAAgB,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAEtD,MAAM,mBAAmB,aAAsB;CAC7C,MAAM,CAAC,SAAS,wBAAwC;EACtD,MAAM,UAAU,qBAAqB;EACrC,IAAI,aAAa,KAAA,GAAW,OAAO;EACnC,MAAM,WAAW;EACjB,IAAI,SAAS,YAAY,GAAG,OAAO;EACnC,OAAO;GACL,GAAG;GACH,GAAG;GACH,OAAO;IAAE,GAAG,QAAQ;IAAO,GAAG,SAAS;GAAM;GAC7C,SAAS;IAAE,GAAG,QAAQ;IAAU,GAAG,SAAS;GAAQ;EACtD;CACF,CAAC;CAED,MAAM,oBAAoB;EACxB,IAAI,MAAM,cAAc,MACtB,MAAM,OAAO,eAAe,oCAAoC;EAElE,IAAI,SAAS,KAAK,GAChB,MAAM,OAAO,UAAU,gBAAgB,MAAM,QAAQ;CAEzD;CAEA,MAAM,uBAAuB;EAC3B,YAAY;EACZ,IAAI,MAAM,WAAW,WACnB,MAAM,OACJ,wBACA,4FACF;CAEJ;CAEA,MAAM,0BAA0B;EAC9B,YAAY;EACZ,IAAI,MAAM,WAAW,cACnB,MAAM,OACJ,iBACA,mEACF;CAEJ;CAEA,MAAM,YAAY,WAAmB;EACnC,MAAM,OAAO,MAAM,MAAM,MAAM,cAAc,UAAU,OAAO,MAAM;EACpE,IAAI,CAAC,MAAM,MAAM,OAAO,gBAAgB,YAAY,OAAO,EAAE;EAC7D,OAAO;CACT;CAEA,MAAM,aAAa,YAAoB;EACrC,MAAM,QAAQ,MAAM,OAAO,MAAM,cAAc,UAAU,OAAO,OAAO;EACvE,IAAI,CAAC,OAAO,MAAM,OAAO,iBAAiB,aAAa,QAAQ,EAAE;EACjE,IAAI,MAAM,WAAW,QACnB,MAAM,OAAO,gBAAgB,UAAU,QAAQ,OAAO,MAAM,QAAQ;EAEtE,OAAO;CACT;CAEA,MAAM,mBAAmB;EACvB,MAAM,MAAM,aAAa,KAAK,IAAI;EAClC,MAAM,MAAM,YAAY;CAC1B;CAEA,MAAM,OAAO,SAAmD;EAC9D,MAAM,OAAO,KAAK,QAAQ;EAC1B,IACE,KAAK,kBAAkB,KAAA,MACtB,SAAS,WAAW,CAAC,iBAAiB,KAAK,KAAK,aAAa,IAE9D,MAAM,OAAO,iBAAiB,+BAA+B;EAE/D,MAAM,UACJ,SAAS,gBACL,uBAAuB,KAAK,OAAO,IACnC,KAAK;EACX,IAAI,SAAS,iBAAiB,CAAC,uBAAuB,IAAI,GACxD,MAAM,OACJ,iBACA,mJACF;EAEF,IAAI,SAAS,WAAW;GACtB,IAAI,CAAC,KAAK,SACR,MAAM,OAAO,iBAAiB,iCAAiC;GAEjE,IAAI,MAAM,SAAS,MAAM,YAAY,QAAQ,SAAS,KAAK,OAAO,GAChE,MAAM,OACJ,iBACA,IAAI,KAAK,QAAQ,gCACnB;EAEJ,OAAO,IAAI,SAAS,UAAU,CAAC,KAAK,SAAS,QAC3C,MAAM,OAAO,iBAAiB,KAAK,KAAK,qBAAqB;EAE/D,IACE,KAAK,aACJ,SAAS,YACR,KAAK,SAAS,MAAM,WAAW,OAAO,UAAU,MAAM,IAExD,MAAM,OACJ,iBACA,sEACF;EAEF,MAAM,UAAU,QAA2B,IAAI,IAAI,GAAG,CAAC,CAAC,SAAS,IAAI;EACrE,IACE,CAAC,OAAO,KAAK,SAAS,KAAK,WAAW,OAAO,EAAE,KAAK,CAAC,CAAC,KACtD,KAAK,SAAS,MACX,WACC,CAAC,OAAO,OAAO,UAAU,KAAK,YAAY,QAAQ,EAAE,KAAK,CAAC,CAAC,CAC/D,GAEA,MAAM,OAAO,iBAAiB,uCAAuC;EAEvE,IACE,KAAK,SAAS,MACX,WACC,OAAO,GAAG,SAAS,GAAG,KACtB,OAAO,UAAU,MAAM,YAAY,QAAQ,GAAG,SAAS,GAAG,CAAC,CAC/D,GAEA,MAAM,OACJ,iBACA,gFACF;EAEF,IACE,KAAK,YAAY,KAAA,KACjB,CAAC,KAAK,SAAS,MAAM,WAAW,OAAO,OAAO,KAAK,OAAO,GAE1D,MAAM,OACJ,iBACA,cAAc,KAAK,QAAQ,gBAC7B;EAEF,IAAI,KAAK,WAAW,KAAA,GAAW,SAAS,KAAK,MAAM;EACnD,MAAM,KAAK,IAAI,MAAM,OAAO,SAAS;EACrC,MAAM,SACJ,KAAK,UAAU,MAAM,MAAM,MAAM,SAAS,KAAK,WAAW,QAAQ,CAAC,EAAE;EACvE,MAAM,OAAO,KAAK;GAChB;GACA,OAAO,MAAM;GACb;GACA,GAAI,KAAK,WAAW,KAAA,KAAa,EAAE,QAAQ,KAAK,OAAO;GACvD,QAAQ,KAAK;GACb,GAAI,KAAK,gBAAgB,KAAA,KAAa,EAAE,aAAa,KAAK,YAAY;GACtE,GAAI,YAAY,KAAA,KAAa,EAAE,QAAQ;GACvC,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK;GACtC,GAAI,SAAS,aAAa,EAAE,SAAS,KAAK,QAAQ;GAClD,GAAI,KAAK,YAAY,KAAA,KAAa,EAAE,SAAS,KAAK,QAAQ;GAC1D,GAAI,KAAK,SAAS,KAAA,KAAa,EAAE,MAAM,KAAK,KAAK;GACjD,GAAI,KAAK,kBAAkB,KAAA,KAAa,EACtC,eAAe,KAAK,cACtB;GACA,UAAU,KAAK,YAAY;GAC3B,QAAQ;GACR,GAAI,WAAW,KAAA,KAAa,EAAE,OAAO;GACrC,WAAW,KAAK,IAAI;EACtB,CAAC;EACD,OAAO,EAAE,SAAS,GAAG;CACvB;CAEA,MAAM,0BAA0B;EAC9B,KAAK,MAAM,SAAS,MAAM,QACxB,IAAI,MAAM,WAAW,QAAQ;GAC3B,MAAM,SAAS;GACf,MAAM,aAAa,KAAK,IAAI;EAC9B;CAEJ;CAEA,MAAM,OAAO,MAAc,WAAoB;EAC7C,MAAM,IAAI,KAAK;GACb,IAAI,IAAI,MAAM,IAAI,SAAS;GAC3B,MAAM;GACN,OAAO,MAAM;GACb,IAAI,KAAK,IAAI;GACb;GACA,GAAI,WAAW,KAAA,KAAa,EAAE,OAAO;EACvC,CAAC;CACH;CAEA,MAAM,cACJ,MACA,QACA,SACG;EACH,MAAM,UAAU,KAAK,WAAW;EAChC,KAAK,SAAS;EACd,IAAI,SAAS,KAAA,GAAW,KAAK,OAAO;EACpC,IAAI,YAAY,WAAW,UAAU,WAAW,YAC9C,IACE,GAAG,WAAW,SAAS,cAAc,UAAU,IAAI,KAAK,QAAQ,KAAK,OAAO,OAAO,KAAK,SAAS,MACjG,KAAK,EACP;CAEJ;CAEA,MAAM,WAAW,qBAAwC;EACvD,oBAAoB,EAAE,IAAI,UAAU,mBAAmB;GACrD,IAAI,MAAM,OAAO,IACf,MAAM,OAAO,mBAAmB,0BAA0B;GAE5D,MAAM,QAAQ,qBAAqB;GACnC,IAAI,CAAC,eAAe,KAAK,GAAG,MAAM,QAAQ,MAAM;GAChD,MAAM,KAAK;GACX,MAAM,SAAS,MAAM;GACrB,OAAO,MAAM;GACb,MAAM,QAAQ,MAAM;GACpB,MAAM,QAAQ,MAAM;GACpB,MAAM,SAAS,MAAM;GACrB,MAAM,MAAM,MAAM;GAClB,MAAM,YAAY,KAAK,IAAI;GAC3B,MAAM,eAAe,cAAc,KAAK,KAAK;GAC7C,MAAM,WAAW,SAAS,KAAK,aAAa;IAC1C,MAAM,QAAQ;IACd,MAAM,QAAQ;IACd,GAAI,QAAQ,UAAU,KAAA,KAAa,EAAE,OAAO,QAAQ,MAAM;GAC5D,EAAE;EACJ;EACA,6BAA6B;GAC3B,YAAY;GACZ,IAAI,MAAM,WAAW,WAAW,MAAM,SAAS;EACjD;EACA,oBAAoB,EAAE,SAAS,QAAQ,WAAW;GAChD,YAAY;GACZ,MAAM,QAAQ,UAAU,OAAO;GAC/B,IACE,MAAM,SAAS,YACf,qBAAqB,OAAO,MAAM,MAAM,WAExC,MAAM,OACJ,kBACA,MAAM,WACF,IAAI,OAAO,uCACX,IAAI,OAAO,0CACjB;GAEF,IACE,MAAM,SAAS,iBACf,CAAC,wBAAwB,OAAO,MAAM,GAEtC,MAAM,OAAO,kBAAkB,uCAAuC;GAExE,IAAI,MAAM,SAAS,WAAW,CAAC,mBAAmB,OAAO,MAAM,GAC7D,MAAM,OACJ,kBACA,gDACF;GAEF,IAAI,MAAM,SAAS,WACjB,MAAM,OACJ,kBACA,uDACF;GAEF,IAAI,MAAM,SAAS,UAAU,OAAO,KAAK,MAAM,IAC7C,MAAM,OAAO,kBAAkB,2BAA2B;GAE5D,MAAM,SAAS;GACf,MAAM,aAAa,KAAK,IAAI;GAC5B,MAAM,SAAS;GACf,IAAI,SAAS,KAAA,KAAa,KAAK,KAAK,MAAM,IAAI,MAAM,OAAO,KAAK,KAAK;EACvE;EACA,yBAAyB,EAAE,SAAS,cAAc;GAChD,YAAY;GACZ,MAAM,QAAQ,UAAU,OAAO;GAC/B,IAAI,MAAM,SAAS,aAAa,MAAM,YAAY,QAAQ,MACxD,MAAM,OACJ,kBACA,UAAU,QAAQ,sBAAsB,QAAQ,KAAK,EACvD;GAEF,IAAI,CAAC,MAAM,SAAS,MAAM,UAAU,MAAM,SAAS,QAAQ,IAAI,GAC7D,MAAM,SAAS,KAAK;IAClB,MAAM,QAAQ;IACd,MAAM,QAAQ;IACd,GAAI,QAAQ,UAAU,KAAA,KAAa,EAAE,OAAO,QAAQ,MAAM;GAC5D,CAAC;GAEH,MAAM,SAAS;GACf,MAAM,SAAS;GACf,MAAM,aAAa,KAAK,IAAI;EAC9B;EACA,qBAAqB,EAAE,WAAW;GAChC,YAAY;GACZ,IAAI,KAAK,KAAK,MAAM,IAClB,MAAM,OAAO,iBAAiB,2BAA2B;GAC3D,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS,KAAK,WAAW,QAAQ,CAAC,EAAE;GACrE,MAAM,IAAI,KAAK;IACb,IAAI,IAAI,MAAM,IAAI,SAAS;IAC3B,MAAM;IACN,OAAO,MAAM;IACb,GAAI,WAAW,KAAA,KAAa,EAAE,OAAO;IACrC,IAAI,KAAK,IAAI;IACb,MAAM,KAAK,KAAK;GAClB,CAAC;EACH;EACA,cAAc,EAAE,gBAAgB;GAC9B,YAAY;GACZ,MAAM,UAAU,MAAM,IAAI,MACvB,UAAU,MAAM,OAAO,aAAa,MAAM,SAAS,MACtD;GACA,IAAI,CAAC,SACH,MAAM,OAAO,mBAAmB,oBAAoB,UAAU,EAAE;GAClE,QAAQ,mBAAmB,KAAK,IAAI;EACtC;EACA,qBAAqB,EAAE,cAAc;GACnC,YAAY;GACZ,MAAM,QAAQ,UAAU,OAAO;GAC/B,MAAM,SAAS;GACf,MAAM,aAAa,KAAK,IAAI;EAC9B;EACA,kBAAkB,WAAW;GAC3B,YAAY;GACZ,MAAM,OAAO,YAAY,KAAK;GAC9B,IAAI,SAAS,KAAA,GACX,MAAM,OAAO,WAAW,uCAAuC;GAEjE,IACG,OAAO,OAAO,KAAA,KAAa,OAAO,OAAO,MAAM,MAC/C,OAAO,aAAa,KAAA,KAAa,OAAO,aAAa,KAAK,UAE3D,MAAM,OACJ,cACA,+CACF;GAEF,IAAI,KAAK,WAAW,YAClB,MAAM,OAAO,gBAAgB,uBAAuB,KAAK,QAAQ;GAEnE,IAAI,OAAO,aAAa,WAAW;IACjC,KAAK,SAAS;IACd,KAAK,YAAY,KAAK,IAAI;IAC1B,MAAM,SAAS;IACf;GACF;GACA,IAAI,OAAO,SAAS,KAAK,MAAM,IAC7B,MAAM,OAAO,qBAAqB,wBAAwB;GAE5D,KAAK,SAAS;GACd,KAAK,YAAY,KAAK,IAAI;GAC1B,KAAK,WAAW,OAAO,SAAS,KAAK;EACvC;EACA,oBAAoB,EAAE,OAAO,CAAC,MAAM;GAClC,IAAI,MAAM,cAAc,QAAQ,SAAS,KAAK,GAAG;GACjD,IAAI,OAAO,KAAA,KAAa,OAAO,MAAM,IAAI;GACzC,MAAM,SAAS;GACf,kBAAkB;EACpB;EACA,yBAAyB;GACvB,YAAY;GACZ,IAAI,MAAM,eAAe,KAAA,GACvB,MAAM,OACJ,uBACA,0CACF;GAEF,kBAAkB;GAClB,MAAM,SAAS;GACf,IAAI,iBAAiB;EACvB;EACA,gBAAgB,EAAE,SAAS,CAAC,MAAM;GAChC,YAAY;GACZ,IAAI,MAAM,MAAM,iBAAiB,MAC/B,MAAM,MAAM,eAAe,KAAK,IAAI;GAEtC,IAAI,SAAS,KAAA,GAAW,MAAM,MAAM,OAAO;EAC7C;EACA,gBAAgB,EAAE,KAAK,SAAS,CAAC,MAAM;GACrC,WAAW;GACX,IAAI,QAAQ,KAAA,GAAW,MAAM,MAAM,MAAM;GACzC,IAAI,SAAS,KAAA,GAAW,MAAM,MAAM,OAAO;EAC7C;EACA,yBAAyB;GACvB,WAAW;EACb;EACA,mBAAmB;GACjB,MAAM,MAAM,YAAY;EAC1B;EACA,2BAA2B;GACzB,MAAM,UAAU;IAAE,YAAY,KAAK,IAAI;IAAG,WAAW;GAAK;EAC5D;EACA,qBAAqB;GACnB,IAAI,MAAM,SAAS,MAAM,QAAQ,YAAY;EAC/C;EACA,eAAe,EAAE,eAAe;GAC9B,eAAe;GACf,WAAW;GACX,IAAI,SAAS,KAAK,MAAM,IACtB,MAAM,OAAO,cAAc,mBAAmB;GAEhD,IAAI,MAAM,WAAW,cACnB,MAAM,OAAO,gBAAgB,8BAA8B;GAE7D,MAAM,WAAW,MAAM,MAAM,SAAS;GACtC,MAAM,MAAM,KAAK;IACf;IACA,UAAU,SAAS,KAAK;IACxB,QAAQ;IACR,aAAa,KAAK,IAAI;GACxB,CAAC;GACD,OAAO,EAAE,SAAS;EACpB;EACA,mBAAmB,EAAE,OAAO,QAAQ,SAAS,aAAa;GACxD,kBAAkB;GAClB,IAAI,YAAY,KAAA,KAAa,QAAQ,KAAK,MAAM,IAC9C,MAAM,OACJ,iBACA,6CACF;GAEF,MAAM,KAAK,IAAI,MAAM,MAAM,SAAS;GACpC,MAAM,MAAM,KAAK;IACf;IACA;IACA,GAAI,WAAW,KAAA,KAAa,EAAE,OAAO;IACrC,QAAQ,SAAS,WAAW;IAC5B,GAAI,YAAY,KAAA,KAAa,EAAE,QAAQ;IACvC,WAAW,KAAK,IAAI;GACtB,CAAC;GACD,OAAO,EAAE,QAAQ,GAAG;EACtB;EACA,eAAe,EAAE,QAAQ,QAAQ,WAAW;GAC1C,kBAAkB;GAClB,MAAM,OAAO,SAAS,MAAM;GAC5B,WAAW,MAAM,QAAQ,IAAI;EAC/B;EACA,cAAc,SAAS;GACrB,eAAe;GACf,WAAW;GACX,OAAO,IAAI,IAAI;EACjB;EACA,cAAc,EAAE,WAAW;GACzB,YAAY;GACZ,WAAW;GACX,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS,KAAK,WAAW,QAAQ,CAAC,EAAE;GACrE,IAAI,MAAM,MAAM;EAClB;EACA,eAAe,EAAE,SAAS,YAAY,CAAC,MAAM;GAC3C,kBAAkB;GAClB,MAAM,aAAa,gBAAgB,SAAS,KAAK,CAAC;GAClD,IAAI,YAAY,KAAA,KAAa,CAAC,YAC5B,MAAM,OACJ,mBACA,sEACF;GAEF,WAAW;GACX,KAAK,MAAM,QAAQ,MAAM,OACvB,IAAI,KAAK,WAAW,aAAa,KAAK,WAAW,UAC/C,WAAW,MAAM,MAAM;GAG3B,IAAI,SAAS,KAAK,KAAK,0BAA0B;GACjD,MAAM,aAAa;IACjB,YAAY,KAAK,IAAI;IACrB,GAAI,cAAc,EAAE,SAAS,WAAW,KAAK;GAC/C;EACF;CACF,CAAC;CAED,MAAM,SAAS,aAAa,KAAK;CACjC,OAAO;EACL;EACA;EACA,UAAU,OAAO;EACjB,YAAY,aAAyB;GACnC,OAAO,YAAY,QAAQ;GAC3B,aAAa,OAAO,YAAY,IAAI;EACtC;CACF;AACF;;AAGA,MAAa,gBAAgB,aAC3B,SAAS,eAAe,CAAC,CAAC,QAAQ"}
|
|
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 browser: { ...initial.browser!, ...snapshot.browser },\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 'the setup page starts planning on its own once the user opens it; 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 (\n seed.redeemKeyHash !== undefined &&\n (kind !== \"model\" || !/^[0-9a-f]{64}$/.test(seed.redeemKeyHash))\n ) {\n throw reject(\"invalid-input\", \"invalid model redeem key hash\");\n }\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 ...(seed.redeemKeyHash !== undefined && {\n redeemKeyHash: seed.redeemKeyHash,\n }),\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 (\n (params.id !== undefined && params.id !== state.id) ||\n (params.revision !== undefined && params.revision !== plan.revision)\n ) {\n throw reject(\n \"stale-plan\",\n \"review the current setup plan before deciding\",\n );\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 \"browser/heartbeat\": () => {\n state.browser = { lastSeenAt: Date.now(), connected: true };\n },\n \"browser/bye\": () => {\n if (state.browser) state.browser.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
|
@@ -4,4 +4,6 @@ export { INPUT_PRESETS, isPresetId, presetInput } from "./presets.js";
|
|
|
4
4
|
export type { PresetId, PresetOverrides } from "./presets.js";
|
|
5
5
|
export { connectCheckout } from "./client.js";
|
|
6
6
|
export type { CheckoutClient } from "./client.js";
|
|
7
|
+
export { GUIDE_PROTOCOL, parseGuide } from "./guide.js";
|
|
8
|
+
export type { Guide, GuideContext, GuideStep, GuideTextKey } from "./guide.js";
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAY,CAAC;AACvD,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,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,MAAM,eAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAW,CAAC;AACnE,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,cAAW,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAU,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,aAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAY,CAAC;AACvD,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,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,MAAM,eAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAW,CAAC;AACnE,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,cAAW,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAU,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,aAAU,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAS,CAAC;AACrD,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, BROWSER_HEARTBEAT_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";
|
|
2
|
+
import { GUIDE_PROTOCOL, parseGuide } from "./guide.js";
|
|
2
3
|
import { connectCheckout } from "./client.js";
|
|
3
4
|
import { INPUT_PRESETS, isPresetId, presetInput } from "./presets.js";
|
|
4
|
-
export { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, BROWSER_HEARTBEAT_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 };
|
|
5
|
+
export { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, BROWSER_HEARTBEAT_MS, GUIDE_PROTOCOL, INPUT_PRESETS, OPTION_ICONS, classifyChoiceAnswer, connectCheckout, currentPlan, finishProposed, followedUpSinceProposal, initialCheckoutState, isAgentPresent, isClosed, isOptionIcon, isPresetId, isValidEntryPointAnswer, isValidEntryPointInput, isValidModelAnswer, openInputs, parseChoiceAnswer, parseEntryPointOptions, parseGuide, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, presetInput, stepProgress };
|
package/dist/needs.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CheckoutClient } from "./client.js";
|
|
2
2
|
import type { Checkout } from "./protocol.js";
|
|
3
|
+
import type { Guide } from "./guide.js";
|
|
3
4
|
/**
|
|
4
5
|
* Something the setup waits on the user for: an open question, a proposed
|
|
5
6
|
* plan, or the agent's proposal to close. `id` is what `answer` takes: the
|
|
@@ -35,7 +36,8 @@ export type TerminalQuestion = {
|
|
|
35
36
|
/** The shape of the answer, as the `answer` command takes it. */
|
|
36
37
|
answerFormat: string;
|
|
37
38
|
};
|
|
38
|
-
|
|
39
|
+
/** A need as a question for the agent's own conversation, worded by the setup server's `guide`. */
|
|
40
|
+
export declare const terminalQuestion: (need: SetupNeed, guide: Pick<Guide, "texts">) => TerminalQuestion;
|
|
39
41
|
/** The question as one line: the prompt, then its options. */
|
|
40
42
|
export declare const formatTerminalQuestion: ({ question, options, multiple, }: TerminalQuestion) => string;
|
|
41
43
|
/**
|
package/dist/needs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"needs.d.ts","sourceRoot":"","sources":["../src/needs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAU,CAAC;AAY/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAY,CAAC;AAE3C;;;;GAIG;AACH,MAAM,MAAM,SAAS,GACjB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAA;CAAE,GACpD;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAA;CAAE,GACjD;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAA;CAAE,CAAC;AAEtE,oEAAoE;AACpE,eAAO,MAAM,YAAY,UAAW,QAAQ,CAAC,KAAK,KAAG,SAAS,EAoB7D,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,OAAO,SAAU,SAAS,WAKO,CAAC;AAE/C,KAAK,UAAU,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,kFAAkF;AAClF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAgCF,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"needs.d.ts","sourceRoot":"","sources":["../src/needs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAU,CAAC;AAY/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,KAAK,EAAgB,MAAM,YAAS,CAAC;AAEnD;;;;GAIG;AACH,MAAM,MAAM,SAAS,GACjB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAA;CAAE,GACpD;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAA;CAAE,GACjD;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAA;CAAE,CAAC;AAEtE,oEAAoE;AACpE,eAAO,MAAM,YAAY,UAAW,QAAQ,CAAC,KAAK,KAAG,SAAS,EAoB7D,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,OAAO,SAAU,SAAS,WAKO,CAAC;AAE/C,KAAK,UAAU,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,kFAAkF;AAClF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAgCF,mGAAmG;AACnG,eAAO,MAAM,gBAAgB,SACrB,SAAS,SACR,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,KAC1B,gBA6CF,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,sBAAsB,qCAIhC,gBAAgB,WAQL,CAAC;AAsDf;;;;GAIG;AACH,eAAO,MAAM,UAAU,WACb,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,UAAU,CAAC,MAC9C,MAAM,SACH,MAAM,GAAG,SAAS,YAChB;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE;;;;;;;;;;EA8DjE,CAAC"}
|
package/dist/needs.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { classifyChoiceAnswer, currentPlan, finishProposed, isClosed, isValidEntryPointAnswer, isValidModelAnswer, openInputs, parseModelAnswer, parseMultipleAnswer } from "./protocol.js";
|
|
2
|
+
import { renderText } from "./guide.js";
|
|
2
3
|
// src/needs.ts
|
|
3
4
|
/** What the setup waits on the user for right now, oldest first. */
|
|
4
5
|
const pendingNeeds = (state) => {
|
|
@@ -35,45 +36,47 @@ const optionList = (input) => (input.options ?? []).flatMap((option) => {
|
|
|
35
36
|
return option.variants?.length ? option.variants.map((variant) => entry(`${option.id}:${variant.id}`, `${option.label} (${variant.label})`)) : [entry(option.id, option.label)];
|
|
36
37
|
});
|
|
37
38
|
const ANSWER_FORMATS = {
|
|
38
|
-
text: "
|
|
39
|
-
product: "
|
|
40
|
-
model: "
|
|
41
|
-
"entry-point": "
|
|
42
|
-
choice: "
|
|
39
|
+
text: "need.format.text",
|
|
40
|
+
product: "need.format.product",
|
|
41
|
+
model: "need.format.model",
|
|
42
|
+
"entry-point": "need.format.entry-point",
|
|
43
|
+
choice: "need.format.choice"
|
|
43
44
|
};
|
|
44
|
-
|
|
45
|
+
/** A need as a question for the agent's own conversation, worded by the setup server's `guide`. */
|
|
46
|
+
const terminalQuestion = (need, guide) => {
|
|
47
|
+
const text = (key, values) => renderText(guide, key, values);
|
|
45
48
|
if (need.kind === "plan") return {
|
|
46
|
-
question: "
|
|
49
|
+
question: text("need.plan.question"),
|
|
47
50
|
options: [{
|
|
48
51
|
id: "approve",
|
|
49
|
-
label: "
|
|
52
|
+
label: text("need.plan.approve")
|
|
50
53
|
}, {
|
|
51
54
|
id: "revise",
|
|
52
|
-
label: "
|
|
55
|
+
label: text("need.plan.revise")
|
|
53
56
|
}],
|
|
54
|
-
answerFormat: "
|
|
57
|
+
answerFormat: text("need.plan.format")
|
|
55
58
|
};
|
|
56
59
|
if (need.kind === "finish") return {
|
|
57
|
-
question:
|
|
60
|
+
question: need.completion.preview ? text("need.finish.question-preview", { preview: need.completion.preview }) : text("need.finish.question"),
|
|
58
61
|
options: [{
|
|
59
62
|
id: "close",
|
|
60
|
-
label: "
|
|
63
|
+
label: text("need.finish.close")
|
|
61
64
|
}],
|
|
62
|
-
answerFormat: "
|
|
65
|
+
answerFormat: text("need.finish.format")
|
|
63
66
|
};
|
|
64
67
|
const { input } = need;
|
|
65
68
|
const options = input.kind === "product" ? [{
|
|
66
69
|
id: "add",
|
|
67
|
-
label:
|
|
70
|
+
label: text("need.product.add", { product: input.product ?? "the product" })
|
|
68
71
|
}, {
|
|
69
72
|
id: "decline",
|
|
70
|
-
label: "
|
|
73
|
+
label: text("need.product.decline")
|
|
71
74
|
}] : input.kind === "text" ? void 0 : optionList(input);
|
|
72
75
|
return {
|
|
73
|
-
question: `${input.prompt.trim() || "
|
|
76
|
+
question: `${input.prompt.trim() || text("need.input.empty-prompt")}${input.optional ? text("need.input.optional") : ""}${input.kind === "model" ? text("need.input.model") : ""}`,
|
|
74
77
|
...options && { options },
|
|
75
78
|
...input.multiple && { multiple: true },
|
|
76
|
-
answerFormat: input.multiple ? "
|
|
79
|
+
answerFormat: text(input.multiple ? "need.format.multiple" : ANSWER_FORMATS[input.kind])
|
|
77
80
|
};
|
|
78
81
|
};
|
|
79
82
|
/** The question as one line: the prompt, then its options. */
|
package/dist/needs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"needs.js","names":[],"sources":["../src/needs.ts"],"sourcesContent":["import type { CheckoutClient } from \"./client\";\nimport {\n classifyChoiceAnswer,\n currentPlan,\n finishProposed,\n isClosed,\n isValidEntryPointAnswer,\n isValidModelAnswer,\n parseModelAnswer,\n openInputs,\n parseMultipleAnswer,\n} from \"./protocol\";\nimport type { Checkout } from \"./protocol\";\n\n/**\n * Something the setup waits on the user for: an open question, a proposed\n * plan, or the agent's proposal to close. `id` is what `answer` takes: the\n * input id, `plan` or `finish`.\n */\nexport type SetupNeed =\n | { id: string; kind: \"input\"; input: Checkout.Input }\n | { id: \"plan\"; kind: \"plan\"; plan: Checkout.Plan }\n | { id: \"finish\"; kind: \"finish\"; completion: Checkout.Completion };\n\n/** What the setup waits on the user for right now, oldest first. */\nexport const pendingNeeds = (state: Checkout.State): SetupNeed[] => {\n if (state.createdAt === null || isClosed(state)) return [];\n const needs: SetupNeed[] = openInputs(state).map((input) => ({\n id: input.id,\n kind: \"input\",\n input,\n }));\n const plan = currentPlan(state);\n if (plan?.status === \"proposed\")\n needs.push({ id: \"plan\", kind: \"plan\", plan });\n // Any message after the proposal, acknowledged or not, means more work first.\n if (\n finishProposed(state) &&\n !state.log.some(\n (entry) =>\n entry.role === \"user\" && entry.at > state.completion!.proposedAt,\n )\n )\n needs.push({ id: \"finish\", kind: \"finish\", completion: state.completion! });\n return needs;\n};\n\n/** Tells one need from a later one with the same id, such as a new plan revision. */\nexport const needKey = (need: SetupNeed) =>\n need.kind === \"input\"\n ? `input:${need.input.id}:${need.input.createdAt}`\n : need.kind === \"plan\"\n ? `plan:${need.plan.revision}:${need.plan.submittedAt}`\n : `finish:${need.completion.proposedAt}`;\n\ntype NeedOption = { id: string; label: string; description?: string };\n\n/** A need as a question the agent can put to the user in its own conversation. */\nexport type TerminalQuestion = {\n question: string;\n options?: NeedOption[];\n multiple?: true;\n /** The shape of the answer, as the `answer` command takes it. */\n answerFormat: string;\n};\n\n/** Options as the ids an answer names: `<option>:<variant>` for an option with variants. */\nconst optionList = (input: Checkout.Input): NeedOption[] =>\n (input.options ?? []).flatMap((option) => {\n const where = option.entryPoint\n ? ` (${option.entryPoint.formFactor}; ${option.entryPoint.placement}; ${option.entryPoint.trigger})`\n : \"\";\n const description = `${option.description ?? \"\"}${where}`.trim();\n const entry = (id: string, label: string) => ({\n id,\n label,\n ...(description && { description }),\n });\n return option.variants?.length\n ? option.variants.map((variant) =>\n entry(\n `${option.id}:${variant.id}`,\n `${option.label} (${variant.label})`,\n ),\n )\n : [entry(option.id, option.label)];\n });\n\nconst ANSWER_FORMATS: Record<Checkout.InputKind, string> = {\n text: \"the user's text, as one shell word\",\n product: \"add or decline\",\n model: \"<provider>:<model>, such as openai:gpt-5\",\n \"entry-point\": \"one option id\",\n choice: \"one option id, or the user's own text\",\n};\n\nexport const terminalQuestion = (need: SetupNeed): TerminalQuestion => {\n if (need.kind === \"plan\")\n return {\n question:\n \"Approve the setup plan? Show the user the plan you proposed and ask whether to go ahead or what to change.\",\n options: [\n { id: \"approve\", label: \"Approve the plan\" },\n { id: \"revise\", label: \"Ask for changes (pass --feedback)\" },\n ],\n answerFormat: 'approve, or revise --feedback \"<what to change>\"',\n };\n if (need.kind === \"finish\")\n return {\n question: `Everything is installed${need.completion.preview ? ` and running at ${need.completion.preview}` : \"\"}. Close the setup, or keep going?`,\n options: [{ id: \"close\", label: \"Close the setup\" }],\n answerFormat:\n \"close; if the user wants more, do it and run done again instead\",\n };\n const { input } = need;\n const options =\n input.kind === \"product\"\n ? [\n { id: \"add\", label: `Add ${input.product ?? \"the product\"}` },\n { id: \"decline\", label: \"Leave it out\" },\n ]\n : input.kind === \"text\"\n ? undefined\n : optionList(input);\n return {\n question: `${input.prompt.trim() || \"Your agent needs an answer.\"}${input.optional ? \" (optional; --dismiss skips it)\" : \"\"}${input.kind === \"model\" ? \" The API key is not typed here: the user adds it to the project's env file themselves.\" : \"\"}`,\n ...(options && { options }),\n ...(input.multiple && { multiple: true as const }),\n answerFormat: input.multiple\n ? \"option ids separated by commas, or the user's own text\"\n : ANSWER_FORMATS[input.kind],\n };\n};\n\n/** The question as one line: the prompt, then its options. */\nexport const formatTerminalQuestion = ({\n question,\n options,\n multiple,\n}: TerminalQuestion) =>\n options\n ? `${question} Options${multiple ? \", any number\" : \"\"}: ${options\n .map(\n (option) =>\n `${option.id} (${option.label}${option.description ? `: ${option.description}` : \"\"})`,\n )\n .join(\", \")}.`\n : question;\n\nconst ids = (input: Checkout.Input) =>\n optionList(input)\n .map((option) => option.id)\n .join(\", \");\n\n/** `value` as the wire answer `input` takes, or an error naming the shape it needs. */\nconst wireAnswer = (input: Checkout.Input, value: string) => {\n const trimmed = value.trim();\n if (input.kind === \"model\") {\n const at = trimmed.indexOf(\":\");\n const json = trimmed.startsWith(\"{\")\n ? trimmed\n : JSON.stringify({\n provider: trimmed.slice(0, at),\n model: trimmed.slice(at + 1),\n });\n if (at <= 0 && !trimmed.startsWith(\"{\"))\n throw new Error(\n `a model answer is \"<provider>:<model>\", such as openai:gpt-5`,\n );\n if (!isValidModelAnswer(input, json))\n throw new Error(\n `a model answer names one of the providers: ${ids(input)}`,\n );\n // Only the documented fields; anything else (a key, say) never reaches the setup.\n return JSON.stringify(parseModelAnswer(json));\n }\n if (input.kind === \"entry-point\") {\n if (!isValidEntryPointAnswer(input, trimmed))\n throw new Error(`an entry-point answer is one of: ${ids(input)}`);\n return trimmed;\n }\n if (input.kind === \"choice\") {\n const answer =\n input.multiple && parseMultipleAnswer(trimmed) === undefined\n ? JSON.stringify(\n trimmed\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean),\n )\n : trimmed;\n if (classifyChoiceAnswer(input, answer) === \"invalid\")\n throw new Error(\n `\"${value}\" names an option without a valid variant; pick from: ${ids(input)}`,\n );\n return answer;\n }\n if (trimmed === \"\") throw new Error(\"an answer cannot be empty\");\n return value;\n};\n\n/**\n * Answers a need from the agent's side, with what the user said in the\n * agent's own conversation: an input, the plan (`approve` or `revise` with\n * feedback) or the proposal to close (`close`).\n */\nexport const answerNeed = async (\n client: Pick<CheckoutClient, \"state\" | \"commands\">,\n id: string,\n value: string | undefined,\n options: { note?: string; feedback?: string; dismiss?: boolean } = {},\n) => {\n const state = client.state;\n if (state === undefined) throw new Error(\"the setup has no state\");\n const { commands } = client;\n if (id === \"plan\") {\n const plan = currentPlan(state);\n if (plan === undefined) throw new Error(\"no plan has been proposed\");\n const feedback = options.feedback?.trim();\n if (value !== \"approve\" && (value !== \"revise\" || !feedback))\n throw new Error(\n 'answer the plan with approve, or revise --feedback \"<what to change>\"',\n );\n await commands[\"checkout/plan\"]({\n ...(state.id !== null && { id: state.id }),\n revision: plan.revision,\n ...(value === \"approve\"\n ? { decision: \"approve\" as const }\n : { decision: \"revise\" as const, feedback: feedback! }),\n });\n return { ok: true, id, decision: value };\n }\n if (id === \"finish\") {\n if (!pendingNeeds(state).some((need) => need.id === \"finish\"))\n throw new Error(\n \"no proposal to close is waiting; finish the user's follow-up and run done again\",\n );\n if (value !== \"close\")\n throw new Error(\n \"answer finish with close; if the user wants more, do it and run done again\",\n );\n await commands[\"checkout/finish\"]();\n return { ok: true, id, status: \"done\" };\n }\n const input = state.inputs.find((candidate) => candidate.id === id);\n if (input === undefined) throw new Error(`no input \"${id}\"`);\n if (input.status !== \"open\")\n throw new Error(`input \"${id}\" is already ${input.status}`);\n if (options.dismiss && !input.optional)\n throw new Error(`input \"${id}\" is required; answer it instead`);\n if (options.dismiss || (input.kind === \"product\" && value === \"decline\")) {\n await commands[\"checkout/dismiss\"]({ inputId: id });\n return { ok: true, id, status: \"dismissed\" };\n }\n if (value === undefined) throw new Error(`usage: answer ${id} <answer>`);\n if (input.kind === \"product\") {\n if (value !== \"add\")\n throw new Error(\"answer a product with add or decline\");\n const slug = input.product ?? \"\";\n await commands[\"checkout/add-product\"]({\n inputId: id,\n product: { slug, name: slug },\n });\n } else {\n const note = options.note?.trim();\n await commands[\"checkout/answer\"]({\n inputId: id,\n answer: wireAnswer(input, value),\n ...(note && { note }),\n });\n }\n return { ok: true, id, status: \"answered\" };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"needs.js","names":[],"sources":["../src/needs.ts"],"sourcesContent":["import type { CheckoutClient } from \"./client\";\nimport {\n classifyChoiceAnswer,\n currentPlan,\n finishProposed,\n isClosed,\n isValidEntryPointAnswer,\n isValidModelAnswer,\n parseModelAnswer,\n openInputs,\n parseMultipleAnswer,\n} from \"./protocol\";\nimport type { Checkout } from \"./protocol\";\nimport { renderText } from \"./guide\";\nimport type { Guide, GuideTextKey } from \"./guide\";\n\n/**\n * Something the setup waits on the user for: an open question, a proposed\n * plan, or the agent's proposal to close. `id` is what `answer` takes: the\n * input id, `plan` or `finish`.\n */\nexport type SetupNeed =\n | { id: string; kind: \"input\"; input: Checkout.Input }\n | { id: \"plan\"; kind: \"plan\"; plan: Checkout.Plan }\n | { id: \"finish\"; kind: \"finish\"; completion: Checkout.Completion };\n\n/** What the setup waits on the user for right now, oldest first. */\nexport const pendingNeeds = (state: Checkout.State): SetupNeed[] => {\n if (state.createdAt === null || isClosed(state)) return [];\n const needs: SetupNeed[] = openInputs(state).map((input) => ({\n id: input.id,\n kind: \"input\",\n input,\n }));\n const plan = currentPlan(state);\n if (plan?.status === \"proposed\")\n needs.push({ id: \"plan\", kind: \"plan\", plan });\n // Any message after the proposal, acknowledged or not, means more work first.\n if (\n finishProposed(state) &&\n !state.log.some(\n (entry) =>\n entry.role === \"user\" && entry.at > state.completion!.proposedAt,\n )\n )\n needs.push({ id: \"finish\", kind: \"finish\", completion: state.completion! });\n return needs;\n};\n\n/** Tells one need from a later one with the same id, such as a new plan revision. */\nexport const needKey = (need: SetupNeed) =>\n need.kind === \"input\"\n ? `input:${need.input.id}:${need.input.createdAt}`\n : need.kind === \"plan\"\n ? `plan:${need.plan.revision}:${need.plan.submittedAt}`\n : `finish:${need.completion.proposedAt}`;\n\ntype NeedOption = { id: string; label: string; description?: string };\n\n/** A need as a question the agent can put to the user in its own conversation. */\nexport type TerminalQuestion = {\n question: string;\n options?: NeedOption[];\n multiple?: true;\n /** The shape of the answer, as the `answer` command takes it. */\n answerFormat: string;\n};\n\n/** Options as the ids an answer names: `<option>:<variant>` for an option with variants. */\nconst optionList = (input: Checkout.Input): NeedOption[] =>\n (input.options ?? []).flatMap((option) => {\n const where = option.entryPoint\n ? ` (${option.entryPoint.formFactor}; ${option.entryPoint.placement}; ${option.entryPoint.trigger})`\n : \"\";\n const description = `${option.description ?? \"\"}${where}`.trim();\n const entry = (id: string, label: string) => ({\n id,\n label,\n ...(description && { description }),\n });\n return option.variants?.length\n ? option.variants.map((variant) =>\n entry(\n `${option.id}:${variant.id}`,\n `${option.label} (${variant.label})`,\n ),\n )\n : [entry(option.id, option.label)];\n });\n\nconst ANSWER_FORMATS = {\n text: \"need.format.text\",\n product: \"need.format.product\",\n model: \"need.format.model\",\n \"entry-point\": \"need.format.entry-point\",\n choice: \"need.format.choice\",\n} as const satisfies Record<Checkout.InputKind, GuideTextKey>;\n\n/** A need as a question for the agent's own conversation, worded by the setup server's `guide`. */\nexport const terminalQuestion = (\n need: SetupNeed,\n guide: Pick<Guide, \"texts\">,\n): TerminalQuestion => {\n const text = (key: GuideTextKey, values?: Record<string, string>) =>\n renderText(guide, key, values);\n if (need.kind === \"plan\")\n return {\n question: text(\"need.plan.question\"),\n options: [\n { id: \"approve\", label: text(\"need.plan.approve\") },\n { id: \"revise\", label: text(\"need.plan.revise\") },\n ],\n answerFormat: text(\"need.plan.format\"),\n };\n if (need.kind === \"finish\")\n return {\n question: need.completion.preview\n ? text(\"need.finish.question-preview\", {\n preview: need.completion.preview,\n })\n : text(\"need.finish.question\"),\n options: [{ id: \"close\", label: text(\"need.finish.close\") }],\n answerFormat: text(\"need.finish.format\"),\n };\n const { input } = need;\n const options =\n input.kind === \"product\"\n ? [\n {\n id: \"add\",\n label: text(\"need.product.add\", {\n product: input.product ?? \"the product\",\n }),\n },\n { id: \"decline\", label: text(\"need.product.decline\") },\n ]\n : input.kind === \"text\"\n ? undefined\n : optionList(input);\n return {\n question: `${input.prompt.trim() || text(\"need.input.empty-prompt\")}${input.optional ? text(\"need.input.optional\") : \"\"}${input.kind === \"model\" ? text(\"need.input.model\") : \"\"}`,\n ...(options && { options }),\n ...(input.multiple && { multiple: true as const }),\n answerFormat: text(\n input.multiple ? \"need.format.multiple\" : ANSWER_FORMATS[input.kind],\n ),\n };\n};\n\n/** The question as one line: the prompt, then its options. */\nexport const formatTerminalQuestion = ({\n question,\n options,\n multiple,\n}: TerminalQuestion) =>\n options\n ? `${question} Options${multiple ? \", any number\" : \"\"}: ${options\n .map(\n (option) =>\n `${option.id} (${option.label}${option.description ? `: ${option.description}` : \"\"})`,\n )\n .join(\", \")}.`\n : question;\n\nconst ids = (input: Checkout.Input) =>\n optionList(input)\n .map((option) => option.id)\n .join(\", \");\n\n/** `value` as the wire answer `input` takes, or an error naming the shape it needs. */\nconst wireAnswer = (input: Checkout.Input, value: string) => {\n const trimmed = value.trim();\n if (input.kind === \"model\") {\n const at = trimmed.indexOf(\":\");\n const json = trimmed.startsWith(\"{\")\n ? trimmed\n : JSON.stringify({\n provider: trimmed.slice(0, at),\n model: trimmed.slice(at + 1),\n });\n if (at <= 0 && !trimmed.startsWith(\"{\"))\n throw new Error(\n `a model answer is \"<provider>:<model>\", such as openai:gpt-5`,\n );\n if (!isValidModelAnswer(input, json))\n throw new Error(\n `a model answer names one of the providers: ${ids(input)}`,\n );\n // Only the documented fields; anything else (a key, say) never reaches the setup.\n return JSON.stringify(parseModelAnswer(json));\n }\n if (input.kind === \"entry-point\") {\n if (!isValidEntryPointAnswer(input, trimmed))\n throw new Error(`an entry-point answer is one of: ${ids(input)}`);\n return trimmed;\n }\n if (input.kind === \"choice\") {\n const answer =\n input.multiple && parseMultipleAnswer(trimmed) === undefined\n ? JSON.stringify(\n trimmed\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean),\n )\n : trimmed;\n if (classifyChoiceAnswer(input, answer) === \"invalid\")\n throw new Error(\n `\"${value}\" names an option without a valid variant; pick from: ${ids(input)}`,\n );\n return answer;\n }\n if (trimmed === \"\") throw new Error(\"an answer cannot be empty\");\n return value;\n};\n\n/**\n * Answers a need from the agent's side, with what the user said in the\n * agent's own conversation: an input, the plan (`approve` or `revise` with\n * feedback) or the proposal to close (`close`).\n */\nexport const answerNeed = async (\n client: Pick<CheckoutClient, \"state\" | \"commands\">,\n id: string,\n value: string | undefined,\n options: { note?: string; feedback?: string; dismiss?: boolean } = {},\n) => {\n const state = client.state;\n if (state === undefined) throw new Error(\"the setup has no state\");\n const { commands } = client;\n if (id === \"plan\") {\n const plan = currentPlan(state);\n if (plan === undefined) throw new Error(\"no plan has been proposed\");\n const feedback = options.feedback?.trim();\n if (value !== \"approve\" && (value !== \"revise\" || !feedback))\n throw new Error(\n 'answer the plan with approve, or revise --feedback \"<what to change>\"',\n );\n await commands[\"checkout/plan\"]({\n ...(state.id !== null && { id: state.id }),\n revision: plan.revision,\n ...(value === \"approve\"\n ? { decision: \"approve\" as const }\n : { decision: \"revise\" as const, feedback: feedback! }),\n });\n return { ok: true, id, decision: value };\n }\n if (id === \"finish\") {\n if (!pendingNeeds(state).some((need) => need.id === \"finish\"))\n throw new Error(\n \"no proposal to close is waiting; finish the user's follow-up and run done again\",\n );\n if (value !== \"close\")\n throw new Error(\n \"answer finish with close; if the user wants more, do it and run done again\",\n );\n await commands[\"checkout/finish\"]();\n return { ok: true, id, status: \"done\" };\n }\n const input = state.inputs.find((candidate) => candidate.id === id);\n if (input === undefined) throw new Error(`no input \"${id}\"`);\n if (input.status !== \"open\")\n throw new Error(`input \"${id}\" is already ${input.status}`);\n if (options.dismiss && !input.optional)\n throw new Error(`input \"${id}\" is required; answer it instead`);\n if (options.dismiss || (input.kind === \"product\" && value === \"decline\")) {\n await commands[\"checkout/dismiss\"]({ inputId: id });\n return { ok: true, id, status: \"dismissed\" };\n }\n if (value === undefined) throw new Error(`usage: answer ${id} <answer>`);\n if (input.kind === \"product\") {\n if (value !== \"add\")\n throw new Error(\"answer a product with add or decline\");\n const slug = input.product ?? \"\";\n await commands[\"checkout/add-product\"]({\n inputId: id,\n product: { slug, name: slug },\n });\n } else {\n const note = options.note?.trim();\n await commands[\"checkout/answer\"]({\n inputId: id,\n answer: wireAnswer(input, value),\n ...(note && { note }),\n });\n }\n return { ok: true, id, status: \"answered\" };\n};\n"],"mappings":"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"}
|
package/dist/sdk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAU,CAAC;AAYnD,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC;IACjC,6FAA6F;IAC7F,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,kEAAkE;IAClE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACnD,MAAM,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC;CACtD,CAAC;AAqBF;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,YAAmB,oBAAoB;;;;;;EAkDhE,CAAC"}
|
package/dist/sdk.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { detectAgentKind } from "./agents.js";
|
|
2
|
+
import { OPTION_ICONS } from "./protocol.js";
|
|
3
|
+
import { loadGuide, renderText } from "./guide.js";
|
|
2
4
|
import { connectCheckout } from "./client.js";
|
|
3
5
|
import { followSetup, setupPageUrl, shellWord } from "./stream.js";
|
|
4
|
-
import { agentInstructions } from "./cli.js";
|
|
5
6
|
import { randomInt } from "node:crypto";
|
|
6
7
|
// src/sdk.ts
|
|
7
8
|
/**
|
|
@@ -12,28 +13,17 @@ import { randomInt } from "node:crypto";
|
|
|
12
13
|
/** A random id in the alphabet and length the setup page uses for its own. */
|
|
13
14
|
const createSetupId = () => Array.from({ length: 12 }, () => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"[randomInt(62)]).join("");
|
|
14
15
|
/** What the agent reads first in the output of a setup command that keeps running. */
|
|
15
|
-
const setupBriefing = (session, { command, name = "Setup"
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
${
|
|
26
|
-
command,
|
|
27
|
-
startedByAgent: {
|
|
28
|
-
browserUrl: session.browserUrl,
|
|
29
|
-
streaming: true,
|
|
30
|
-
opened: canOpen
|
|
31
|
-
}
|
|
32
|
-
})}
|
|
33
|
-
Setup page, if the user asks for it: ${session.browserUrl}
|
|
34
|
-
|
|
35
|
-
Current setup:
|
|
36
|
-
${JSON.stringify({
|
|
16
|
+
const setupBriefing = (session, guide, { command, name = "Setup" }) => {
|
|
17
|
+
const values = {
|
|
18
|
+
name,
|
|
19
|
+
cmd: command,
|
|
20
|
+
url: shellWord(session.agentUrl),
|
|
21
|
+
linkId: session.linkId,
|
|
22
|
+
browserUrl: session.browserUrl,
|
|
23
|
+
icons: OPTION_ICONS.join(", ")
|
|
24
|
+
};
|
|
25
|
+
return `${renderText(guide, "stream.briefing", values)}${renderText(guide, "briefing", values)}
|
|
26
|
+
${renderText(guide, "stream.briefing-footer", values)}${JSON.stringify({
|
|
37
27
|
status: "planning",
|
|
38
28
|
products: session.products
|
|
39
29
|
})}
|
|
@@ -51,6 +41,10 @@ const runAgentSetup = async (options) => {
|
|
|
51
41
|
const linkId = createSetupId();
|
|
52
42
|
const setupId = createSetupId();
|
|
53
43
|
const agentUrl = `${options.server.replace(/\/$/, "")}/${linkId}`;
|
|
44
|
+
const guide = await loadGuide(agentUrl, {
|
|
45
|
+
started: "agent-stream",
|
|
46
|
+
opened: options.canOpen !== false
|
|
47
|
+
});
|
|
54
48
|
const client = await connect(agentUrl);
|
|
55
49
|
try {
|
|
56
50
|
const kind = detectAgentKind(options.env);
|
|
@@ -68,10 +62,11 @@ const runAgentSetup = async (options) => {
|
|
|
68
62
|
products: options.products
|
|
69
63
|
};
|
|
70
64
|
options.onSession?.(session);
|
|
71
|
-
(options.write ?? ((text) => process.stdout.write(text)))(setupBriefing(session, options));
|
|
65
|
+
(options.write ?? ((text) => process.stdout.write(text)))(setupBriefing(session, guide, options));
|
|
72
66
|
return {
|
|
73
67
|
session,
|
|
74
68
|
stream: await followSetup(client, {
|
|
69
|
+
guide,
|
|
75
70
|
...options.stream,
|
|
76
71
|
agent: {
|
|
77
72
|
...kind !== void 0 && { kind },
|
package/dist/sdk.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.js","names":[],"sources":["../src/sdk.ts"],"sourcesContent":["/**\n * `<tool> setup` for coding agents, for a CLI to embed. `runAgentSetup` is\n * the whole command; route `<tool> setup <url> <step> ...` to\n * `runSetupCommand` from `setup-agent/cli`. Node only.\n */\nimport { randomInt } from \"node:crypto\";\nimport { detectAgentKind } from \"./agents\";\nimport {
|
|
1
|
+
{"version":3,"file":"sdk.js","names":[],"sources":["../src/sdk.ts"],"sourcesContent":["/**\n * `<tool> setup` for coding agents, for a CLI to embed. `runAgentSetup` is\n * the whole command; route `<tool> setup <url> <step> ...` to\n * `runSetupCommand` from `setup-agent/cli`. Node only.\n */\nimport { randomInt } from \"node:crypto\";\nimport { detectAgentKind } from \"./agents\";\nimport { OPTION_ICONS } from \"./protocol\";\nimport { loadGuide, renderText } from \"./guide\";\nimport type { Guide } from \"./guide\";\nimport { connectCheckout } from \"./client\";\nimport type { CheckoutClient } from \"./client\";\nimport type { Checkout } from \"./protocol\";\nimport { followSetup, setupPageUrl, shellWord } from \"./stream\";\nimport type { FollowSetupOptions } from \"./stream\";\n\n/** A random id in the alphabet and length the setup page uses for its own. */\nconst createSetupId = () =>\n Array.from(\n { length: 12 },\n () =>\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\"[\n randomInt(62)\n ]!,\n ).join(\"\");\n\nexport type SetupSession = {\n linkId: string;\n setupId: string;\n /** The URL every setup command takes. */\n agentUrl: string;\n /** The page the user follows the setup on. */\n browserUrl: string;\n products: Checkout.ProductSeed[];\n};\n\nexport type RunAgentSetupOptions = {\n /** The setup server, such as https://checkout.assistant-ui.com. */\n server: string;\n /** The setup app the browser opens, such as https://setup.assistant-ui.com. */\n setupAppUrl: string;\n products: Checkout.ProductSeed[];\n /** How the agent invokes setup commands, before the URL: `npx assistant-ui@latest setup`. */\n command: string;\n /** What the product calls this, for the briefing's first line. */\n name?: string;\n /** False for `--no-open`: the agent asks the user itself. */\n canOpen?: boolean;\n /** Opens the setup page; the default browser by default. */\n open?: (url: string) => void;\n /** Called once the session exists, before the briefing prints. */\n onSession?: (session: SetupSession) => void;\n write?: (text: string) => void;\n env?: NodeJS.ProcessEnv;\n connect?: (url: string) => Promise<CheckoutClient>;\n stream?: Pick<FollowSetupOptions, \"print\" | \"agent\">;\n};\n\n/** What the agent reads first in the output of a setup command that keeps running. */\nconst setupBriefing = (\n session: SetupSession,\n guide: Guide,\n { command, name = \"Setup\" }: Pick<RunAgentSetupOptions, \"command\" | \"name\">,\n) => {\n const values = {\n name,\n cmd: command,\n url: shellWord(session.agentUrl),\n linkId: session.linkId,\n browserUrl: session.browserUrl,\n icons: OPTION_ICONS.join(\", \"),\n };\n return `${renderText(guide, \"stream.briefing\", values)}${renderText(guide, \"briefing\", values)}\n${renderText(guide, \"stream.briefing-footer\", values)}${JSON.stringify({ status: \"planning\", products: session.products })}\n`;\n};\n\n/**\n * `<tool> setup` for a coding agent, in one call: opens a new link on the\n * setup server, creates a setup there and starts planning, prints the\n * briefing, then streams the setup and opens the browser only when it waits\n * on the user. Resolves with the running stream; the caller stops it (on\n * SIGINT, say) and disposes the client.\n */\nexport const runAgentSetup = async (options: RunAgentSetupOptions) => {\n const connect = options.connect ?? connectCheckout;\n const linkId = createSetupId();\n const setupId = createSetupId();\n const agentUrl = `${options.server.replace(/\\/$/, \"\")}/${linkId}`;\n // Online-only: the briefing and the stream's wording come from the setup server.\n const guide = await loadGuide(agentUrl, {\n started: \"agent-stream\",\n opened: options.canOpen !== false,\n });\n const client = await connect(agentUrl);\n try {\n const kind = detectAgentKind(options.env);\n await client.commands[\"checkout/create\"]({\n id: setupId,\n products: options.products,\n });\n await client.commands[\"checkout/begin-plan\"]();\n // The agent shows as connected once the stream says hello with the cwd.\n await client.commands[\"agent/intro\"]({\n ...(kind !== undefined && { kind }),\n });\n const session: SetupSession = {\n linkId,\n setupId,\n agentUrl,\n browserUrl: setupPageUrl(options.setupAppUrl, linkId, setupId),\n products: options.products,\n };\n options.onSession?.(session);\n (options.write ?? ((text) => process.stdout.write(text)))(\n setupBriefing(session, guide, options),\n );\n const stream = await followSetup(client, {\n guide,\n ...options.stream,\n agent: { ...(kind !== undefined && { kind }), ...options.stream?.agent },\n browser: {\n setupAppUrl: options.setupAppUrl,\n linkId,\n answerCommand: `${options.command} ${shellWord(agentUrl)} answer`,\n ...(options.open && { open: options.open }),\n ...(options.canOpen !== undefined && { canOpen: options.canOpen }),\n },\n });\n return { session, stream, client };\n } catch (error) {\n client.dispose();\n throw error;\n }\n};\n"],"mappings":";;;;;;;;;;;;;AAiBA,MAAM,sBACJ,MAAM,KACJ,EAAE,QAAQ,GAAG,SAEX,iEACE,UAAU,EAAE,EAElB,CAAC,CAAC,KAAK,EAAE;;AAmCX,MAAM,iBACJ,SACA,OACA,EAAE,SAAS,OAAO,cACf;CACH,MAAM,SAAS;EACb;EACA,KAAK;EACL,KAAK,UAAU,QAAQ,QAAQ;EAC/B,QAAQ,QAAQ;EAChB,YAAY,QAAQ;EACpB,OAAO,aAAa,KAAK,IAAI;CAC/B;CACA,OAAO,GAAG,WAAW,OAAO,mBAAmB,MAAM,IAAI,WAAW,OAAO,YAAY,MAAM,EAAE;EAC/F,WAAW,OAAO,0BAA0B,MAAM,IAAI,KAAK,UAAU;EAAE,QAAQ;EAAY,UAAU,QAAQ;CAAS,CAAC,EAAE;;AAE3H;;;;;;;;AASA,MAAa,gBAAgB,OAAO,YAAkC;CACpE,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,SAAS,cAAc;CAC7B,MAAM,UAAU,cAAc;CAC9B,MAAM,WAAW,GAAG,QAAQ,OAAO,QAAQ,OAAO,EAAE,EAAE,GAAG;CAEzD,MAAM,QAAQ,MAAM,UAAU,UAAU;EACtC,SAAS;EACT,QAAQ,QAAQ,YAAY;CAC9B,CAAC;CACD,MAAM,SAAS,MAAM,QAAQ,QAAQ;CACrC,IAAI;EACF,MAAM,OAAO,gBAAgB,QAAQ,GAAG;EACxC,MAAM,OAAO,SAAS,kBAAkB,CAAC;GACvC,IAAI;GACJ,UAAU,QAAQ;EACpB,CAAC;EACD,MAAM,OAAO,SAAS,sBAAsB,CAAC;EAE7C,MAAM,OAAO,SAAS,cAAc,CAAC,EACnC,GAAI,SAAS,KAAA,KAAa,EAAE,KAAK,EACnC,CAAC;EACD,MAAM,UAAwB;GAC5B;GACA;GACA;GACA,YAAY,aAAa,QAAQ,aAAa,QAAQ,OAAO;GAC7D,UAAU,QAAQ;EACpB;EACA,QAAQ,YAAY,OAAO;EAC3B,CAAC,QAAQ,WAAW,SAAS,QAAQ,OAAO,MAAM,IAAI,GAAA,CACpD,cAAc,SAAS,OAAO,OAAO,CACvC;EAaA,OAAO;GAAE;GAAS,QAAA,MAZG,YAAY,QAAQ;IACvC;IACA,GAAG,QAAQ;IACX,OAAO;KAAE,GAAI,SAAS,KAAA,KAAa,EAAE,KAAK;KAAI,GAAG,QAAQ,QAAQ;IAAM;IACvE,SAAS;KACP,aAAa,QAAQ;KACrB;KACA,eAAe,GAAG,QAAQ,QAAQ,GAAG,UAAU,QAAQ,EAAE;KACzD,GAAI,QAAQ,QAAQ,EAAE,MAAM,QAAQ,KAAK;KACzC,GAAI,QAAQ,YAAY,KAAA,KAAa,EAAE,SAAS,QAAQ,QAAQ;IAClE;GACF,CAAC;GACyB;EAAO;CACnC,SAAS,OAAO;EACd,OAAO,QAAQ;EACf,MAAM;CACR;AACF"}
|
package/dist/stream.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { AskReason } from "./browser.js";
|
|
|
2
2
|
import type { CheckoutClient } from "./client.js";
|
|
3
3
|
import type { TerminalQuestion } from "./needs.js";
|
|
4
4
|
import type { Checkout } from "./protocol.js";
|
|
5
|
+
import type { Guide } from "./guide.js";
|
|
5
6
|
export type StreamEvent = {
|
|
6
7
|
event: "planning.started";
|
|
7
8
|
} | {
|
|
@@ -51,7 +52,8 @@ export type StreamEvent = {
|
|
|
51
52
|
/** The command that answers it, with the answer's shape in angle brackets. */
|
|
52
53
|
answer: string;
|
|
53
54
|
} & TerminalQuestion);
|
|
54
|
-
|
|
55
|
+
/** The events between two states, with hints worded by the setup server's `guide`. */
|
|
56
|
+
export declare const diffEvents: (before: Checkout.State, after: Checkout.State, guide: Pick<Guide, "texts">) => StreamEvent[];
|
|
55
57
|
export declare const answerFields: (input: Checkout.Input, answer: string) => {
|
|
56
58
|
answer: string | string[];
|
|
57
59
|
note?: string;
|
|
@@ -62,6 +64,8 @@ export declare const setupPageUrl: (setupAppUrl: string, linkId: string, setupId
|
|
|
62
64
|
/** `value` as one shell word, quoted only when it holds more than plain URL characters. */
|
|
63
65
|
export declare const shellWord: (value: string) => string;
|
|
64
66
|
export type FollowSetupOptions = {
|
|
67
|
+
/** The setup server's wording for the stream's messages. */
|
|
68
|
+
guide: Pick<Guide, "texts">;
|
|
65
69
|
/** Writes one line of events; the default prints a JSON array to stdout. */
|
|
66
70
|
print?: (events: StreamEvent[]) => void;
|
|
67
71
|
/** The agent the stream reports in its hello; detected by default. */
|
|
@@ -87,7 +91,7 @@ export type FollowSetupOptions = {
|
|
|
87
91
|
* only when the setup waits on the user. Resolves with `stop`, which says
|
|
88
92
|
* goodbye; the caller disposes the client.
|
|
89
93
|
*/
|
|
90
|
-
export declare const followSetup: (client: Pick<CheckoutClient, "state" | "subscribe" | "commands">, { print, agent, browser }
|
|
94
|
+
export declare const followSetup: (client: Pick<CheckoutClient, "state" | "subscribe" | "commands">, { print, agent, browser, guide }: FollowSetupOptions) => Promise<{
|
|
91
95
|
stop: () => Promise<void>;
|
|
92
96
|
}>;
|
|
93
97
|
//# sourceMappingURL=stream.d.ts.map
|
package/dist/stream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAqB,MAAM,cAAW,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAU,CAAC;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAS,CAAC;AAMhD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAY,CAAC;AAE3C,MAAM,MAAM,WAAW,GACnB;IAAE,KAAK,EAAE,kBAAkB,CAAA;CAAE,GAC7B;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC7D;IACE,KAAK,EAAE,gBAAgB,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2GAA2G;IAC3G,MAAM,CAAC,EAAE,IAAI,CAAC;CACf,GACD;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7D;IAAE,KAAK,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,KAAK,EAAE,wBAAwB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,KAAK,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAC9C;IACE,KAAK,EAAE,gBAAgB,CAAC;IACxB,gEAAgE;IAChE,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB,GACD,CAAC;IACC,KAAK,EAAE,cAAc,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,gBAAgB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
|