@agimon-ai/po-plan 0.0.0-alpha.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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/dist/apiContracts.cjs +57 -0
  4. package/dist/apiContracts.cjs.map +1 -0
  5. package/dist/apiContracts.d.mts +53 -0
  6. package/dist/apiContracts.d.mts.map +1 -0
  7. package/dist/apiContracts.mjs +52 -0
  8. package/dist/apiContracts.mjs.map +1 -0
  9. package/dist/config-BC-Tou05.mjs +25 -0
  10. package/dist/config-BC-Tou05.mjs.map +1 -0
  11. package/dist/config-D0iTexGm.cjs +42 -0
  12. package/dist/config-D0iTexGm.cjs.map +1 -0
  13. package/dist/config-DWo57HKB.d.mts +13 -0
  14. package/dist/config-DWo57HKB.d.mts.map +1 -0
  15. package/dist/config.cjs +12 -0
  16. package/dist/config.d.mts +2 -0
  17. package/dist/config.mjs +2 -0
  18. package/dist/extensions/mcp-ui.mjs +46 -0
  19. package/dist/extensions/mcp-ui.mjs.map +1 -0
  20. package/dist/extensions/mcp.cjs +93 -0
  21. package/dist/extensions/mcp.cjs.map +1 -0
  22. package/dist/extensions/mcp.d.mts +5 -0
  23. package/dist/extensions/mcp.d.mts.map +1 -0
  24. package/dist/extensions/mcp.mjs +88 -0
  25. package/dist/extensions/mcp.mjs.map +1 -0
  26. package/dist/extensions/server.cjs +1025 -0
  27. package/dist/extensions/server.cjs.map +1 -0
  28. package/dist/extensions/server.d.mts +5 -0
  29. package/dist/extensions/server.d.mts.map +1 -0
  30. package/dist/extensions/server.mjs +993 -0
  31. package/dist/extensions/server.mjs.map +1 -0
  32. package/dist/extensions/web.mjs +861 -0
  33. package/dist/extensions/web.mjs.map +1 -0
  34. package/dist/fableFlow-BDc8a__Y.mjs +214 -0
  35. package/dist/fableFlow-BDc8a__Y.mjs.map +1 -0
  36. package/dist/fableFlow-CzfrLPzJ.cjs +279 -0
  37. package/dist/fableFlow-CzfrLPzJ.cjs.map +1 -0
  38. package/dist/fableFlow.cjs +13 -0
  39. package/dist/fableFlow.d.mts +2 -0
  40. package/dist/fableFlow.mjs +2 -0
  41. package/dist/index-C8e7IK60.d.mts +68 -0
  42. package/dist/index-C8e7IK60.d.mts.map +1 -0
  43. package/dist/index-C9QBG9cw.d.mts +51 -0
  44. package/dist/index-C9QBG9cw.d.mts.map +1 -0
  45. package/dist/index.cjs +31 -0
  46. package/dist/index.d.mts +4 -0
  47. package/dist/index.mjs +4 -0
  48. package/dist/logSinkTelemetry.cjs +42 -0
  49. package/dist/logSinkTelemetry.cjs.map +1 -0
  50. package/dist/logSinkTelemetry.d.mts +33 -0
  51. package/dist/logSinkTelemetry.d.mts.map +1 -0
  52. package/dist/logSinkTelemetry.mjs +40 -0
  53. package/dist/logSinkTelemetry.mjs.map +1 -0
  54. package/dist/planConfig.cjs +190 -0
  55. package/dist/planConfig.cjs.map +1 -0
  56. package/dist/planConfig.d.mts +47 -0
  57. package/dist/planConfig.d.mts.map +1 -0
  58. package/dist/planConfig.mjs +186 -0
  59. package/dist/planConfig.mjs.map +1 -0
  60. package/dist/prompts-DUVUPNZd.cjs +103 -0
  61. package/dist/prompts-DUVUPNZd.cjs.map +1 -0
  62. package/dist/prompts-DnVJGtEC.mjs +68 -0
  63. package/dist/prompts-DnVJGtEC.mjs.map +1 -0
  64. package/dist/prompts.cjs +8 -0
  65. package/dist/prompts.d.mts +2 -0
  66. package/dist/prompts.mjs +2 -0
  67. package/llms.txt +9 -0
  68. package/package.json +164 -0
  69. package/src/prompts/po-use-plan/SKILL.md +44 -0
  70. package/src/types/planApi.ts +125 -0
  71. package/src/types/planSettings.ts +78 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.mjs","names":["routes","settingPlan","settingPlan2","activityGroupPlan","fillPlan","leaderToggle","modePlan","toolPlan"],"sources":["../../src/types/planSettings.ts","../../src/extensions/(frontend)/_lib/planSettings.ts","../../src/extensions/(frontend)/setting/plan.web.ts","../../src/extensions/workspaces/(frontend)/setting/plan.web.ts","../../src/types/planApi.ts","../../src/extensions/workspaces/sessions/(frontend)/activity-group/plan.web.ts","../../src/types/apiRoutes.ts","../../generated/client.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/_lib/planApi.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/_components/PlanPanel.tsx","../../src/extensions/workspaces/sessions/(frontend)/fill/_components/PlanActivitySection.tsx","../../src/extensions/workspaces/sessions/(frontend)/fill/Plan.activity.plan.web.tsx","../../src/extensions/workspaces/sessions/(frontend)/leader/toggle.web.ts","../../src/extensions/workspaces/sessions/(frontend)/mode/plan/mode.web.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/PlanReviewPrompt.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/planToolRender.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/PlanToolMessage.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/plan.web.tsx","../../generated/web.ts"],"sourcesContent":["/**\n * The planning settings, described once for both surfaces.\n *\n * The terminal panel and the cockpit settings page render the same five fields,\n * and the one thing they must agree on is which config key each one writes. So\n * the ids, labels and key paths live here rather than in either renderer.\n *\n * It lives under src/types because that is the one server root a browser bundle\n * may read, which also means it may import nothing: pulling the config package\n * in for the thinking levels would drag a node module into the page. They are\n * spelled out instead, and a test asserts they still match the parser's.\n */\n\nexport const PLAN_CONFIG_SECTION_ID = 'planning';\nconst PLANNING_PATH = ['modes', PLAN_CONFIG_SECTION_ID] as const;\nconst MAIN_KEY = 'main';\nconst SUBAGENTS_KEY = 'subagents';\n\n/** Both agents fall back to the session's own settings when left unset. */\nconst INHERIT_MODEL = 'inherit the session model';\nconst INHERIT_THINKING = 'inherit';\n\n/** Mirrors DOOM_PLANNING_THINKING_LEVELS; pinned by planConfig's test. */\nexport const PLAN_THINKING_LEVELS: readonly string[] = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];\n\nexport interface PlanSettingShape {\n readonly id: string;\n readonly label: string;\n readonly keyPath: readonly string[];\n readonly placeholder: string;\n readonly detail: string;\n /** Present for a closed set; a renderer offers these rather than free text. */\n readonly options?: readonly string[];\n /** Offers the machine's authenticated models rather than free text. */\n readonly models?: boolean;\n}\n\nexport const PLAN_SETTING_SHAPES: readonly PlanSettingShape[] = [\n {\n id: 'main.model',\n label: 'main model',\n keyPath: [...PLANNING_PATH, MAIN_KEY, 'model'],\n placeholder: INHERIT_MODEL,\n detail: 'model the main agent switches to while plan mode is on.',\n models: true,\n },\n {\n id: 'main.thinking',\n label: 'main thinking',\n keyPath: [...PLANNING_PATH, MAIN_KEY, 'thinking'],\n placeholder: INHERIT_THINKING,\n detail: 'thinking level appended to the main model while planning.',\n options: PLAN_THINKING_LEVELS,\n },\n {\n id: 'subagents.model',\n label: 'subagent model',\n keyPath: [...PLANNING_PATH, SUBAGENTS_KEY, 'model'],\n placeholder: INHERIT_MODEL,\n detail: 'model forced onto delegated planning subagents.',\n models: true,\n },\n {\n id: 'subagents.thinking',\n label: 'subagent thinking',\n keyPath: [...PLANNING_PATH, SUBAGENTS_KEY, 'thinking'],\n placeholder: INHERIT_THINKING,\n detail: 'thinking level appended to the subagent model.',\n options: PLAN_THINKING_LEVELS,\n },\n {\n id: 'plansdirectory',\n label: 'plans directory',\n keyPath: [...PLANNING_PATH, 'plansDirectory'],\n placeholder: '~/.po/plans',\n detail: 'absolute, repo-relative, or under ~. written plans land here.',\n },\n];\n","import type { SettingsSectionContribution } from '@agimon-ai/po-core/web';\n\nimport { PLAN_CONFIG_SECTION_ID, PLAN_SETTING_SHAPES } from '../../../types/planSettings';\n\n/**\n * The planning settings, as the cockpit's settings page renders them.\n *\n * Built from the same descriptor table the terminal panel uses, so the two\n * surfaces cannot disagree about which config key a field writes. The host owns\n * the scope switch and the writes; this only says what the fields are.\n */\nexport const planSettingsSection: SettingsSectionContribution = {\n id: PLAN_CONFIG_SECTION_ID,\n label: 'planning',\n detail: 'the models plan mode switches to, and where written plans land',\n order: 30,\n fields: PLAN_SETTING_SHAPES.map((shape) => ({\n id: shape.id,\n label: shape.label,\n // A model field offers the machine's authenticated models, which only the\n // host can enumerate; the rest are a closed set or free text.\n kind: shape.models === true || shape.options !== undefined ? ('select' as const) : ('text' as const),\n keyPath: shape.keyPath,\n detail: shape.detail,\n placeholder: shape.placeholder,\n ...(shape.models === true ? { optionsFrom: 'models' as const } : {}),\n ...(shape.options === undefined\n ? {}\n : { options: shape.options.map((option) => ({ value: option, label: option })) }),\n })),\n};\n","import { defineSettingsSection } from '@agimon-ai/po-core/web';\n\nimport { planSettingsSection } from '../_lib/planSettings';\n\nexport default defineSettingsSection(planSettingsSection);\n","import { defineSettingsSection } from '@agimon-ai/po-core/web';\n\nimport { planSettingsSection } from '../../../(frontend)/_lib/planSettings';\n\nexport default defineSettingsSection(planSettingsSection);\n","/**\n * The wire vocabulary this package's API shares with whatever calls it.\n *\n * It lives under src/types because that is the one server root a browser\n * bundle may read: the routes answer with these shapes and the cockpit plugin\n * reads them, so neither half can drift from the other. The paths themselves\n * are not here; they are declared once in `./apiRoutes`, which the Hono app\n * and the generated client both read.\n *\n * Two routes, and neither takes a path. A session has one current plan, so\n * `current` answers it and `content` takes the manual save back; the file they\n * act on is the one write_plan recorded, never one the page named. That is\n * also why there is nothing to escape here: a reader cannot address a second\n * file through these URLs.\n */\n\n/** Where a host mounts this package's API; the segment after /api/plugins/. */\nexport const API_BASE_PATH = 'plans';\n\n/**\n * Footer status key the session publishes once a plan exists, which is what\n * makes the cockpit's activity group appear at all. It outlives plan mode: the\n * plan is most worth reading while the agent implements it.\n */\nexport const PLAN_STATUS_KEY = 'doom-plan-document';\n\n/** The selector contract shared by complete_plan and its cockpit composer prompt. */\nexport const PLAN_REVIEW_TITLE = 'Plan complete. What would you like to do?';\nexport const EXIT_PLAN_MODE_CHOICE = 'Exit plan mode and start implementation';\nexport const CONTINUE_PLANNING_CHOICE = 'Continue planning';\nexport const EXIT_PLAN_DECISION = 'exit';\nexport const CONTINUE_PLAN_DECISION = 'continue';\n\n/**\n * The label the reader sees paired with the decision the tool acts on. A headless\n * client answers with `value`, so a facet never has to recognise the wording; Pi's\n * own `ui.select` carries labels only, so its half maps the label back itself.\n */\nexport const PLAN_REVIEW_CHOICES = [\n { label: EXIT_PLAN_MODE_CHOICE, value: EXIT_PLAN_DECISION },\n { label: CONTINUE_PLANNING_CHOICE, value: CONTINUE_PLAN_DECISION },\n] as const;\n\n/** The same options as bare labels, which is all Pi's select and the composer prompt need. */\nexport const PLAN_REVIEW_OPTIONS = PLAN_REVIEW_CHOICES.map((choice) => choice.label);\n\n/** Between the plan's title and the stamp that marks a rewrite. */\nconst STATUS_SEPARATOR = ' · ';\n\n/** Strips the colour a session may have themed a status with. */\nconst ANSI = /\u001b\\[[0-9;]*m/gu;\n\n/** The status line the session publishes; the stamp changes on every write. */\nexport function formatPlanStatus(title: string, stamp: string): string {\n return `${title}${STATUS_SEPARATOR}${stamp}`;\n}\n\n/** What a reader gets out of the status line. */\nexport interface PlanStatusView {\n title: string;\n /** When the plan was last written, as the session worded it; empty when it said nothing. */\n stamp: string;\n}\n\n/**\n * Reading the status line back. Statuses reach a plugin raw, so the colour a\n * session may have added comes off here. A title can itself contain the\n * separator, so the stamp is taken from the last one rather than the first.\n */\nexport function parsePlanStatus(raw: string | undefined): PlanStatusView | undefined {\n if (raw === undefined) return undefined;\n const text = raw.replace(ANSI, '').trim();\n if (text === '') return undefined;\n const cut = text.lastIndexOf(STATUS_SEPARATOR);\n if (cut === -1) return { title: text, stamp: '' };\n return { title: text.slice(0, cut).trim(), stamp: text.slice(cut + STATUS_SEPARATOR.length).trim() };\n}\n\n/**\n * What write_plan recorded about this session's plan.\n *\n * The absolute path is the point of it: the extension resolves the plans\n * directory against the harness root, which no other process knows, so it\n * writes down where the plan landed rather than leaving the API to derive a\n * location it would sometimes get wrong.\n */\nexport interface PlanPointerRecord {\n path: string;\n title: string;\n writtenAt: string;\n planId?: string;\n}\n\n/** The plan as it stands on disk right now, which is what the source view edits. */\nexport interface PlanDetailView {\n path: string;\n title: string;\n writtenAt: string;\n planId?: string;\n content: string;\n /** sha256 of the content; the token a save hands back to prove it is not stale. */\n hash: string;\n /** True when the plan is missing or past the size cap, so `content` is empty. */\n unavailable: boolean;\n reason?: string;\n}\n\n/** What the save route takes. */\nexport interface PlanSaveRequest {\n /** The hash the reader loaded; a mismatch means the plan moved underneath them. */\n expectedHash: string;\n content: string;\n}\n\n/** What the save route answers with. */\nexport interface PlanSaveView {\n hash: string;\n}\n\n/** What a route reports when it refuses; the page shows `error` verbatim. */\nexport interface PlanErrorView {\n error: string;\n /** On a stale save, the hash the plan actually has now. */\n hash?: string;\n}\n","import { defineActivityGroup } from '@agimon-ai/po-core/web';\n\nimport { PLAN_STATUS_KEY } from '../../../../../types/planApi';\n\n// Keyed off the saved plan rather than the mode because the plan remains useful\n// while it is being implemented after the read-only mode exits.\nexport default defineActivityGroup({\n name: 'plan',\n keys: 'p e',\n statusKey: PLAN_STATUS_KEY,\n marksBackgroundWork: false,\n order: 25,\n});\n","import { apiResponse, defineApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { PlanDetailView, PlanSaveView } from './planApi';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path. The build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/plans/`, so the mount is\n * stated once, by the folder that creates it. The paths used to be written\n * here as helpers, again in the contract, and again inside every URL the page\n * built, and the copies were free to disagree.\n *\n * This is the one file the routes, the Hono app and the browser all read, so a\n * path cannot move on one side alone.\n *\n * Neither route takes a query or a path parameter: a session has one current\n * plan, so `current` answers it and `save` takes the manual write back.\n */\nexport default defineApiRoutes({\n current: {\n method: 'GET',\n path: '/current',\n response: apiResponse<PlanDetailView>(),\n },\n save: {\n method: 'PUT',\n path: '/content',\n response: apiResponse<PlanSaveView>(),\n },\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';\nimport { sealedTransport } from '@agimon-ai/po-web-security/browser';\n\nimport routes from '../src/types/apiRoutes';\n\n/** Scopes the tree mounts 'plans' at. */\nexport const apiScopes = ['session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'plans',\n transport: (input, init) => sealedTransport.fetch(input, init),\n sessionAddress: sessionApiAddress,\n});\n","import { api } from '../../../../../../../generated/client';\nimport type { PlanDetailView } from '../../../../../../types/planApi';\n\n/**\n * The page's half of this package's session API: the current plan, and the\n * manual save.\n *\n * These are adapters now, not a transport. The generated client owns the URL\n * and the sealed transport with it, so nothing here spells a route or reaches\n * for `fetch`. What stays is this package's own vocabulary: which refusals the\n * reader is shown, and the stale-save contract the editor depends on.\n */\n\nconst UNREACHABLE = 'The session is unreachable.';\nconst NO_PLAN = 'This session has not written a plan yet.';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** The message a reader is shown: the route's own words, or why there were none. */\nfunction messageOf(result: { status: number; error: string }): string {\n if (result.status === 0) return UNREACHABLE;\n return result.error === '' ? `The session answered ${String(result.status)}.` : result.error;\n}\n\nexport type FetchPlanResult = { ok: true; detail: PlanDetailView } | { ok: false; error: string };\n\nexport async function fetchPlan(sessionId: string): Promise<FetchPlanResult> {\n const result = await api.session(sessionId).current();\n // A session that has written nothing answers 404, which is not a failure the\n // reader should be shown as one.\n if (result.status === 404) return { ok: false, error: result.ok || result.error === '' ? NO_PLAN : result.error };\n if (!result.ok) return { ok: false, error: messageOf(result) };\n if (!isRecord(result.data)) return { ok: false, error: 'The session answered with no plan.' };\n return { ok: true, detail: result.data };\n}\n\nexport type SavePlanResult =\n | { ok: true; hash: string }\n /** The plan moved under the editor; `hash` is what it holds now. */\n | { ok: false; stale: true; error: string; hash?: string }\n | { ok: false; stale: false; error: string };\n\nexport async function savePlan(sessionId: string, expectedHash: string, content: string): Promise<SavePlanResult> {\n const result = await api.session(sessionId).save({ body: { expectedHash, content } });\n // The agent can rewrite the plan while a reader is still editing it. A moved\n // hash is the one refusal the editor recovers from, so it is read before the\n // generic failure.\n if (result.status === 409) {\n const data = result.ok ? undefined : result.data;\n const hash = isRecord(data) && typeof data.hash === 'string' ? data.hash : undefined;\n return {\n ok: false,\n stale: true,\n error: result.ok ? 'The plan changed since it was opened.' : messageOf(result),\n ...(hash === undefined ? {} : { hash }),\n };\n }\n if (!result.ok) return { ok: false, stale: false, error: messageOf(result) };\n return { ok: true, hash: isRecord(result.data) && typeof result.data.hash === 'string' ? result.data.hash : '' };\n}\n","import type { TransientTab, WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { Button, CodeEditor, Markdown } from '@agimon-ai/po-web-components';\nimport { useEffect, useState } from 'react';\n\nimport { PLAN_STATUS_KEY, type PlanDetailView } from '../../../../../../types/planApi';\nimport { fetchPlan, savePlan } from '../_lib/planApi';\n\n/**\n * The plan's tab: what the agent decided, and what the reader wants changed.\n *\n * Two views over the same file. A plan is read, not diffed, so preview is\n * where it opens; source is a real editor, line numbers and markdown colour\n * and all, because a reader who corrects a step themselves is cheaper than a\n * turn spent asking for it. The save goes to the plan file the\n * session recorded, and the next turn reads that file, so a correction made\n * here is the plan the agent implements.\n *\n * The tab is transient: it belongs to the session and the page, and closing it\n * is how it ends. It refetches whenever the session republishes its plan\n * status, which is what a rewrite by the agent looks like from here.\n */\n\ntype View = 'preview' | 'source';\n\n/** One tab per session; the plan is singular, so the id needs nothing else. */\nexport const PLAN_TAB_ID = 'plan-document';\n\n/** The one note that is not an error: anything else in that slot failed. */\nconst SAVED_NOTE = 'saved';\n\nexport function PlanPanel({ sessionId, statuses, closeTransientTab }: WebPluginSlotProps) {\n const [detail, setDetail] = useState<PlanDetailView | undefined>(undefined);\n const [error, setError] = useState<string | undefined>(undefined);\n const [loading, setLoading] = useState(false);\n const [view, setView] = useState<View>('preview');\n const [draft, setDraft] = useState<string | undefined>(undefined);\n const [saving, setSaving] = useState(false);\n const [saveNote, setSaveNote] = useState<string | undefined>(undefined);\n\n // One fetch per open, and again whenever the session republishes the status:\n // its stamp changes on every write, which is the cheapest signal that the\n // agent has revised the plan under the reader.\n const revision = statuses[PLAN_STATUS_KEY] ?? '';\n useEffect(() => {\n if (sessionId === null) return;\n let cancelled = false;\n // The fetch below is the external system; this marks it in flight before it starts.\n // oxlint-disable-next-line react/set-state-in-effect\n setLoading(true);\n void fetchPlan(sessionId).then((result) => {\n if (cancelled) return;\n setLoading(false);\n if (result.ok) {\n setDetail(result.detail);\n setError(undefined);\n // A rewrite invalidates an unsaved edit's baseline, so the editor goes\n // back to what is on disk rather than offering a save that must fail.\n setDraft(undefined);\n setSaveNote(undefined);\n return;\n }\n setDetail(undefined);\n setError(result.error);\n });\n return () => {\n cancelled = true;\n };\n }, [sessionId, revision]);\n\n const content = draft ?? detail?.content ?? '';\n\n const save = async (): Promise<void> => {\n if (sessionId === null || detail === undefined || draft === undefined) return;\n setSaving(true);\n const result = await savePlan(sessionId, detail.hash, draft);\n setSaving(false);\n if (result.ok) {\n setSaveNote(SAVED_NOTE);\n setDraft(undefined);\n const refreshed = await fetchPlan(sessionId);\n if (refreshed.ok) setDetail(refreshed.detail);\n else setError(refreshed.error);\n return;\n }\n setSaveNote(result.error);\n };\n\n return (\n <div data-testid=\"plan-panel\" className=\"flex min-h-0 flex-1 flex-col\">\n <header className=\"flex shrink-0 items-center gap-2 border-b border-doom-border px-4 py-2\">\n <span data-testid=\"plan-title\" className=\"min-w-0 flex-1 truncate text-lg font-semibold text-doom-hi\">\n {detail?.title ?? 'plan'}\n </span>\n <nav className=\"flex shrink-0 items-center gap-1\">\n {(['preview', 'source'] as View[]).map((entry) => (\n <Button\n key={entry}\n variant={view === entry ? 'outline' : 'ghost'}\n size=\"xs\"\n data-testid={`plan-view-${entry}`}\n onClick={() => setView(entry)}\n >\n {entry}\n </Button>\n ))}\n </nav>\n <Button\n variant=\"ghost\"\n size=\"xs\"\n data-testid=\"plan-close\"\n onClick={() => closeTransientTab(PLAN_TAB_ID)}\n className=\"shrink-0\"\n >\n close\n </Button>\n </header>\n\n {loading && detail === undefined ? (\n <p data-testid=\"plan-loading\" className=\"px-4 py-3 text-sm text-doom-dim\">\n reading this session's plan…\n </p>\n ) : null}\n {error === undefined ? null : (\n <p data-testid=\"plan-error\" className=\"px-4 py-3 text-sm text-doom-red\">\n {error}\n </p>\n )}\n\n <div className=\"flex min-h-0 flex-1 flex-col\">\n {detail === undefined ? null : detail.unavailable ? (\n <p data-testid=\"plan-unavailable\" className=\"px-4 py-3 text-sm text-doom-dim\">\n {detail.reason ?? 'this plan cannot be shown'}\n </p>\n ) : view === 'preview' ? (\n <div\n data-testid=\"plan-preview\"\n className=\"flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto p-4 text-base text-doom-text\"\n >\n <Markdown text={content} />\n </div>\n ) : (\n <>\n {/* The editor owns the scrolling: its gutter has to stay put while\n the text moves, which an outer scroll container would break. */}\n <CodeEditor\n data-testid=\"plan-source\"\n value={content}\n path={detail.path}\n onChange={setDraft}\n className=\"min-h-0 flex-1\"\n />\n <div className=\"flex shrink-0 items-center gap-2 border-t border-doom-border px-3 py-2\">\n <Button\n variant=\"outline\"\n size=\"xs\"\n data-testid=\"plan-save\"\n loading={saving}\n disabled={draft === undefined || saving}\n onClick={() => void save()}\n >\n save to disk\n </Button>\n {draft === undefined ? null : (\n <Button variant=\"ghost\" size=\"xs\" data-testid=\"plan-revert\" onClick={() => setDraft(undefined)}>\n discard edits\n </Button>\n )}\n {saveNote === undefined ? null : (\n <span\n data-testid=\"plan-save-note\"\n className={saveNote === SAVED_NOTE ? 'text-sm text-doom-green' : 'text-sm text-doom-red'}\n >\n {saveNote}\n </span>\n )}\n </div>\n </>\n )}\n </div>\n\n {detail === undefined ? null : (\n <footer data-testid=\"plan-source-path\" className=\"shrink-0 truncate border-t border-doom-border px-4 py-1.5\">\n <span className=\"font-mono text-sm text-doom-dim\">{detail.path}</span>\n </footer>\n )}\n </div>\n );\n}\n\n/** The transient tab the plan opens in; opening it again only focuses it. */\nexport function planTab(): TransientTab {\n return { id: PLAN_TAB_ID, label: 'plan', panel: PlanPanel };\n}\n","import type { WebPluginSlotProps } from '@agimon-ai/po-core/web';\nimport { Button } from '@agimon-ai/po-web-components';\n\nimport { parsePlanStatus, PLAN_STATUS_KEY } from '../../../../../../types/planApi';\nimport { planTab } from './PlanPanel';\n\n/**\n * The plan group's body in the activity dock: the plan this session wrote, and\n * the way in.\n *\n * A plan is one document, so the group is one row rather than a list. The row\n * is the whole point of the group: a written plan the reader cannot open is a\n * path in a tool card, which is what this replaces.\n *\n * The session's status line carries the title and the time it was last\n * written, so the row says what the plan is and whether the agent has revised\n * it since the reader last looked, without the dock holding any state.\n */\nexport function PlanActivitySection({ sessionId, statuses, openTransientTab }: WebPluginSlotProps) {\n const view = parsePlanStatus(statuses[PLAN_STATUS_KEY]);\n\n if (view === undefined) {\n return (\n <p data-testid=\"activity-summary-plan\" className=\"px-1 text-sm text-doom-dim\">\n no plan written yet\n </p>\n );\n }\n\n return (\n <div data-testid=\"activity-plan\" className=\"flex flex-col gap-0.5\">\n <Button\n variant=\"ghost\"\n size=\"card\"\n data-testid=\"activity-plan-open\"\n title={`open ${view.title}`}\n disabled={sessionId === null}\n onClick={() => {\n if (sessionId === null) return;\n openTransientTab(planTab());\n }}\n className=\"min-w-0 gap-0.5 rounded-md px-1 py-1 hover:bg-doom-panel\"\n >\n <span className=\"flex w-full min-w-0 items-center gap-1.5\">\n <span\n data-testid=\"activity-plan-title\"\n className=\"min-w-0 flex-1 truncate text-left text-base font-semibold text-doom-hi\"\n >\n {view.title}\n </span>\n {view.stamp === '' ? null : (\n <span data-testid=\"activity-plan-stamp\" className=\"shrink-0 text-sm tabular-nums text-doom-dim\">\n {view.stamp}\n </span>\n )}\n </span>\n </Button>\n <p className=\"px-1 text-sm leading-relaxed text-doom-dim\">open to read or edit; the agent reads what you save.</p>\n </div>\n );\n}\n","import { defineFill } from '@agimon-ai/po-core/web';\n\nimport { PlanActivitySection } from './_components/PlanActivitySection';\n\nexport default defineFill({ slot: 'activity.plan', id: 'plan', component: PlanActivitySection });\n","import { defineLeaderBinding } from '@agimon-ai/po-core/web';\n\nexport default defineLeaderBinding({\n id: 'plan.toggle',\n path: [\n { key: 'p', label: 'plan', detail: 'read-only planning modes' },\n { key: 'e', label: 'toggle', detail: 'enter or leave read-only planning' },\n ],\n command: 'minor plan',\n});\n","import { defineMinorModeFile } from '@agimon-ai/po-core/web';\n\nexport default defineMinorModeFile({ name: 'plan', keys: 'p e', statusKey: 'plan-mode', order: 20 });\n","import type { ToolPromptDialog, ToolPromptRenderProps } from '@agimon-ai/po-core/web';\nimport {\n Button,\n handleOptionListKey,\n Kbd,\n OptionList,\n optionListHint,\n SectionLabel,\n} from '@agimon-ai/po-web-components';\nimport { useEffect, useRef, useState } from 'react';\n\nimport { PLAN_REVIEW_OPTIONS, PLAN_REVIEW_TITLE } from '../../../../../../types/planApi';\n\n/** Claims only the selector opened by complete_plan, leaving unrelated extension dialogs alone. */\nexport function claimsPlanReviewPrompt(dialog: ToolPromptDialog): boolean {\n return (\n dialog.method === 'select' &&\n dialog.title === PLAN_REVIEW_TITLE &&\n dialog.options.length === PLAN_REVIEW_OPTIONS.length &&\n PLAN_REVIEW_OPTIONS.every((option, index) => dialog.options[index] === option)\n );\n}\n\n/** Keeps plan approval in the composer so the conversation remains visible while the user decides. */\nexport function PlanReviewPrompt({ dialog, answer, cancel }: ToolPromptRenderProps) {\n const [cursor, setCursor] = useState(0);\n const frame = useRef<HTMLDivElement>(null);\n\n useEffect(() => frame.current?.focus(), [dialog.id]);\n\n return (\n <div\n ref={frame}\n role=\"group\"\n tabIndex={-1}\n data-testid=\"plan-review-prompt\"\n className=\"flex min-w-0 flex-col overflow-hidden outline-none\"\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n cancel();\n return;\n }\n handleOptionListKey(event, {\n options: dialog.options,\n cursor,\n onCursorChange: setCursor,\n onSelect: answer,\n });\n }}\n >\n <div className=\"flex flex-col gap-1 px-3.5 pt-3 pb-2\">\n <SectionLabel>plan review</SectionLabel>\n <p className=\"text-base font-semibold leading-relaxed text-doom-hi\">{dialog.title}</p>\n <p className=\"text-sm leading-relaxed text-doom-dim\">\n Review the plan in the conversation before choosing what happens next.\n </p>\n </div>\n\n <OptionList\n options={dialog.options}\n cursor={cursor}\n onCursorChange={setCursor}\n onSelect={answer}\n testIdPrefix=\"plan-review-option\"\n className=\"max-h-48 px-3.5 pb-3\"\n />\n\n <div className=\"flex min-h-[34px] items-center gap-2 border-t border-doom-border-soft px-3.5 py-2\">\n <span className=\"text-sm text-doom-dim\">\n {optionListHint(dialog.options.length)} · <Kbd>esc</Kbd> cancels\n </span>\n <Button variant=\"outline\" size=\"sm\" data-testid=\"plan-review-cancel\" className=\"ml-auto\" onClick={cancel}>\n cancel\n </Button>\n </div>\n </div>\n );\n}\n","import type { ToolResultView } from '@agimon-ai/po-core/web';\nimport type { MessageLine, MessageLineTone } from '@agimon-ai/po-web-components';\n\n/**\n * Purpose-first summaries for the plan-mode tools. The TUI leaves these on\n * Pi's default shell, so the shapes here come straight from the tool\n * definitions in src/services/planServerSession/index.ts: write_plan and complete_plan\n * report through `details` (written, path, phase, exited), run_fable_plan\n * carries the broker result, and record_debug_evidence only confirms.\n */\n\nexport const PLAN_TOOL_NAMES = ['record_debug_evidence', 'run_fable_plan', 'write_plan', 'complete_plan'] as const;\n\n/** The toned lines the shared MessageLines draws; the vocabulary is the components package's. */\nexport type LineTone = MessageLineTone;\nexport type ToolLine = MessageLine;\n\nexport interface PlanCallSummary {\n action: string;\n detail?: string;\n metadata: string[];\n}\n\nexport interface PlanRenderOptions {\n expanded: boolean;\n isError: boolean;\n isPartial: boolean;\n}\n\ntype JsonRecord = Record<string, unknown>;\n\nconst COLLAPSED_BODY_LINES = 12;\nconst PREVIEW_LENGTH = 72;\n\nconst EVIDENCE_LISTS: ReadonlyArray<[key: string, label: string]> = [\n ['logs', 'log'],\n ['correlatedTraceEvidence', 'trace'],\n ['processOutput', 'process output'],\n ['browserConsoleEvidence', 'console entry'],\n ['correlationIds', 'correlation id'],\n ['timestamps', 'timestamp'],\n ['verifiedFacts', 'verified fact'],\n ['hypotheses', 'hypothesis'],\n ['unavailableEvidence', 'unavailable item'],\n];\n\nconst PACKET_LISTS: ReadonlyArray<[key: string, label: string]> = [\n ['goal', 'goal'],\n ['constraints', 'constraint'],\n ['decisions', 'decision'],\n ['verifiedFindings', 'verified finding'],\n ['inferredFindings', 'inferred finding'],\n ['unresolvedQuestions', 'open question'],\n];\n\nfunction asRecord(value: unknown): JsonRecord | undefined {\n return value !== null && typeof value === 'object' && !Array.isArray(value) ? (value as JsonRecord) : undefined;\n}\n\nfunction inlineText(value: string): string {\n let normalized = '';\n for (const character of value) {\n const code = character.codePointAt(0) ?? 0;\n normalized += code <= 31 || code === 127 ? ' ' : character;\n }\n return normalized.replace(/\\s+/gu, ' ').trim();\n}\n\nfunction readString(record: JsonRecord | undefined, key: string): string | undefined {\n const value = record?.[key];\n return typeof value === 'string' ? inlineText(value) || undefined : undefined;\n}\n\nfunction readNumber(record: JsonRecord | undefined, key: string): number | undefined {\n const value = record?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction listLength(record: JsonRecord, key: string): number {\n const value = record[key];\n return Array.isArray(value) ? value.length : 0;\n}\n\nfunction plural(count: number, singular: string): string {\n if (count === 1) return singular;\n if (singular.endsWith('y')) return `${singular.slice(0, -1)}ies`;\n return `${singular}s`;\n}\n\nfunction preview(value: string | undefined): string | undefined {\n if (!value) return undefined;\n return value.length > PREVIEW_LENGTH ? `${value.slice(0, PREVIEW_LENGTH - 3)}…` : value;\n}\n\nfunction countMetadata(args: JsonRecord, lists: ReadonlyArray<[string, string]>): string[] {\n return lists.flatMap(([key, label]) => {\n const count = listLength(args, key);\n return count > 0 ? [`${count} ${plural(count, label)}`] : [];\n });\n}\n\nfunction line(text: string, tone: LineTone, extra: Omit<ToolLine, 'text' | 'tone'> = {}): ToolLine {\n return { text, tone, ...extra };\n}\n\nfunction outputText(result: ToolResultView | null): string {\n if (!result) return '';\n return result.content\n .flatMap((item) => {\n const record = asRecord(item);\n return record?.type === 'text' && typeof record.text === 'string' ? [record.text] : [];\n })\n .join('\\n');\n}\n\nfunction bodyLines(text: string, expanded: boolean): ToolLine[] {\n const all = text\n .split('\\n')\n .map((entry) => entry.trimEnd())\n .filter((entry) => entry.length > 0);\n const shown = expanded ? all : all.slice(0, COLLAPSED_BODY_LINES);\n const lines = shown.map((entry) => line(entry, 'text'));\n if (all.length > shown.length) lines.push(line(`… ${all.length - shown.length} more lines`, 'dim'));\n return lines;\n}\n\n/** The header beside the tool name: what the call is for and how much it carries. */\nexport function planCallSummary(toolName: string, args: JsonRecord): PlanCallSummary {\n if (toolName === 'record_debug_evidence') {\n const issue = preview(readString(args, 'issue'));\n return {\n action: 'record evidence',\n ...(issue ? { detail: issue } : {}),\n metadata: countMetadata(args, EVIDENCE_LISTS),\n };\n }\n if (toolName === 'run_fable_plan') {\n const metadata = countMetadata(args, PACKET_LISTS);\n if (readString(args, 'currentPlan')) metadata.push('with current plan');\n return { action: 'fable draft', metadata };\n }\n if (toolName === 'write_plan') return { action: 'save plan', metadata: [] };\n const decision = readString(args, 'decision');\n return { action: decision ? `decide ${decision}` : 'request approval', metadata: [] };\n}\n\nfunction evidenceLines(args: JsonRecord, result: ToolResultView | null, options: PlanRenderOptions): ToolLine[] {\n const recorded = asRecord(result?.details)?.recorded === true;\n const lines = [\n recorded\n ? line('✓ evidence recorded as planning context', 'success')\n : line(`○ ${inlineText(outputText(result)) || 'not recorded'}`, 'muted'),\n ];\n if (!options.expanded) return lines;\n const fields: ReadonlyArray<[string, string]> = [\n ['issue', 'issue'],\n ['expectedBehavior', 'expected'],\n ['reproductionAttempt', 'reproduction'],\n ['actualBehavior', 'actual'],\n ];\n for (const [key, label] of fields) {\n const value = readString(args, key);\n if (value) lines.push(line(`${label} ${value}`, 'text'));\n }\n for (const [key, label] of EVIDENCE_LISTS) {\n const raw = args[key];\n const items = Array.isArray(raw) ? raw.filter((item): item is string => typeof item === 'string') : [];\n if (items.length === 0) continue;\n lines.push(line(plural(items.length, label), 'hi', { bold: true }));\n lines.push(...items.map((item) => line(inlineText(item), 'text', { indent: true })));\n }\n return lines;\n}\n\nfunction fableLines(result: ToolResultView | null, options: PlanRenderOptions): ToolLine[] {\n const details = asRecord(result?.details);\n const text = outputText(result);\n if (details?.started !== true) {\n const code = readString(details, 'errorCode');\n return [line(`○ ${inlineText(text) || 'fable planning did not start'}${code ? ` · ${code}` : ''}`, 'muted')];\n }\n const status = readString(details, 'status') ?? 'unknown';\n const draft = typeof details?.draft === 'string' ? details.draft : undefined;\n const code = readString(details, 'errorCode');\n if (!draft) return [line(`✗ fable planning ${status}${code ? ` · ${code}` : ''}`, 'error')];\n const finished = status === 'completed';\n return [\n line(\n `${finished ? '✓' : '!'} fable draft · ${status}${code ? ` · ${code}` : ''}`,\n finished ? 'success' : 'warning',\n {\n bold: true,\n },\n ),\n ...bodyLines(draft, options.expanded),\n ];\n}\n\nfunction writeLines(result: ToolResultView | null, options: PlanRenderOptions): ToolLine[] {\n const details = asRecord(result?.details);\n const path = readString(details, 'path');\n if (options.isPartial) {\n const phase = readString(details, 'phase') ?? 'checking';\n return [line(`◐ ${phase}${path ? ` ${path}` : ''}`, 'warning')];\n }\n const duration = readNumber(details, 'durationMs');\n if (details?.written === true) {\n return [line(`✓ wrote ${path ?? 'the plan'}${duration === undefined ? '' : ` · ${duration}ms`}`, 'success')];\n }\n const lines = [line(`○ ${inlineText(outputText(result)) || 'plan not written'}`, 'muted')];\n if (path) lines.push(line(`path ${path}`, 'dim'));\n return lines;\n}\n\nfunction completeLines(result: ToolResultView | null): ToolLine[] {\n const text = inlineText(outputText(result));\n if (asRecord(result?.details)?.exited === true) {\n return [line(`✓ plan mode exited${text ? ` · ${text}` : ''}`, 'success')];\n }\n return [line(`○ staying in plan mode${text ? ` · ${text}` : ''}`, 'muted')];\n}\n\n/** The body of a plan tool card: the outcome line, plus the carried detail once expanded. */\nexport function planResultLines(\n toolName: string,\n args: JsonRecord,\n result: ToolResultView | null,\n options: PlanRenderOptions,\n): ToolLine[] {\n if (options.isError) {\n const text = inlineText(outputText(result));\n return [line(`✗ ${text || `${toolName} failed`}`, 'error')];\n }\n if (toolName === 'record_debug_evidence') return evidenceLines(args, result, options);\n if (toolName === 'run_fable_plan') {\n if (options.isPartial) return [line('◐ drafting with the local Fable broker', 'warning')];\n return fableLines(result, options);\n }\n if (toolName === 'write_plan') return writeLines(result, options);\n if (options.isPartial) return [line('◐ waiting for the review decision', 'warning')];\n return completeLines(result);\n}\n","import type { ToolMessageRenderProps } from '@agimon-ai/po-core/web';\nimport { MessageItem, MessageItemBody, MessageItemHeader, MessageLines, toolTone } from '@agimon-ai/po-web-components';\n\nimport { planCallSummary, planResultLines } from '../_lib/planToolRender';\n\n/**\n * The plan tools' timeline item: the plan action, the issue or decision it\n * targets, and what the packet carries in the header; the outcome line, with\n * the recorded evidence or the Fable draft once expanded, in the body.\n */\nexport function PlanToolMessage({ toolName, args, result, running, isError }: ToolMessageRenderProps) {\n const summary = planCallSummary(toolName, args);\n const collapsed = planResultLines(toolName, args, result, { expanded: false, isError, isPartial: running });\n const full = planResultLines(toolName, args, result, { expanded: true, isError, isPartial: running });\n return (\n <MessageItem tone={toolTone({ running, isError })} expandable={full.length > collapsed.length}>\n {({ expanded }) => (\n <>\n <MessageItemHeader title={summary.action}>\n <span\n data-testid={`tool-call-${toolName}`}\n title={[summary.detail, ...summary.metadata].filter(Boolean).join(' · ') || undefined}\n className=\"min-w-0 flex-1 truncate text-doom-dim\"\n >\n {[summary.detail, ...summary.metadata].filter(Boolean).join(' · ')}\n </span>\n </MessageItemHeader>\n {(expanded ? full : collapsed).length > 0 ? (\n <MessageItemBody data-testid={`tool-result-${toolName}`}>\n <MessageLines lines={expanded ? full : collapsed} />\n </MessageItemBody>\n ) : null}\n </>\n )}\n </MessageItem>\n );\n}\n","import { defineToolRenderer } from '@agimon-ai/po-core/web';\n\nimport { claimsPlanReviewPrompt, PlanReviewPrompt } from './_components/PlanReviewPrompt';\nimport { PlanToolMessage } from './_components/PlanToolMessage';\nimport { PLAN_TOOL_NAMES } from './_lib/planToolRender';\n\nexport default defineToolRenderer({\n tools: [...PLAN_TOOL_NAMES],\n message: PlanToolMessage,\n prompt: { claims: claimsPlanReviewPrompt, component: PlanReviewPrompt },\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/po-core/web';\n\nimport settingPlan from '../src/extensions/(frontend)/setting/plan.web';\nimport settingPlan2 from '../src/extensions/workspaces/(frontend)/setting/plan.web';\nimport activityGroupPlan from '../src/extensions/workspaces/sessions/(frontend)/activity-group/plan.web';\nimport fillPlan from '../src/extensions/workspaces/sessions/(frontend)/fill/Plan.activity.plan.web';\nimport leaderToggle from '../src/extensions/workspaces/sessions/(frontend)/leader/toggle.web';\nimport modePlan from '../src/extensions/workspaces/sessions/(frontend)/mode/plan/mode.web';\nimport toolPlan from '../src/extensions/workspaces/sessions/(frontend)/tool/plan.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'plan',\n global: {\n get settingsSections(): WebPluginContributions['settingsSections'] { return [via({ id: 'plan' }, settingPlan)]; },\n },\n workspace: {\n get settingsSections(): WebPluginContributions['settingsSections'] { return [via({ id: 'plan' }, settingPlan2)]; },\n },\n session: {\n get activityGroups(): WebPluginContributions['activityGroups'] { return [via({ name: 'plan' }, activityGroupPlan)]; },\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'activity.plan', id: 'plan' }, fillPlan)]; },\n get leaderBindings(): WebPluginContributions['leaderBindings'] { return [via({ id: 'plan.toggle' }, leaderToggle)]; },\n get minorModes(): WebPluginContributions['minorModes'] { return [via({ name: 'plan' }, modePlan)]; },\n get toolRenderers(): WebPluginContributions['toolRenderers'] { return [via({ tools: ['plan'] }, toolPlan)]; },\n },\n});\n"],"mappings":"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"}
@@ -0,0 +1,214 @@
1
+ import { Buffer } from "node:buffer";
2
+ //#region src/services/fableFlow/index.ts
3
+ const FABLE_PROVIDER_SOURCE = "@agimon-ai/po-team";
4
+ const FABLE_PLAN_REQUESTER = "@agimon-ai/po-plan";
5
+ const FABLE_PLAN_RUNTIME = "claude";
6
+ const FABLE_PLAN_MODEL = "fable";
7
+ const FABLE_PLAN_PROFILE = "claude/fable-plan-v1";
8
+ const MAX_FABLE_PACKET_BYTES = 32768;
9
+ const MAX_FABLE_TEXT_BYTES = 4096;
10
+ const MAX_FABLE_ARRAY_ITEMS = 24;
11
+ const SECRET_PATTERNS = [
12
+ /(?:api[_-]?key|access[_-]?token|client[_-]?secret|password|private[_-]?key|authorization|bearer)\s*[:=]/iu,
13
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----/u,
14
+ /\bsk-[A-Za-z0-9_-]{16,}\b/u,
15
+ /\bgh[pousr]_[A-Za-z0-9_]{16,}\b/u
16
+ ];
17
+ const CONTROL_CHARACTER_MAX = 31;
18
+ const DELETE_CHARACTER = 127;
19
+ const MAX_SOURCE_PATH_LENGTH = 512;
20
+ function isRecord(value) {
21
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22
+ }
23
+ function hasControlCharacter(value) {
24
+ for (const character of value) {
25
+ const code = character.codePointAt(0);
26
+ if (code !== void 0 && (code <= CONTROL_CHARACTER_MAX || code === DELETE_CHARACTER)) return true;
27
+ }
28
+ return false;
29
+ }
30
+ function containsSuspectedCredential(value) {
31
+ return SECRET_PATTERNS.some((pattern) => pattern.test(value));
32
+ }
33
+ function boundedText(value, field, required = true) {
34
+ if (typeof value !== "string") throw new Error(`${field} must be a string.`);
35
+ const text = value.trim();
36
+ if (!text && required) throw new Error(`${field} must not be empty.`);
37
+ if (Buffer.byteLength(text, "utf8") > 4096 || hasControlCharacter(text)) throw new Error(`${field} exceeds the bounded Fable text limit or contains control characters.`);
38
+ if (containsSuspectedCredential(text)) throw new Error(`${field} appears to contain a credential.`);
39
+ return text;
40
+ }
41
+ function boundedList(value, field, required = true) {
42
+ if (!Array.isArray(value)) throw new Error(`${field} must be an array.`);
43
+ if (value.length > 24) throw new Error(`${field} exceeds the bounded item limit.`);
44
+ const items = value.map((item, index) => boundedText(item, `${field}[${index}]`));
45
+ if (required && items.length === 0) throw new Error(`${field} must contain at least one item.`);
46
+ return items;
47
+ }
48
+ function boundedPath(value, field) {
49
+ if (typeof value !== "string") throw new Error(`${field} must be a string.`);
50
+ const sourcePath = value.trim();
51
+ if (!sourcePath || sourcePath.length > MAX_SOURCE_PATH_LENGTH || hasControlCharacter(sourcePath)) throw new Error(`${field} is not a safe source path.`);
52
+ if (containsSuspectedCredential(sourcePath)) throw new Error(`${field} appears to contain a credential.`);
53
+ return sourcePath;
54
+ }
55
+ function boundedFindings(value) {
56
+ if (!Array.isArray(value)) throw new Error("verifiedFindings must be an array.");
57
+ if (value.length > 24) throw new Error("verifiedFindings exceeds the bounded item limit.");
58
+ return value.map((item, index) => {
59
+ if (!isRecord(item)) throw new Error(`verifiedFindings[${index}] must be an object.`);
60
+ if (Object.keys(item).some((key) => key !== "path" && key !== "finding")) throw new Error(`verifiedFindings[${index}] contains an unsupported field.`);
61
+ return {
62
+ path: boundedPath(item.path, `verifiedFindings[${index}].path`),
63
+ finding: boundedText(item.finding, `verifiedFindings[${index}].finding`)
64
+ };
65
+ });
66
+ }
67
+ function assertAllowedKeys(value, keys, field) {
68
+ const allowed = new Set(keys);
69
+ const unexpected = Object.keys(value).find((key) => !allowed.has(key));
70
+ if (unexpected) throw new Error(`${field} contains unsupported field '${unexpected}'.`);
71
+ }
72
+ function sanitizeFablePacket(value) {
73
+ if (!isRecord(value)) throw new Error("Fable packet must be an object.");
74
+ assertAllowedKeys(value, [
75
+ "goal",
76
+ "constraints",
77
+ "decisions",
78
+ "verifiedFindings",
79
+ "inferredFindings",
80
+ "unresolvedQuestions",
81
+ "currentPlan"
82
+ ], "Fable packet");
83
+ const packet = {
84
+ goal: boundedList(value.goal, "goal"),
85
+ constraints: boundedList(value.constraints, "constraints", false),
86
+ decisions: boundedList(value.decisions, "decisions", false),
87
+ verifiedFindings: boundedFindings(value.verifiedFindings),
88
+ inferredFindings: boundedList(value.inferredFindings, "inferredFindings", false),
89
+ unresolvedQuestions: boundedList(value.unresolvedQuestions, "unresolvedQuestions", false),
90
+ ...value.currentPlan === void 0 ? {} : { currentPlan: boundedText(value.currentPlan, "currentPlan", false) }
91
+ };
92
+ if (Buffer.byteLength(JSON.stringify(packet), "utf8") > 32768) throw new Error("Fable packet exceeds the bounded packet size.");
93
+ return packet;
94
+ }
95
+ function safeResult(result) {
96
+ if (containsSuspectedCredential([result.draft, result.review].filter((value) => value !== void 0).join("\n"))) return {
97
+ operationId: result.operationId,
98
+ status: "failed",
99
+ stage: "failed",
100
+ durationMs: result.durationMs,
101
+ errorCode: "unsafe_output"
102
+ };
103
+ return result;
104
+ }
105
+ function timedOutResult(operationId, startedAt) {
106
+ return {
107
+ operationId,
108
+ status: "timed_out",
109
+ stage: "interrupted",
110
+ durationMs: Math.max(0, Date.now() - startedAt),
111
+ errorCode: "timeout"
112
+ };
113
+ }
114
+ function createFablePlanFlow(options) {
115
+ const newOperationId = options.newOperationId ?? (() => crypto.randomUUID());
116
+ let active;
117
+ const cancel = (reason = "Fable operation cancelled.") => {
118
+ const current = active;
119
+ if (!current) return;
120
+ if (!current.cancelSent) {
121
+ current.cancelSent = true;
122
+ try {
123
+ options.broker?.cancel(current.operationId, reason);
124
+ } catch (error) {
125
+ options.onError?.(error);
126
+ }
127
+ }
128
+ current.controller.abort(new Error(reason));
129
+ };
130
+ const run = async (value, signal) => {
131
+ if (active) throw new Error("A Fable operation is already active for this session.");
132
+ if (!options.isAuthorized()) throw new Error("The Fable capability profile is not authorized.");
133
+ const packet = sanitizeFablePacket(value);
134
+ const operationId = `fable-${newOperationId()}`;
135
+ const startedAt = Date.now();
136
+ const controller = new AbortController();
137
+ active = {
138
+ operationId,
139
+ controller,
140
+ cancelSent: false
141
+ };
142
+ options.onStage?.("draft");
143
+ const parentAbort = () => controller.abort(signal?.reason);
144
+ if (signal) {
145
+ signal.addEventListener("abort", parentAbort, { once: true });
146
+ if (signal.aborted) parentAbort();
147
+ }
148
+ const brokerRequest = {
149
+ requester: FABLE_PLAN_REQUESTER,
150
+ operationId,
151
+ runtime: FABLE_PLAN_RUNTIME,
152
+ model: FABLE_PLAN_MODEL,
153
+ profile: FABLE_PLAN_PROFILE,
154
+ packet
155
+ };
156
+ let timeout;
157
+ let timeoutTriggered = false;
158
+ try {
159
+ if (!options.broker) return timedOutResult(operationId, startedAt);
160
+ const brokerResult = options.broker.start(brokerRequest, controller.signal);
161
+ const cancelledResult = new Promise((resolve) => {
162
+ const onAbort = () => {
163
+ if (timeoutTriggered) return;
164
+ resolve({
165
+ operationId,
166
+ status: "cancelled",
167
+ stage: "cancelled",
168
+ durationMs: Math.max(0, Date.now() - startedAt),
169
+ errorCode: "cancelled"
170
+ });
171
+ };
172
+ controller.signal.addEventListener("abort", onAbort, { once: true });
173
+ });
174
+ const timeoutResult = options.timeoutMs === void 0 ? void 0 : new Promise((resolve) => {
175
+ timeout = setTimeout(() => {
176
+ timeoutTriggered = true;
177
+ cancel("Fable operation timed out.");
178
+ resolve(timedOutResult(operationId, startedAt));
179
+ }, options.timeoutMs);
180
+ });
181
+ const safe = safeResult(timeoutResult === void 0 ? await Promise.race([brokerResult, cancelledResult]) : await Promise.race([
182
+ brokerResult,
183
+ timeoutResult,
184
+ cancelledResult
185
+ ]));
186
+ options.onStage?.(safe.status === "completed" ? "completed" : safe.status === "cancelled" ? "cancelled" : safe.stage);
187
+ return safe;
188
+ } catch (error) {
189
+ options.onError?.(error);
190
+ const failure = {
191
+ operationId,
192
+ status: "failed",
193
+ stage: "failed",
194
+ durationMs: Math.max(0, Date.now() - startedAt),
195
+ errorCode: "unavailable"
196
+ };
197
+ options.onStage?.("failed");
198
+ return failure;
199
+ } finally {
200
+ if (timeout) clearTimeout(timeout);
201
+ if (signal) signal.removeEventListener("abort", parentAbort);
202
+ active = void 0;
203
+ }
204
+ };
205
+ return {
206
+ run,
207
+ cancel,
208
+ isActive: () => active !== void 0
209
+ };
210
+ }
211
+ //#endregion
212
+ export { FABLE_PROVIDER_SOURCE as a, MAX_FABLE_TEXT_BYTES as c, sanitizeFablePacket as d, FABLE_PLAN_RUNTIME as i, containsSuspectedCredential as l, FABLE_PLAN_PROFILE as n, MAX_FABLE_ARRAY_ITEMS as o, FABLE_PLAN_REQUESTER as r, MAX_FABLE_PACKET_BYTES as s, FABLE_PLAN_MODEL as t, createFablePlanFlow as u };
213
+
214
+ //# sourceMappingURL=fableFlow-BDc8a__Y.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fableFlow-BDc8a__Y.mjs","names":[],"sources":["../src/services/fableFlow/index.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\n\nexport const FABLE_PROVIDER_SOURCE = '@agimon-ai/po-team';\nexport const FABLE_PLAN_REQUESTER = '@agimon-ai/po-plan';\nexport const FABLE_PLAN_RUNTIME = 'claude';\nexport const FABLE_PLAN_MODEL = 'fable';\nexport const FABLE_PLAN_PROFILE = 'claude/fable-plan-v1';\nexport const MAX_FABLE_PACKET_BYTES = 32 * 1024;\nexport const MAX_FABLE_TEXT_BYTES = 4 * 1024;\nexport const MAX_FABLE_ARRAY_ITEMS = 24;\n\nconst SECRET_PATTERNS = [\n /(?:api[_-]?key|access[_-]?token|client[_-]?secret|password|private[_-]?key|authorization|bearer)\\s*[:=]/iu,\n /-----BEGIN [A-Z ]*PRIVATE KEY-----/u,\n /\\bsk-[A-Za-z0-9_-]{16,}\\b/u,\n /\\bgh[pousr]_[A-Za-z0-9_]{16,}\\b/u,\n];\nconst CONTROL_CHARACTER_MAX = 31;\nconst DELETE_CHARACTER = 127;\nconst MAX_SOURCE_PATH_LENGTH = 512;\n\nexport interface FablePlanFinding {\n path: string;\n finding: string;\n}\n\nexport interface FablePlanPacket {\n goal: string[];\n constraints: string[];\n decisions: string[];\n verifiedFindings: FablePlanFinding[];\n inferredFindings: string[];\n unresolvedQuestions: string[];\n currentPlan?: string;\n}\n\nexport type FablePlanStatus = 'completed' | 'failed' | 'timed_out' | 'cancelled' | 'interrupted';\nexport type FablePlanStage = 'draft' | 'review' | 'completed' | 'failed' | 'cancelled' | 'interrupted';\n\nexport interface FablePlanResult {\n operationId: string;\n status: FablePlanStatus;\n stage: FablePlanStage;\n draft?: string;\n review?: string;\n draftRunId?: string;\n reviewRunId?: string;\n durationMs: number;\n errorCode?: string;\n}\n\nexport interface FablePlanRequest {\n requester: typeof FABLE_PLAN_REQUESTER;\n operationId: string;\n runtime: typeof FABLE_PLAN_RUNTIME;\n model: typeof FABLE_PLAN_MODEL;\n profile: typeof FABLE_PLAN_PROFILE;\n packet: FablePlanPacket;\n}\n\nexport interface FablePlanBroker {\n start(request: FablePlanRequest, signal: AbortSignal): Promise<FablePlanResult>;\n cancel(operationId: string, reason: string): void;\n}\n\nexport interface FableFlowOptions {\n broker?: FablePlanBroker;\n isAuthorized: () => boolean;\n /** Supplies operation ids. Injected so a test can pin them. */\n newOperationId?: () => string;\n timeoutMs?: number;\n onStage?: (stage: FableStage) => void;\n onError?: (error: unknown) => void;\n}\n\nexport type FableStage = 'idle' | 'draft' | 'review' | 'completed' | 'failed' | 'cancelled' | 'interrupted';\n\nexport interface FablePlanFlow {\n run(value: unknown, signal?: AbortSignal): Promise<FablePlanResult>;\n cancel(reason?: string): void;\n isActive(): boolean;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction hasControlCharacter(value: string): boolean {\n for (const character of value) {\n const code = character.codePointAt(0);\n if (code !== undefined && (code <= CONTROL_CHARACTER_MAX || code === DELETE_CHARACTER)) return true;\n }\n return false;\n}\n\nexport function containsSuspectedCredential(value: string): boolean {\n return SECRET_PATTERNS.some((pattern) => pattern.test(value));\n}\n\nfunction boundedText(value: unknown, field: string, required = true): string {\n if (typeof value !== 'string') throw new Error(`${field} must be a string.`);\n const text = value.trim();\n if (!text && required) throw new Error(`${field} must not be empty.`);\n if (Buffer.byteLength(text, 'utf8') > MAX_FABLE_TEXT_BYTES || hasControlCharacter(text)) {\n throw new Error(`${field} exceeds the bounded Fable text limit or contains control characters.`);\n }\n if (containsSuspectedCredential(text)) throw new Error(`${field} appears to contain a credential.`);\n return text;\n}\n\nfunction boundedList(value: unknown, field: string, required = true): string[] {\n if (!Array.isArray(value)) throw new Error(`${field} must be an array.`);\n if (value.length > MAX_FABLE_ARRAY_ITEMS) throw new Error(`${field} exceeds the bounded item limit.`);\n const items = value.map((item, index) => boundedText(item, `${field}[${index}]`));\n if (required && items.length === 0) throw new Error(`${field} must contain at least one item.`);\n return items;\n}\n\nfunction boundedPath(value: unknown, field: string): string {\n if (typeof value !== 'string') throw new Error(`${field} must be a string.`);\n const sourcePath = value.trim();\n if (!sourcePath || sourcePath.length > MAX_SOURCE_PATH_LENGTH || hasControlCharacter(sourcePath)) {\n throw new Error(`${field} is not a safe source path.`);\n }\n if (containsSuspectedCredential(sourcePath)) throw new Error(`${field} appears to contain a credential.`);\n return sourcePath;\n}\n\nfunction boundedFindings(value: unknown): FablePlanFinding[] {\n if (!Array.isArray(value)) throw new Error('verifiedFindings must be an array.');\n if (value.length > MAX_FABLE_ARRAY_ITEMS) throw new Error('verifiedFindings exceeds the bounded item limit.');\n return value.map((item, index) => {\n if (!isRecord(item)) throw new Error(`verifiedFindings[${index}] must be an object.`);\n const keys = Object.keys(item);\n if (keys.some((key) => key !== 'path' && key !== 'finding')) {\n throw new Error(`verifiedFindings[${index}] contains an unsupported field.`);\n }\n return {\n path: boundedPath(item.path, `verifiedFindings[${index}].path`),\n finding: boundedText(item.finding, `verifiedFindings[${index}].finding`),\n };\n });\n}\n\nfunction assertAllowedKeys(value: Record<string, unknown>, keys: readonly string[], field: string): void {\n const allowed = new Set(keys);\n const unexpected = Object.keys(value).find((key) => !allowed.has(key));\n if (unexpected) throw new Error(`${field} contains unsupported field '${unexpected}'.`);\n}\n\nexport function sanitizeFablePacket(value: unknown): FablePlanPacket {\n if (!isRecord(value)) throw new Error('Fable packet must be an object.');\n assertAllowedKeys(\n value,\n ['goal', 'constraints', 'decisions', 'verifiedFindings', 'inferredFindings', 'unresolvedQuestions', 'currentPlan'],\n 'Fable packet',\n );\n const packet: FablePlanPacket = {\n goal: boundedList(value.goal, 'goal'),\n constraints: boundedList(value.constraints, 'constraints', false),\n decisions: boundedList(value.decisions, 'decisions', false),\n verifiedFindings: boundedFindings(value.verifiedFindings),\n inferredFindings: boundedList(value.inferredFindings, 'inferredFindings', false),\n unresolvedQuestions: boundedList(value.unresolvedQuestions, 'unresolvedQuestions', false),\n ...(value.currentPlan === undefined ? {} : { currentPlan: boundedText(value.currentPlan, 'currentPlan', false) }),\n };\n if (Buffer.byteLength(JSON.stringify(packet), 'utf8') > MAX_FABLE_PACKET_BYTES) {\n throw new Error('Fable packet exceeds the bounded packet size.');\n }\n return packet;\n}\n\nfunction safeResult(result: FablePlanResult): FablePlanResult {\n const output = [result.draft, result.review].filter((value): value is string => value !== undefined).join('\\n');\n if (containsSuspectedCredential(output)) {\n return {\n operationId: result.operationId,\n status: 'failed',\n stage: 'failed',\n durationMs: result.durationMs,\n errorCode: 'unsafe_output',\n };\n }\n return result;\n}\n\nfunction timedOutResult(operationId: string, startedAt: number): FablePlanResult {\n return {\n operationId,\n status: 'timed_out',\n stage: 'interrupted',\n durationMs: Math.max(0, Date.now() - startedAt),\n errorCode: 'timeout',\n };\n}\n\nexport function createFablePlanFlow(options: FableFlowOptions): FablePlanFlow {\n const newOperationId = options.newOperationId ?? (() => crypto.randomUUID());\n let active:\n | {\n operationId: string;\n controller: AbortController;\n cancelSent: boolean;\n }\n | undefined;\n\n const cancel = (reason = 'Fable operation cancelled.'): void => {\n const current = active;\n if (!current) return;\n if (!current.cancelSent) {\n current.cancelSent = true;\n try {\n options.broker?.cancel(current.operationId, reason);\n } catch (error) {\n options.onError?.(error);\n }\n }\n current.controller.abort(new Error(reason));\n };\n\n const run = async (value: unknown, signal?: AbortSignal): Promise<FablePlanResult> => {\n if (active) throw new Error('A Fable operation is already active for this session.');\n if (!options.isAuthorized()) throw new Error('The Fable capability profile is not authorized.');\n const packet = sanitizeFablePacket(value);\n const operationId = `fable-${newOperationId()}`;\n const startedAt = Date.now();\n const controller = new AbortController();\n active = { operationId, controller, cancelSent: false };\n options.onStage?.('draft');\n\n const parentAbort = (): void => controller.abort(signal?.reason);\n if (signal) {\n signal.addEventListener('abort', parentAbort, { once: true });\n if (signal.aborted) parentAbort();\n }\n\n const brokerRequest: FablePlanRequest = {\n requester: FABLE_PLAN_REQUESTER,\n operationId,\n runtime: FABLE_PLAN_RUNTIME,\n model: FABLE_PLAN_MODEL,\n profile: FABLE_PLAN_PROFILE,\n packet,\n };\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let timeoutTriggered = false;\n try {\n if (!options.broker) return timedOutResult(operationId, startedAt);\n const brokerResult = options.broker.start(brokerRequest, controller.signal);\n const cancelledResult = new Promise<FablePlanResult>((resolve) => {\n const onAbort = (): void => {\n if (timeoutTriggered) return;\n resolve({\n operationId,\n status: 'cancelled',\n stage: 'cancelled',\n durationMs: Math.max(0, Date.now() - startedAt),\n errorCode: 'cancelled',\n });\n };\n controller.signal.addEventListener('abort', onAbort, { once: true });\n });\n const timeoutResult =\n options.timeoutMs === undefined\n ? undefined\n : new Promise<FablePlanResult>((resolve) => {\n timeout = setTimeout(() => {\n timeoutTriggered = true;\n cancel('Fable operation timed out.');\n resolve(timedOutResult(operationId, startedAt));\n }, options.timeoutMs);\n });\n const result =\n timeoutResult === undefined\n ? await Promise.race([brokerResult, cancelledResult])\n : await Promise.race([brokerResult, timeoutResult, cancelledResult]);\n const safe = safeResult(result);\n options.onStage?.(\n safe.status === 'completed' ? 'completed' : safe.status === 'cancelled' ? 'cancelled' : safe.stage,\n );\n return safe;\n } catch (error) {\n options.onError?.(error);\n const failure: FablePlanResult = {\n operationId,\n status: 'failed',\n stage: 'failed',\n durationMs: Math.max(0, Date.now() - startedAt),\n errorCode: 'unavailable',\n };\n options.onStage?.('failed');\n return failure;\n } finally {\n if (timeout) clearTimeout(timeout);\n if (signal) signal.removeEventListener('abort', parentAbort);\n active = undefined;\n }\n };\n\n return {\n run,\n cancel,\n isActive: () => active !== undefined,\n };\n}\n"],"mappings":"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"}