@ai-agent-forge/plugin-workflow 0.87.0 → 0.88.1

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/agent-forge.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "packageFormatVersion": 1,
3
3
  "id": "agent-forge.plugin.workflow",
4
- "pluginVersion": "0.87.0",
4
+ "pluginVersion": "0.88.1",
5
5
  "apiVersion": "1",
6
6
  "minHostVersion": "0.84.4",
7
7
  "platform": {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AA2BrF,YAAY,EACX,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,SAAS,EACT,YAAY,EACZ,SAAS,EACT,eAAe,EACf,eAAe,EACf,kBAAkB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/E,YAAY,EACX,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,uBAAuB,EACvB,yBAAyB,GACzB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AA0V7E,wBAAgB,gBAAgB,CAC/B,GAAG,EAAE,aAAa,GAChB,OAAO,CAAC,SAAS,sBAAsB,EAAE,CAAC,GAAG,SAAS,sBAAsB,EAAE,CA0MhF","sourcesContent":["/**\n * Workflow orchestration capability plugin (M1 of\n * `docs/design/工作流编排插件设计.md`): a fourth self-contained state machine\n * orchestrating a stage dependency graph on top of the goal/plan/todo layers.\n *\n * Persistence protocol, structurally identical to the goal/plan/todo plugins:\n * append-only `workflow.state` full snapshots (the single rebuild source) plus\n * one `workflow.transition` audit entry per conversion, announced as\n * `workflow.changed` (version 1, correlationId \"workflow.state\", data = the\n * complete WorkflowState snapshot).\n *\n * This plugin is the repository's FIRST event subscriber (§7): `plan.changed`\n * is used as a trigger signal only — its in-memory data may be misaligned\n * with the current branch, so every reevaluation reads the plan facts from\n * the current branch entries. The handler is fully isolated (§5 总则 3): any\n * failure lands in diagnostics + a `handler.error` transition and never\n * rethrows at the publisher (an unisolated throw would pierce plan_set's tool\n * return). Soft references (goalId / planId / role) never fail a write (#10):\n * they surface as response warnings; a dangling planId additionally holds the\n * stage's activation until a reevaluation finds the plan.\n */\n\nimport type { CapabilityAPI, DisposableRegistration } from \"@agent-forge/plugin-sdk\";\nimport { Type } from \"typebox\";\nimport {\n\ttype AdvanceAction,\n\ttype AdvanceInput,\n\tapplyAdvance,\n\tapplyWorkflowSet,\n\ttype EngineDeps,\n\ttype EngineRun,\n\ttype EvidenceItem,\n\tinspectWorkflow,\n\ttype PlanFacts,\n\treevaluateAll,\n\ttype WorkflowClosure,\n\ttype WorkflowInspection,\n} from \"./engine.ts\";\nimport { createWorkflowToolResultRenderer, renderLog, renderPanorama, renderWhy } from \"./render.ts\";\nimport {\n\ttype TransitionReasonCode,\n\tWORKFLOW_STATE_ENTRY,\n\tWORKFLOW_TRANSITION_ENTRY,\n\ttype WorkflowState,\n\ttype WorkflowTransitionEntry,\n\ttype WorkflowTransitionTrigger,\n} from \"./types.ts\";\nimport { asObject, asText, parseWorkflowSetInput, planWorkflowSpecUpdate, validateStageGraph } from \"./validate.ts\";\n\nexport type {\n\tAcceptanceCoverage,\n\tAdvanceAction,\n\tAdvanceInput,\n\tClosureEvidenceItem,\n\tEngineDeps,\n\tEngineRun,\n\tEvidenceItem,\n\tPlanFacts,\n\tStageInspection,\n\tWorkflowClosure,\n\tWorkflowInspection,\n} from \"./engine.ts\";\nexport { cascadeLimitFor, computeWaitingOn, reevaluateAll } from \"./engine.ts\";\nexport type {\n\tStageRuntime,\n\tStageSpec,\n\tTransitionReason,\n\tTransitionReasonCode,\n\tWorkflowRecord,\n\tWorkflowState,\n\tWorkflowTransitionEntry,\n\tWorkflowTransitionTrigger,\n} from \"./types.ts\";\nexport { WORKFLOW_STATE_ENTRY, WORKFLOW_TRANSITION_ENTRY } from \"./types.ts\";\n\n/** Session protocol key of the plan plugin's snapshot entry (plugins/plan). */\nconst planStateEntry = \"plan.state\";\n/** Session protocol key of the goal plugin's snapshot entry (plugins/goal). */\nconst goalStateEntry = \"goal.state\";\nconst DIAGNOSTICS_CAP = 50;\n\n/** §3.1 applicability, verbatim scope (when to use / not to use a workflow). */\nconst WORKFLOW_APPLICABILITY_GUIDE =\n\t\"Workflow applicability — use a workflow only when ALL of these hold: (1) the goal needs at least 3 stages that can be accepted independently (you can write each stage's objective/acceptance); (2) the stages have a real dependency structure (branching or joins), not a plain sequence; (3) independent branches can be pushed in parallel — parallelism is a core payoff, purely linear multi-stage work belongs to goal/plan/todo; (4) the task spans multiple turns/days and is valuable enough to justify process overhead (definition, evidence recording, stage gates). Counterexamples that keep goal/plan/todo or direct execution: single-step tasks; two- or three-step linear tasks; exploratory work with no presettable dependency graph; one-shot questions. A workflow does not exclude the three layers — each stage still uses plan/todo for its execution checklist. Never escalate a small task to a workflow by default.\";\n\nconst WORKFLOW_AUTHORING_GUIDE =\n\t\"Define each stage by a verifiable end state, not by a work list or a role: objective.statement says what must be achieved; acceptance items list the evidence that proves it, referenced from real tool output. Stop splitting at the first verifiable boundary — finer handoffs lose more to interface drift than they gain in drift protection.\";\n\nconst WORKFLOW_EXECUTION_GUIDE =\n\t\"After plan_set marks a stage's plan completed, run workflow_advance inspect to see unlocked stages and acceptance coverage. Record evidence (workflow_advance record-evidence) before completion is evaluated; lower-bound stages (no acceptance declared) need no record-evidence. When multiple stages are active, delegate one subagent per stage following stage.role and push them in parallel.\";\n\nconst WORKFLOW_CORRECTION_GUIDE =\n\t\"On user corrections: run workflow_advance inspect first to confirm the current state, then route by what changed — amend the stage spec (active/awaiting_review stages, amendNote required), append a stage, skip, retreat, or cancel — and report the adjustment and its impact range back to the user.\";\n\nconst WORKFLOW_REVIEW_GUIDE =\n\t\"Stages declared advance: 'review' stop at awaiting_review. Before approve you must present the result and its evidence to the user and get explicit confirmation — never approve on your own (the engine does not authorize; the discipline lives here).\";\n\nconst WORKFLOW_CONCURRENCY_GUIDE =\n\t\"Do not send workflow_set in the same parallel batch as plan_set: amends and completion evaluation race (completed freezes first; if the amend loses, recover via retreat).\";\n\nconst WORKFLOW_INCREMENTAL_DEMAND_GUIDE =\n\t\"When a new batch of requirements arrives (incremental features on top of shipped work), default to creating a NEW workflow to carry it — reuse the same goalId and keep the completed workflow's history intact; never pull a completed workflow back into motion. Retreat or append a corrective stage only when the new requirements overturn already completed output.\";\n\nconst WORKFLOW_EXECUTOR_GUIDE =\n\t\"When delegating a stage to a subagent, pass that subagent's session/task id as the executor field of record-evidence — it is recorded on the stage (advisory) so /workflow log shows who drove it.\";\n\nconst acceptanceSchema = Type.Object({\n\tid: Type.String({ description: \"Acceptance item id; evidence references this id\" }),\n\tstatement: Type.String({ description: \"Verifiable completion criterion for this item\" }),\n});\n\nconst stageSchema = Type.Object({\n\tid: Type.String({ description: \"Stable stage id, unique within the workflow\" }),\n\ttitle: Type.String({ description: \"Human-readable stage title\" }),\n\tobjective: Type.Object({\n\t\tstatement: Type.String({\n\t\t\tdescription: \"Verifiable end state this stage must reach (required, non-empty)\",\n\t\t}),\n\t\tacceptance: Type.Optional(\n\t\t\tType.Array(acceptanceSchema, {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Completion criteria items; when empty (or omitted) the lower bound is the referenced plan's completion evidence\",\n\t\t\t}),\n\t\t),\n\t}),\n\tdependsOn: Type.Optional(\n\t\tType.Array(Type.String(), { description: \"Hard prerequisite stage ids; empty array = root stage\" }),\n\t),\n\tplanId: Type.Optional(Type.String({ description: \"Soft reference to a plan id; missing = pure checkpoint stage\" })),\n\tkind: Type.Optional(\n\t\tType.Union([Type.Literal(\"read\"), Type.Literal(\"write\")], {\n\t\t\tdescription: 'Stage kind; write stages are scope-checked, default \"write\"',\n\t\t}),\n\t),\n\tscope: Type.Optional(\n\t\tType.Array(Type.String(), {\n\t\t\tdescription: \"Write scope tags (modules/dirs); missing = global (intersects every other write stage)\",\n\t\t}),\n\t),\n\trole: Type.Optional(\n\t\tType.String({ description: \"Delegation advice: role id for the subagent recommended to drive this stage\" }),\n\t),\n\tadvance: Type.Optional(\n\t\tType.Union([Type.Literal(\"auto\"), Type.Literal(\"review\")], {\n\t\t\tdescription:\n\t\t\t\t'\"auto\" completes on evaluation, \"review\" stops at awaiting_review (planId stages only), default \"auto\"',\n\t\t}),\n\t),\n\tretryLimit: Type.Optional(\n\t\tType.Integer({ description: \"In-place retry limit before onFail/workflow failure, default 2\", minimum: 0 }),\n\t),\n\tonFail: Type.Optional(\n\t\tType.String({ description: \"Explicit cross-stage retreat target (a topologically earlier stage id)\" }),\n\t),\n\tamendNote: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Correction reason; required when an active/awaiting_review stage's spec changes\",\n\t\t}),\n\t),\n});\n\nconst workflowSetSchema = Type.Object({\n\tid: Type.String({ description: \"Stable id of the workflow to create or update\" }),\n\ttitle: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Human-readable workflow title; required on create, omitting on update keeps the stored value\",\n\t\t}),\n\t),\n\trevision: Type.Integer({\n\t\tdescription: \"New revision counter; must be greater than the stored revision of an existing workflow\",\n\t\tminimum: 0,\n\t}),\n\tgoalId: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Soft reference to a goal id; the closure evidence summary is addressed to this goal. Omitting on update keeps the stored value\",\n\t\t}),\n\t),\n\tmaxRetreats: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription:\n\t\t\t\t\"Workflow lifetime retreat budget before failure, default 3. Omitting on update keeps the stored value\",\n\t\t\tminimum: 0,\n\t\t}),\n\t),\n\tmaxActiveStages: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Parallel activation cap, default 4. Omitting on update keeps the stored value\",\n\t\t\tminimum: 1,\n\t\t}),\n\t),\n\tstages: Type.Array(stageSchema, {\n\t\tdescription: \"Full stage list (whole submit, not a patch); the engine aligns runtime by stage id\",\n\t}),\n});\n\nconst workflowAdvanceSchema = Type.Object({\n\tworkflowId: Type.String({ description: \"Target workflow id\" }),\n\taction: Type.Union(\n\t\t[\n\t\t\tType.Literal(\"start\"),\n\t\t\tType.Literal(\"record-evidence\"),\n\t\t\tType.Literal(\"inspect\"),\n\t\t\tType.Literal(\"approve\"),\n\t\t\tType.Literal(\"reject\"),\n\t\t\tType.Literal(\"retreat\"),\n\t\t\tType.Literal(\"complete\"),\n\t\t\tType.Literal(\"skip\"),\n\t\t\tType.Literal(\"cancel\"),\n\t\t],\n\t\t{ description: \"Explicit action to perform\" },\n\t),\n\tstageId: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Stage id: the evidence/approve/reject/complete/skip target, or the retreat target stage to roll back to\",\n\t\t}),\n\t),\n\texecutor: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Advisory executor id (e.g. subagent session/task id) driving the stage; record-only, last-write-wins, never affects evaluation\",\n\t\t}),\n\t),\n\tevidence: Type.Optional(\n\t\tType.Array(\n\t\t\tType.Object({\n\t\t\t\tacceptanceId: Type.Optional(\n\t\t\t\t\tType.String({ description: \"Acceptance item id; required in acceptance mode\" }),\n\t\t\t\t),\n\t\t\t\tref: Type.String({ description: \"Evidence reference (tool output, file, artifact)\" }),\n\t\t\t}),\n\t\t\t{ description: \"Evidence items for record-evidence/approve/complete\" },\n\t\t),\n\t),\n\tnote: Type.Optional(\n\t\tType.String({ description: \"Optional note recorded in the transition detail (reject/retreat/skip/cancel)\" }),\n\t),\n});\n\nconst ADVANCE_ACTIONS: readonly string[] = [\n\t\"start\",\n\t\"record-evidence\",\n\t\"inspect\",\n\t\"approve\",\n\t\"reject\",\n\t\"retreat\",\n\t\"complete\",\n\t\"skip\",\n\t\"cancel\",\n];\n\nfunction objectInput(first: unknown, second: unknown): Record<string, unknown> {\n\tconst value = second === undefined ? first : second;\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\tthrow new Error(\"Workflow input must be an object\");\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction parseAdvanceInput(value: Record<string, unknown>): AdvanceInput {\n\tconst workflowId = asText(value.workflowId, \"workflowId\");\n\tconst action = value.action;\n\tif (typeof action !== \"string\" || !ADVANCE_ACTIONS.includes(action)) {\n\t\tthrow new Error(`action must be one of ${ADVANCE_ACTIONS.join(\", \")}`);\n\t}\n\tconst stageId = value.stageId === undefined ? undefined : asText(value.stageId, \"stageId\");\n\tconst note = value.note === undefined ? undefined : asText(value.note, \"note\");\n\tconst executor = value.executor === undefined ? undefined : asText(value.executor, \"executor\");\n\tlet evidence: EvidenceItem[] | undefined;\n\tif (value.evidence !== undefined) {\n\t\tif (!Array.isArray(value.evidence)) throw new Error(\"evidence must be an array of {acceptanceId?, ref}\");\n\t\tevidence = value.evidence.map((item) => {\n\t\t\tconst entry = asObject(item, \"evidence item\");\n\t\t\tconst ref = asText(entry.ref, \"evidence item ref\");\n\t\t\tconst acceptanceId =\n\t\t\t\tentry.acceptanceId === undefined ? undefined : asText(entry.acceptanceId, \"evidence item acceptanceId\");\n\t\t\treturn acceptanceId === undefined ? { ref } : { acceptanceId, ref };\n\t\t});\n\t}\n\treturn {\n\t\tworkflowId,\n\t\taction: action as AdvanceAction,\n\t\t...(stageId !== undefined ? { stageId } : {}),\n\t\t...(evidence !== undefined ? { evidence } : {}),\n\t\t...(note !== undefined ? { note } : {}),\n\t\t...(executor !== undefined ? { executor } : {}),\n\t};\n}\n\nfunction loadState(api: CapabilityAPI): WorkflowState {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tconst entry = [...entries].reverse().find((item) => item.customType === WORKFLOW_STATE_ENTRY);\n\tif (!entry?.data || typeof entry.data !== \"object\") return { workflows: [] };\n\tconst data = entry.data as Partial<WorkflowState>;\n\treturn { workflows: Array.isArray(data.workflows) ? [...data.workflows] : [] };\n}\n\n/**\n * Plan facts read from the current branch's LATEST `plan.state` snapshot (the\n * same latest-snapshot semantics as the plan plugin's own loadState). The\n * plan.changed payload is never trusted for state — only as a trigger.\n *\n * Uncached by design: identity-keyed caching collided across test hosts and\n * hooking invalidation into appendEntry crosses the persistence boundary; the\n * O(stages x entries) rescan is the price of snapshot-driven truth. Revisit\n * only with a per-run cache channel if long-session profiling demands it.\n */\nfunction loadPlansSnapshot(api: CapabilityAPI): Map<string, PlanFacts> {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tfor (let index = entries.length - 1; index >= 0; index -= 1) {\n\t\tconst entry = entries[index] as { customType?: string; data?: unknown } | undefined;\n\t\tif (entry?.customType !== planStateEntry) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return new Map();\n\t\tconst plans = (data as { plans?: unknown }).plans;\n\t\tif (!Array.isArray(plans)) return new Map();\n\t\tconst facts = new Map<string, PlanFacts>();\n\t\tfor (const plan of plans) {\n\t\t\tif (!plan || typeof plan !== \"object\") continue;\n\t\t\tconst record = plan as { id?: unknown; status?: unknown; evidence?: unknown };\n\t\t\tif (typeof record.id !== \"string\") continue;\n\t\t\tconst status =\n\t\t\t\trecord.status === \"draft\" ||\n\t\t\t\trecord.status === \"active\" ||\n\t\t\t\trecord.status === \"completed\" ||\n\t\t\t\trecord.status === \"cancelled\"\n\t\t\t\t\t? record.status\n\t\t\t\t\t: \"draft\";\n\t\t\tconst evidence = Array.isArray(record.evidence)\n\t\t\t\t? record.evidence.filter((item): item is string => typeof item === \"string\")\n\t\t\t\t: [];\n\t\t\tfacts.set(record.id, { id: record.id, status, evidence });\n\t\t}\n\t\treturn facts;\n\t}\n\treturn new Map();\n}\n\n/** Whether the latest `<entryType>` snapshot on the branch knows `id` in its `idsField` array (advisory, #10). */\nfunction idKnownToSession(api: CapabilityAPI, entryType: string, idsField: string, id: string): boolean {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tfor (let index = entries.length - 1; index >= 0; index -= 1) {\n\t\tconst entry = entries[index];\n\t\tif (entry.customType !== entryType) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return false;\n\t\tconst records = (data as Record<string, unknown>)[idsField];\n\t\tif (!Array.isArray(records)) return false;\n\t\treturn records.some(\n\t\t\t(record) => record !== null && typeof record === \"object\" && (record as { id?: unknown }).id === id,\n\t\t);\n\t}\n\treturn false;\n}\n\n/**\n * Seeds the held-dedup view (§5 总则 5) from the branch audit stream: the\n * last transition per stage, whatever its code — a repeat held emission is\n * dropped only when it matches that last code and detail.\n */\nfunction buildLastStageTransition(api: CapabilityAPI): Map<string, { code: TransitionReasonCode; detail?: string }> {\n\tconst map = new Map<string, { code: TransitionReasonCode; detail?: string }>();\n\tfor (const entry of api.session?.getBranchEntries() ?? []) {\n\t\tif (entry.customType !== WORKFLOW_TRANSITION_ENTRY) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) continue;\n\t\tconst record = data as Partial<WorkflowTransitionEntry>;\n\t\tif (typeof record.workflowId !== \"string\" || typeof record.stageId !== \"string\") continue;\n\t\tconst reason = record.reason as { code?: unknown; detail?: unknown } | undefined;\n\t\tif (!reason || typeof reason !== \"object\" || typeof reason.code !== \"string\") continue;\n\t\tmap.set(`${record.workflowId}::${record.stageId}`, {\n\t\t\tcode: reason.code as TransitionReasonCode,\n\t\t\tdetail: typeof reason.detail === \"string\" ? reason.detail : undefined,\n\t\t});\n\t}\n\treturn map;\n}\n\n/** Soft-reference warnings for the tool response only (never the persisted state, #10). */\nfunction collectSoftReferenceWarnings(\n\tapi: CapabilityAPI,\n\tgoalId: string | undefined,\n\tplanIds: { stageId: string; planId: string }[],\n\troles: { stageId: string; role: string }[],\n): string[] {\n\tconst warnings: string[] = [];\n\tif (goalId !== undefined && !idKnownToSession(api, goalStateEntry, \"goals\", goalId)) {\n\t\twarnings.push(\n\t\t\t`goalId \"${goalId}\" does not match any goal in this session's goal state; the workflow keeps the soft reference`,\n\t\t);\n\t}\n\tfor (const item of planIds) {\n\t\tif (!idKnownToSession(api, planStateEntry, \"plans\", item.planId)) {\n\t\t\twarnings.push(\n\t\t\t\t`stage ${item.stageId}: planId \"${item.planId}\" does not match any plan in this session's plan state; ` +\n\t\t\t\t\t\"the stage stays held until a reevaluation finds the plan\",\n\t\t\t);\n\t\t}\n\t}\n\tfor (const item of roles) {\n\t\t// The host's suite role associations are the only verifiable role face;\n\t\t// hosts without the listing leave the advice unverified (no warning).\n\t\tconst associated = api.host?.roleAssociations;\n\t\tif (associated !== undefined && !associated.includes(item.role)) {\n\t\t\twarnings.push(\n\t\t\t\t`stage ${item.stageId}: role \"${item.role}\" is not associated with the active suite; delegation advice treated as unspecified`,\n\t\t\t);\n\t\t}\n\t}\n\treturn warnings;\n}\n\ninterface WorkflowToolResponse extends WorkflowState {\n\tclosures?: WorkflowClosure[];\n\tinspections?: WorkflowInspection[];\n\twarning?: string;\n}\n\nexport function createCapability(\n\tapi: CapabilityAPI,\n): Promise<readonly DisposableRegistration[]> | readonly DisposableRegistration[] {\n\tlet state = loadState(api);\n\tconst deps: EngineDeps = {\n\t\tgetPlan: (planId) => loadPlansSnapshot(api).get(planId),\n\t\tlastStageTransition: buildLastStageTransition(api),\n\t};\n\t// Shared structural renderResult (render.ts): the host TUI gets the\n\t// panorama face for both tools' JSON-serialized responses.\n\tconst toolResultRenderer = createWorkflowToolResultRenderer(deps);\n\n\t// Persistence boundary (§4): audit entries first, then the full snapshot —\n\t// a crash mid-batch replays from the older snapshot while the audit stays\n\t// intact. appendEntry failures propagate unchanged (§5 总则 5: no swallow);\n\t// publish rejections surface to the tool caller (or are contained on the\n\t// load/subscriber paths below).\n\tconst persistRun = (run: EngineRun): void => {\n\t\tfor (const transition of run.transitions) {\n\t\t\tapi.session?.appendEntry(WORKFLOW_TRANSITION_ENTRY, transition);\n\t\t}\n\t\tapi.session?.appendEntry(WORKFLOW_STATE_ENTRY, run.state);\n\t};\n\tconst announce = (run: EngineRun): Promise<unknown> =>\n\t\tapi.publish({ type: \"workflow.changed\", version: 1, correlationId: WORKFLOW_STATE_ENTRY, data: run.state });\n\n\t/**\n\t * §5 总则 3 containment: land the failure in diagnostics + a handler.error\n\t * transition (detail = original error text) and never rethrow. Best-effort\n\t * — an already-failing boundary must not turn isolation into a second crash.\n\t */\n\tconst quarantineEngineError = (error: unknown, trigger: WorkflowTransitionTrigger): void => {\n\t\ttry {\n\t\t\tif (state.workflows.length === 0) return;\n\t\t\tconst original = error instanceof Error ? error.message : String(error);\n\t\t\tconst text = `${trigger} handler failed: ${original}`;\n\t\t\tconst workflows = state.workflows.map((workflow) => structuredClone(workflow));\n\t\t\tconst transitions: WorkflowTransitionEntry[] = [];\n\t\t\tfor (const workflow of workflows) {\n\t\t\t\tif (workflow.diagnostics.length >= DIAGNOSTICS_CAP) workflow.diagnostics.shift();\n\t\t\t\tworkflow.diagnostics.push(text);\n\t\t\t\tworkflow.revision += 1;\n\t\t\t\ttransitions.push({\n\t\t\t\t\tworkflowId: workflow.id,\n\t\t\t\t\tfrom: workflow.status,\n\t\t\t\t\tto: workflow.status,\n\t\t\t\t\ttrigger,\n\t\t\t\t\treason: { code: \"handler.error\", message: text, detail: original },\n\t\t\t\t\trevision: workflow.revision,\n\t\t\t\t});\n\t\t\t}\n\t\t\tstate = { workflows };\n\t\t\tfor (const transition of transitions) {\n\t\t\t\tapi.session?.appendEntry(WORKFLOW_TRANSITION_ENTRY, transition);\n\t\t\t}\n\t\t\tapi.session?.appendEntry(WORKFLOW_STATE_ENTRY, state);\n\t\t\tvoid api\n\t\t\t\t.publish({ type: \"workflow.changed\", version: 1, correlationId: WORKFLOW_STATE_ENTRY, data: state })\n\t\t\t\t.catch(() => undefined);\n\t\t} catch {\n\t\t\t// Containment is best-effort by design.\n\t\t}\n\t};\n\n\t// §5 rule 8 recovery: state replayed from workflow.state, then the\n\t// load-time reevaluation (总则 1 fallback moment #1). Runs synchronously at\n\t// factory time; failures are contained, never blocking plugin load.\n\ttry {\n\t\tconst run = reevaluateAll(state, deps, \"load-reeval\");\n\t\tif (run.transitions.length > 0) {\n\t\t\tstate = run.state;\n\t\t\tpersistRun(run);\n\t\t\tvoid announce(run).catch(() => undefined);\n\t\t}\n\t} catch (error) {\n\t\tquarantineEngineError(error, \"load-reeval\");\n\t}\n\n\tconst setTool = api.registerTool({\n\t\tname: \"workflow_set\",\n\t\tlabel: \"Set workflow\",\n\t\tpromptGuidelines: [\n\t\t\tWORKFLOW_APPLICABILITY_GUIDE,\n\t\t\tWORKFLOW_AUTHORING_GUIDE,\n\t\t\tWORKFLOW_EXECUTION_GUIDE,\n\t\t\tWORKFLOW_CORRECTION_GUIDE,\n\t\t\tWORKFLOW_REVIEW_GUIDE,\n\t\t\tWORKFLOW_CONCURRENCY_GUIDE,\n\t\t\tWORKFLOW_INCREMENTAL_DEMAND_GUIDE,\n\t\t\tWORKFLOW_EXECUTOR_GUIDE,\n\t\t],\n\t\tdescription:\n\t\t\t\"Create or update a workflow: a stage dependency graph (DAG) with per-stage objectives, acceptance evidence, and soft goal/plan references. On update, an omitted workflow-level optional field (title/goalId/maxRetreats/maxActiveStages) keeps its stored value; stages are always submitted in full\",\n\t\tparameters: workflowSetSchema,\n\t\trenderResult: toolResultRenderer,\n\t\texecute: async (first: unknown, second?: unknown) => {\n\t\t\tconst parsed = parseWorkflowSetInput(objectInput(first, second));\n\t\t\tvalidateStageGraph(parsed.stages);\n\t\t\tconst current = state.workflows.find((workflow) => workflow.id === parsed.id);\n\t\t\tif (current !== undefined && parsed.revision <= current.revision) {\n\t\t\t\tthrow new Error(`stale workflow revision: ${parsed.id}`);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tcurrent !== undefined &&\n\t\t\t\t(current.status === \"completed\" || current.status === \"failed\" || current.status === \"cancelled\")\n\t\t\t) {\n\t\t\t\tthrow new Error(`workflow is terminal and cannot be updated: ${parsed.id}`);\n\t\t\t}\n\t\t\tconst diff = planWorkflowSpecUpdate(current, parsed);\n\t\t\tconst run = applyWorkflowSet(state, parsed, diff, deps);\n\t\t\tstate = run.state;\n\t\t\tif (run.transitions.length > 0 || diff.created) persistRun(run);\n\t\t\tif (run.transitions.length > 0 || diff.created) await announce(run);\n\t\t\tconst response: WorkflowToolResponse = { ...state };\n\t\t\tif (run.closures.length > 0) response.closures = run.closures;\n\t\t\tconst warnings = collectSoftReferenceWarnings(\n\t\t\t\tapi,\n\t\t\t\tparsed.goalId,\n\t\t\t\tparsed.stages.flatMap((stage) =>\n\t\t\t\t\tstage.planId !== undefined ? [{ stageId: stage.id, planId: stage.planId }] : [],\n\t\t\t\t),\n\t\t\t\tparsed.stages.flatMap((stage) =>\n\t\t\t\t\tstage.role !== undefined ? [{ stageId: stage.id, role: stage.role }] : [],\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (warnings.length > 0) response.warning = warnings.join(\"; \");\n\t\t\treturn response;\n\t\t},\n\t});\n\n\tconst advanceTool = api.registerTool({\n\t\tname: \"workflow_advance\",\n\t\tlabel: \"Advance workflow\",\n\t\tdescription:\n\t\t\t\"Drive a workflow: start it, record acceptance evidence, inspect waiting conditions and coverage, approve/reject reviews, retreat, complete checkpoints, skip stages, or cancel\",\n\t\tparameters: workflowAdvanceSchema,\n\t\trenderResult: toolResultRenderer,\n\t\texecute: async (first: unknown, second?: unknown) => {\n\t\t\tconst parsed = parseAdvanceInput(objectInput(first, second));\n\t\t\t// Illegal conversions throw structured errors (§5 rule 6) surfaced\n\t\t\t// as tool errors unchanged; legal ones return the full snapshot\n\t\t\t// (the goal_set full-state response pattern).\n\t\t\tconst run = applyAdvance(state, parsed, deps);\n\t\t\tstate = run.state;\n\t\t\tif (run.transitions.length > 0) {\n\t\t\t\tpersistRun(run);\n\t\t\t\tawait announce(run);\n\t\t\t}\n\t\t\tconst response: WorkflowToolResponse = { ...state };\n\t\t\tif (parsed.action === \"inspect\") {\n\t\t\t\t// §5 rule 6 single-workflow semantics: applyAdvance has already\n\t\t\t\t// rejected unknown ids, so the target always resolves here.\n\t\t\t\tconst target = state.workflows.find((workflow) => workflow.id === parsed.workflowId);\n\t\t\t\tif (target !== undefined) response.inspections = [inspectWorkflow(target, deps)];\n\t\t\t}\n\t\t\tif (run.closures.length > 0) response.closures = run.closures;\n\t\t\treturn response;\n\t\t},\n\t});\n\n\tconst workflowCommand = api.registerCommand({\n\t\tname: \"workflow\",\n\t\tdescription: \"Workflow overview: dependency tree and status (default), why it is stopped, or the transition log\",\n\t\targumentHint: \"[view | why | log [N]]\",\n\t\texecute: async (args: string) => {\n\t\t\ttry {\n\t\t\t\tconst trimmed = args.trim();\n\t\t\t\tif (trimmed === \"\" || trimmed === \"view\") {\n\t\t\t\t\treturn { status: \"success\" as const, message: renderPanorama(state, deps) };\n\t\t\t\t}\n\t\t\t\tif (trimmed === \"why\") {\n\t\t\t\t\treturn { status: \"success\" as const, message: renderWhy(state, deps) };\n\t\t\t\t}\n\t\t\t\tconst logMatch = /^log(?:\\s+(\\d+))?$/.exec(trimmed);\n\t\t\t\tif (logMatch !== null) {\n\t\t\t\t\tconst limit = logMatch[1] === undefined ? 20 : Math.max(1, Number.parseInt(logMatch[1], 10));\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: \"success\" as const,\n\t\t\t\t\t\tmessage: renderLog(api.session?.getBranchEntries() ?? [], limit),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn { status: \"error\" as const, message: \"Usage: /workflow [view | why | log [N]]\" };\n\t\t\t} catch (error) {\n\t\t\t\t// A slash command must not crash the host (memory /promote precedent).\n\t\t\t\treturn { status: \"error\" as const, message: error instanceof Error ? error.message : String(error) };\n\t\t\t}\n\t\t},\n\t});\n\n\t// The repository's first event subscription (§7). The payload is ignored:\n\t// reevaluation always reads the current branch entries (§5 总则 1).\n\tconst planChanged = api.subscribe(\"plan.changed\", async () => {\n\t\ttry {\n\t\t\tconst run = reevaluateAll(state, deps, \"plan.changed\");\n\t\t\tif (run.transitions.length === 0) return;\n\t\t\tstate = run.state;\n\t\t\tpersistRun(run);\n\t\t\tawait announce(run);\n\t\t} catch (error) {\n\t\t\tquarantineEngineError(error, \"plan.changed\");\n\t\t}\n\t});\n\n\treturn [setTool, advanceTool, workflowCommand, planChanged];\n}\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AA2BrF,YAAY,EACX,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,SAAS,EACT,YAAY,EACZ,SAAS,EACT,eAAe,EACf,eAAe,EACf,kBAAkB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/E,YAAY,EACX,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,uBAAuB,EACvB,yBAAyB,GACzB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAmZ7E,wBAAgB,gBAAgB,CAC/B,GAAG,EAAE,aAAa,GAChB,OAAO,CAAC,SAAS,sBAAsB,EAAE,CAAC,GAAG,SAAS,sBAAsB,EAAE,CA0MhF","sourcesContent":["/**\n * Workflow orchestration capability plugin (M1 of\n * `docs/design/工作流编排插件设计.md`): a fourth self-contained state machine\n * orchestrating a stage dependency graph on top of the goal/plan/todo layers.\n *\n * Persistence protocol, structurally identical to the goal/plan/todo plugins:\n * append-only `workflow.state` full snapshots (the single rebuild source) plus\n * one `workflow.transition` audit entry per conversion, announced as\n * `workflow.changed` (version 1, correlationId \"workflow.state\", data = the\n * complete WorkflowState snapshot).\n *\n * This plugin is the repository's FIRST event subscriber (§7): `plan.changed`\n * is used as a trigger signal only — its in-memory data may be misaligned\n * with the current branch, so every reevaluation reads the plan facts from\n * the current branch entries. The handler is fully isolated (§5 总则 3): any\n * failure lands in diagnostics + a `handler.error` transition and never\n * rethrows at the publisher (an unisolated throw would pierce plan_set's tool\n * return). Soft references (goalId / planId / role) never fail a write (#10):\n * they surface as response warnings; a dangling planId additionally holds the\n * stage's activation until a reevaluation finds the plan.\n */\n\nimport type { CapabilityAPI, DisposableRegistration } from \"@agent-forge/plugin-sdk\";\nimport { Type } from \"typebox\";\nimport {\n\ttype AdvanceAction,\n\ttype AdvanceInput,\n\tapplyAdvance,\n\tapplyWorkflowSet,\n\ttype EngineDeps,\n\ttype EngineRun,\n\ttype EvidenceItem,\n\tinspectWorkflow,\n\ttype PlanFacts,\n\treevaluateAll,\n\ttype WorkflowClosure,\n\ttype WorkflowInspection,\n} from \"./engine.ts\";\nimport { createWorkflowToolResultRenderer, renderLog, renderPanorama, renderWhy } from \"./render.ts\";\nimport {\n\ttype TransitionReasonCode,\n\tWORKFLOW_STATE_ENTRY,\n\tWORKFLOW_TRANSITION_ENTRY,\n\ttype WorkflowState,\n\ttype WorkflowTransitionEntry,\n\ttype WorkflowTransitionTrigger,\n} from \"./types.ts\";\nimport { asObject, asText, parseWorkflowSetInput, planWorkflowSpecUpdate, validateStageGraph } from \"./validate.ts\";\n\nexport type {\n\tAcceptanceCoverage,\n\tAdvanceAction,\n\tAdvanceInput,\n\tClosureEvidenceItem,\n\tEngineDeps,\n\tEngineRun,\n\tEvidenceItem,\n\tPlanFacts,\n\tStageInspection,\n\tWorkflowClosure,\n\tWorkflowInspection,\n} from \"./engine.ts\";\nexport { cascadeLimitFor, computeWaitingOn, reevaluateAll } from \"./engine.ts\";\nexport type {\n\tStageRuntime,\n\tStageSpec,\n\tTransitionReason,\n\tTransitionReasonCode,\n\tWorkflowRecord,\n\tWorkflowState,\n\tWorkflowTransitionEntry,\n\tWorkflowTransitionTrigger,\n} from \"./types.ts\";\nexport { WORKFLOW_STATE_ENTRY, WORKFLOW_TRANSITION_ENTRY } from \"./types.ts\";\n\n/** Session protocol key of the plan plugin's snapshot entry (plugins/plan). */\nconst planStateEntry = \"plan.state\";\n/** Session protocol key of the goal plugin's snapshot entry (plugins/goal). */\nconst goalStateEntry = \"goal.state\";\n/**\n * Session protocol key of the subagent-delegate plugin's session role snapshot\n * (plugins/subagent-delegate, D-081 item 7: `{ version: 1, roles: [{ id, ... }] }`\n * append-only full snapshots). Same literal-coupling precedent as plan/goal\n * above: the workflow plugin never imports the owning plugin.\n */\nconst subagentRoleStateEntry = \"subagent.roles.state\";\n/**\n * Role id shape: the same expression as the authoritative reader's\n * ROLE_ID_PATTERN (plugins/subagent-delegate `readSessionRoles`). Same\n * cross-plugin literal-coupling precedent as the entry keys above — the\n * workflow plugin never imports the owning plugin. The residual difference is\n * field-level only: subagent-delegate validates the whole role record and drops\n * invalid entries, while this side reads the id alone — so the divergence can\n * only under-warn, never false-warn.\n */\nconst roleIdPattern = /^[a-z0-9][a-z0-9_-]{0,31}$/;\nconst DIAGNOSTICS_CAP = 50;\n\n/** §3.1 applicability, verbatim scope (when to use / not to use a workflow). */\nconst WORKFLOW_APPLICABILITY_GUIDE =\n\t\"Workflow applicability — use a workflow only when ALL of these hold: (1) the goal needs at least 3 stages that can be accepted independently (you can write each stage's objective/acceptance); (2) the stages have a real dependency structure (branching or joins), not a plain sequence; (3) independent branches can be pushed in parallel — parallelism is a core payoff, purely linear multi-stage work belongs to goal/plan/todo; (4) the task spans multiple turns/days and is valuable enough to justify process overhead (definition, evidence recording, stage gates). Counterexamples that keep goal/plan/todo or direct execution: single-step tasks; two- or three-step linear tasks; exploratory work with no presettable dependency graph; one-shot questions. A workflow does not exclude the three layers — each stage still uses plan/todo for its execution checklist. Never escalate a small task to a workflow by default. When the user explicitly asks for workflow orchestration, still run this weighing: if the task genuinely misses the bar, skipping is fine — but say so in one line why you stayed on goal/plan/todo (an unexplained skip reads as ignoring the request); conversely, do not build one just because it was asked when the task is small (over-orchestration costs more than it saves). (2026-10-04 user ruling after a dogfood where an explicitly requested orchestration was silently skipped.)\";\n\nconst WORKFLOW_AUTHORING_GUIDE =\n\t\"Define each stage by a verifiable end state, not by a work list or a role: objective.statement says what must be achieved; acceptance items list the evidence that proves it, referenced from real tool output. Stop splitting at the first verifiable boundary — finer handoffs lose more to interface drift than they gain in drift protection.\";\n\nconst WORKFLOW_EXECUTION_GUIDE =\n\t\"After plan_set marks a stage's plan completed, run workflow_advance inspect to see unlocked stages and acceptance coverage. Record evidence (workflow_advance record-evidence) before completion is evaluated; lower-bound stages (no acceptance declared) need no record-evidence. When multiple stages are active, delegate one subagent per stage following stage.role and push them in parallel.\";\n\nconst WORKFLOW_CORRECTION_GUIDE =\n\t\"On user corrections: run workflow_advance inspect first to confirm the current state, then route by what changed — amend the stage spec (active/awaiting_review stages, amendNote required), append a stage, skip, retreat, or cancel — and report the adjustment and its impact range back to the user.\";\n\nconst WORKFLOW_REVIEW_GUIDE =\n\t\"Stages declared advance: 'review' stop at awaiting_review. Before approve you must present the result and its evidence to the user and get explicit confirmation — never approve on your own (the engine does not authorize; the discipline lives here).\";\n\nconst WORKFLOW_CONCURRENCY_GUIDE =\n\t\"Do not send workflow_set in the same parallel batch as plan_set: amends and completion evaluation race (completed freezes first; if the amend loses, recover via retreat).\";\n\nconst WORKFLOW_INCREMENTAL_DEMAND_GUIDE =\n\t\"When a new batch of requirements arrives (incremental features on top of shipped work), default to creating a NEW workflow to carry it — reuse the same goalId and keep the completed workflow's history intact; never pull a completed workflow back into motion. Retreat or append a corrective stage only when the new requirements overturn already completed output.\";\n\nconst WORKFLOW_EXECUTOR_GUIDE =\n\t\"When delegating a stage to a subagent, pass that subagent's session/task id as the executor field of record-evidence — it is recorded on the stage (advisory) so /workflow log shows who drove it.\";\n\nconst acceptanceSchema = Type.Object({\n\tid: Type.String({ description: \"Acceptance item id; evidence references this id\" }),\n\tstatement: Type.String({ description: \"Verifiable completion criterion for this item\" }),\n});\n\nconst stageSchema = Type.Object({\n\tid: Type.String({ description: \"Stable stage id, unique within the workflow\" }),\n\ttitle: Type.String({ description: \"Human-readable stage title\" }),\n\tobjective: Type.Object({\n\t\tstatement: Type.String({\n\t\t\tdescription: \"Verifiable end state this stage must reach (required, non-empty)\",\n\t\t}),\n\t\tacceptance: Type.Optional(\n\t\t\tType.Array(acceptanceSchema, {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Completion criteria items; when empty (or omitted) the lower bound is the referenced plan's completion evidence\",\n\t\t\t}),\n\t\t),\n\t}),\n\tdependsOn: Type.Optional(\n\t\tType.Array(Type.String(), { description: \"Hard prerequisite stage ids; empty array = root stage\" }),\n\t),\n\tplanId: Type.Optional(Type.String({ description: \"Soft reference to a plan id; missing = pure checkpoint stage\" })),\n\tkind: Type.Optional(\n\t\tType.Union([Type.Literal(\"read\"), Type.Literal(\"write\")], {\n\t\t\tdescription: 'Stage kind; write stages are scope-checked, default \"write\"',\n\t\t}),\n\t),\n\tscope: Type.Optional(\n\t\tType.Array(Type.String(), {\n\t\t\tdescription: \"Write scope tags (modules/dirs); missing = global (intersects every other write stage)\",\n\t\t}),\n\t),\n\trole: Type.Optional(\n\t\tType.String({ description: \"Delegation advice: role id for the subagent recommended to drive this stage\" }),\n\t),\n\tadvance: Type.Optional(\n\t\tType.Union([Type.Literal(\"auto\"), Type.Literal(\"review\")], {\n\t\t\tdescription:\n\t\t\t\t'\"auto\" completes on evaluation, \"review\" stops at awaiting_review (planId stages only), default \"auto\"',\n\t\t}),\n\t),\n\tretryLimit: Type.Optional(\n\t\tType.Integer({ description: \"In-place retry limit before onFail/workflow failure, default 2\", minimum: 0 }),\n\t),\n\tonFail: Type.Optional(\n\t\tType.String({ description: \"Explicit cross-stage retreat target (a topologically earlier stage id)\" }),\n\t),\n\tamendNote: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Correction reason; required when an active/awaiting_review stage's spec changes\",\n\t\t}),\n\t),\n});\n\nconst workflowSetSchema = Type.Object({\n\tid: Type.String({ description: \"Stable id of the workflow to create or update\" }),\n\ttitle: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Human-readable workflow title; required on create, omitting on update keeps the stored value\",\n\t\t}),\n\t),\n\trevision: Type.Integer({\n\t\tdescription: \"New revision counter; must be greater than the stored revision of an existing workflow\",\n\t\tminimum: 0,\n\t}),\n\tgoalId: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Soft reference to a goal id; the closure evidence summary is addressed to this goal. Omitting on update keeps the stored value\",\n\t\t}),\n\t),\n\tmaxRetreats: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription:\n\t\t\t\t\"Workflow lifetime retreat budget before failure, default 3. Omitting on update keeps the stored value\",\n\t\t\tminimum: 0,\n\t\t}),\n\t),\n\tmaxActiveStages: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Parallel activation cap, default 4. Omitting on update keeps the stored value\",\n\t\t\tminimum: 1,\n\t\t}),\n\t),\n\tstages: Type.Array(stageSchema, {\n\t\tdescription: \"Full stage list (whole submit, not a patch); the engine aligns runtime by stage id\",\n\t}),\n});\n\nconst workflowAdvanceSchema = Type.Object({\n\tworkflowId: Type.String({ description: \"Target workflow id\" }),\n\taction: Type.Union(\n\t\t[\n\t\t\tType.Literal(\"start\"),\n\t\t\tType.Literal(\"record-evidence\"),\n\t\t\tType.Literal(\"inspect\"),\n\t\t\tType.Literal(\"approve\"),\n\t\t\tType.Literal(\"reject\"),\n\t\t\tType.Literal(\"retreat\"),\n\t\t\tType.Literal(\"complete\"),\n\t\t\tType.Literal(\"skip\"),\n\t\t\tType.Literal(\"cancel\"),\n\t\t],\n\t\t{ description: \"Explicit action to perform\" },\n\t),\n\tstageId: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Stage id: the evidence/approve/reject/complete/skip target, or the retreat target stage to roll back to\",\n\t\t}),\n\t),\n\texecutor: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Advisory executor id (e.g. subagent session/task id) driving the stage; record-only, last-write-wins, never affects evaluation\",\n\t\t}),\n\t),\n\tevidence: Type.Optional(\n\t\tType.Array(\n\t\t\tType.Object({\n\t\t\t\tacceptanceId: Type.Optional(\n\t\t\t\t\tType.String({ description: \"Acceptance item id; required in acceptance mode\" }),\n\t\t\t\t),\n\t\t\t\tref: Type.String({ description: \"Evidence reference (tool output, file, artifact)\" }),\n\t\t\t}),\n\t\t\t{ description: \"Evidence items for record-evidence/approve/complete\" },\n\t\t),\n\t),\n\tnote: Type.Optional(\n\t\tType.String({ description: \"Optional note recorded in the transition detail (reject/retreat/skip/cancel)\" }),\n\t),\n});\n\nconst ADVANCE_ACTIONS: readonly string[] = [\n\t\"start\",\n\t\"record-evidence\",\n\t\"inspect\",\n\t\"approve\",\n\t\"reject\",\n\t\"retreat\",\n\t\"complete\",\n\t\"skip\",\n\t\"cancel\",\n];\n\nfunction objectInput(first: unknown, second: unknown): Record<string, unknown> {\n\tconst value = second === undefined ? first : second;\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\tthrow new Error(\"Workflow input must be an object\");\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction parseAdvanceInput(value: Record<string, unknown>): AdvanceInput {\n\tconst workflowId = asText(value.workflowId, \"workflowId\");\n\tconst action = value.action;\n\tif (typeof action !== \"string\" || !ADVANCE_ACTIONS.includes(action)) {\n\t\tthrow new Error(`action must be one of ${ADVANCE_ACTIONS.join(\", \")}`);\n\t}\n\tconst stageId = value.stageId === undefined ? undefined : asText(value.stageId, \"stageId\");\n\tconst note = value.note === undefined ? undefined : asText(value.note, \"note\");\n\tconst executor = value.executor === undefined ? undefined : asText(value.executor, \"executor\");\n\tlet evidence: EvidenceItem[] | undefined;\n\tif (value.evidence !== undefined) {\n\t\tif (!Array.isArray(value.evidence)) throw new Error(\"evidence must be an array of {acceptanceId?, ref}\");\n\t\tevidence = value.evidence.map((item) => {\n\t\t\tconst entry = asObject(item, \"evidence item\");\n\t\t\tconst ref = asText(entry.ref, \"evidence item ref\");\n\t\t\tconst acceptanceId =\n\t\t\t\tentry.acceptanceId === undefined ? undefined : asText(entry.acceptanceId, \"evidence item acceptanceId\");\n\t\t\treturn acceptanceId === undefined ? { ref } : { acceptanceId, ref };\n\t\t});\n\t}\n\treturn {\n\t\tworkflowId,\n\t\taction: action as AdvanceAction,\n\t\t...(stageId !== undefined ? { stageId } : {}),\n\t\t...(evidence !== undefined ? { evidence } : {}),\n\t\t...(note !== undefined ? { note } : {}),\n\t\t...(executor !== undefined ? { executor } : {}),\n\t};\n}\n\nfunction loadState(api: CapabilityAPI): WorkflowState {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tconst entry = [...entries].reverse().find((item) => item.customType === WORKFLOW_STATE_ENTRY);\n\tif (!entry?.data || typeof entry.data !== \"object\") return { workflows: [] };\n\tconst data = entry.data as Partial<WorkflowState>;\n\treturn { workflows: Array.isArray(data.workflows) ? [...data.workflows] : [] };\n}\n\n/**\n * Plan facts read from the current branch's LATEST `plan.state` snapshot (the\n * same latest-snapshot semantics as the plan plugin's own loadState). The\n * plan.changed payload is never trusted for state — only as a trigger.\n *\n * Uncached by design: identity-keyed caching collided across test hosts and\n * hooking invalidation into appendEntry crosses the persistence boundary; the\n * O(stages x entries) rescan is the price of snapshot-driven truth. Revisit\n * only with a per-run cache channel if long-session profiling demands it.\n */\nfunction loadPlansSnapshot(api: CapabilityAPI): Map<string, PlanFacts> {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tfor (let index = entries.length - 1; index >= 0; index -= 1) {\n\t\tconst entry = entries[index] as { customType?: string; data?: unknown } | undefined;\n\t\tif (entry?.customType !== planStateEntry) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return new Map();\n\t\tconst plans = (data as { plans?: unknown }).plans;\n\t\tif (!Array.isArray(plans)) return new Map();\n\t\tconst facts = new Map<string, PlanFacts>();\n\t\tfor (const plan of plans) {\n\t\t\tif (!plan || typeof plan !== \"object\") continue;\n\t\t\tconst record = plan as { id?: unknown; status?: unknown; evidence?: unknown };\n\t\t\tif (typeof record.id !== \"string\") continue;\n\t\t\tconst status =\n\t\t\t\trecord.status === \"draft\" ||\n\t\t\t\trecord.status === \"active\" ||\n\t\t\t\trecord.status === \"completed\" ||\n\t\t\t\trecord.status === \"cancelled\"\n\t\t\t\t\t? record.status\n\t\t\t\t\t: \"draft\";\n\t\t\tconst evidence = Array.isArray(record.evidence)\n\t\t\t\t? record.evidence.filter((item): item is string => typeof item === \"string\")\n\t\t\t\t: [];\n\t\t\tfacts.set(record.id, { id: record.id, status, evidence });\n\t\t}\n\t\treturn facts;\n\t}\n\treturn new Map();\n}\n\n/** Whether the latest `<entryType>` snapshot on the branch knows `id` in its `idsField` array (advisory, #10). */\nfunction idKnownToSession(api: CapabilityAPI, entryType: string, idsField: string, id: string): boolean {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tfor (let index = entries.length - 1; index >= 0; index -= 1) {\n\t\tconst entry = entries[index];\n\t\tif (entry.customType !== entryType) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return false;\n\t\tconst records = (data as Record<string, unknown>)[idsField];\n\t\tif (!Array.isArray(records)) return false;\n\t\treturn records.some(\n\t\t\t(record) => record !== null && typeof record === \"object\" && (record as { id?: unknown }).id === id,\n\t\t);\n\t}\n\treturn false;\n}\n\n/**\n * Seeds the held-dedup view (§5 总则 5) from the branch audit stream: the\n * last transition per stage, whatever its code — a repeat held emission is\n * dropped only when it matches that last code and detail.\n */\nfunction buildLastStageTransition(api: CapabilityAPI): Map<string, { code: TransitionReasonCode; detail?: string }> {\n\tconst map = new Map<string, { code: TransitionReasonCode; detail?: string }>();\n\tfor (const entry of api.session?.getBranchEntries() ?? []) {\n\t\tif (entry.customType !== WORKFLOW_TRANSITION_ENTRY) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) continue;\n\t\tconst record = data as Partial<WorkflowTransitionEntry>;\n\t\tif (typeof record.workflowId !== \"string\" || typeof record.stageId !== \"string\") continue;\n\t\tconst reason = record.reason as { code?: unknown; detail?: unknown } | undefined;\n\t\tif (!reason || typeof reason !== \"object\" || typeof reason.code !== \"string\") continue;\n\t\tmap.set(`${record.workflowId}::${record.stageId}`, {\n\t\t\tcode: reason.code as TransitionReasonCode,\n\t\t\tdetail: typeof reason.detail === \"string\" ? reason.detail : undefined,\n\t\t});\n\t}\n\treturn map;\n}\n\n/**\n * Role ids from the current branch's latest READABLE `subagent.roles.state`\n * snapshot (D-081 item 7). Fallback is symmetric with the authoritative reader\n * (subagent-delegate `readSessionRoles`): a snapshot-level corruption (data not\n * an object / version mismatch / roles not an array) steps back to the previous\n * readable snapshot, and only a fully unreadable table degrades to the empty\n * set. Individual malformed role records are skipped — including ids that do\n * not match `roleIdPattern` — keeping the remaining ids of an otherwise\n * readable snapshot. Silent by contract: the\n * subagent-delegate plugin owns the degradation warning on its side, and a\n * second report here would double-warn for the same fact — this side only\n * tolerates.\n */\nfunction loadSessionRoleIds(api: CapabilityAPI): Set<string> {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tfor (let index = entries.length - 1; index >= 0; index -= 1) {\n\t\tconst entry = entries[index];\n\t\tif (entry.customType !== subagentRoleStateEntry) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) continue;\n\t\tconst snapshot = data as { version?: unknown; roles?: unknown };\n\t\tif (snapshot.version !== 1 || !Array.isArray(snapshot.roles)) continue;\n\t\tconst known = new Set<string>();\n\t\tfor (const role of snapshot.roles) {\n\t\t\tif (!role || typeof role !== \"object\" || Array.isArray(role)) continue;\n\t\t\tconst id = (role as { id?: unknown }).id;\n\t\t\tif (typeof id === \"string\" && roleIdPattern.test(id)) known.add(id);\n\t\t}\n\t\treturn known;\n\t}\n\treturn new Set();\n}\n\n/** Soft-reference warnings for the tool response only (never the persisted state, #10). */\nfunction collectSoftReferenceWarnings(\n\tapi: CapabilityAPI,\n\tgoalId: string | undefined,\n\tplanIds: { stageId: string; planId: string }[],\n\troles: { stageId: string; role: string }[],\n): string[] {\n\tconst warnings: string[] = [];\n\tif (goalId !== undefined && !idKnownToSession(api, goalStateEntry, \"goals\", goalId)) {\n\t\twarnings.push(\n\t\t\t`goalId \"${goalId}\" does not match any goal in this session's goal state; the workflow keeps the soft reference`,\n\t\t);\n\t}\n\tfor (const item of planIds) {\n\t\tif (!idKnownToSession(api, planStateEntry, \"plans\", item.planId)) {\n\t\t\twarnings.push(\n\t\t\t\t`stage ${item.stageId}: planId \"${item.planId}\" does not match any plan in this session's plan state; ` +\n\t\t\t\t\t\"the stage stays held until a reevaluation finds the plan\",\n\t\t\t);\n\t\t}\n\t}\n\t// Two verifiable role sources (D-081 item 7): the host's suite role\n\t// associations (disk roles) plus this branch's session role table. A host\n\t// without the listing AND an empty session table verifies nothing, so the\n\t// advice stays unverified — no warning (缺面不告警). An explicitly empty\n\t// association face ([]) still warns for every role.\n\tconst associated = api.host?.roleAssociations;\n\tconst sessionRoleIds = roles.length > 0 ? loadSessionRoleIds(api) : new Set<string>();\n\tconst merged = new Set([...(associated ?? []), ...sessionRoleIds]);\n\tfor (const item of roles) {\n\t\tif ((associated !== undefined || sessionRoleIds.size > 0) && !merged.has(item.role)) {\n\t\t\twarnings.push(\n\t\t\t\t`stage ${item.stageId}: role \"${item.role}\" is neither associated with the active suite nor ` +\n\t\t\t\t\t\"registered in this session; delegation advice treated as unspecified — register it with \" +\n\t\t\t\t\t\"subagent_role_set or add it to the suite's roles associations\",\n\t\t\t);\n\t\t}\n\t}\n\treturn warnings;\n}\n\ninterface WorkflowToolResponse extends WorkflowState {\n\tclosures?: WorkflowClosure[];\n\tinspections?: WorkflowInspection[];\n\twarning?: string;\n}\n\nexport function createCapability(\n\tapi: CapabilityAPI,\n): Promise<readonly DisposableRegistration[]> | readonly DisposableRegistration[] {\n\tlet state = loadState(api);\n\tconst deps: EngineDeps = {\n\t\tgetPlan: (planId) => loadPlansSnapshot(api).get(planId),\n\t\tlastStageTransition: buildLastStageTransition(api),\n\t};\n\t// Shared structural renderResult (render.ts): the host TUI gets the\n\t// panorama face for both tools' JSON-serialized responses.\n\tconst toolResultRenderer = createWorkflowToolResultRenderer(deps);\n\n\t// Persistence boundary (§4): audit entries first, then the full snapshot —\n\t// a crash mid-batch replays from the older snapshot while the audit stays\n\t// intact. appendEntry failures propagate unchanged (§5 总则 5: no swallow);\n\t// publish rejections surface to the tool caller (or are contained on the\n\t// load/subscriber paths below).\n\tconst persistRun = (run: EngineRun): void => {\n\t\tfor (const transition of run.transitions) {\n\t\t\tapi.session?.appendEntry(WORKFLOW_TRANSITION_ENTRY, transition);\n\t\t}\n\t\tapi.session?.appendEntry(WORKFLOW_STATE_ENTRY, run.state);\n\t};\n\tconst announce = (run: EngineRun): Promise<unknown> =>\n\t\tapi.publish({ type: \"workflow.changed\", version: 1, correlationId: WORKFLOW_STATE_ENTRY, data: run.state });\n\n\t/**\n\t * §5 总则 3 containment: land the failure in diagnostics + a handler.error\n\t * transition (detail = original error text) and never rethrow. Best-effort\n\t * — an already-failing boundary must not turn isolation into a second crash.\n\t */\n\tconst quarantineEngineError = (error: unknown, trigger: WorkflowTransitionTrigger): void => {\n\t\ttry {\n\t\t\tif (state.workflows.length === 0) return;\n\t\t\tconst original = error instanceof Error ? error.message : String(error);\n\t\t\tconst text = `${trigger} handler failed: ${original}`;\n\t\t\tconst workflows = state.workflows.map((workflow) => structuredClone(workflow));\n\t\t\tconst transitions: WorkflowTransitionEntry[] = [];\n\t\t\tfor (const workflow of workflows) {\n\t\t\t\tif (workflow.diagnostics.length >= DIAGNOSTICS_CAP) workflow.diagnostics.shift();\n\t\t\t\tworkflow.diagnostics.push(text);\n\t\t\t\tworkflow.revision += 1;\n\t\t\t\ttransitions.push({\n\t\t\t\t\tworkflowId: workflow.id,\n\t\t\t\t\tfrom: workflow.status,\n\t\t\t\t\tto: workflow.status,\n\t\t\t\t\ttrigger,\n\t\t\t\t\treason: { code: \"handler.error\", message: text, detail: original },\n\t\t\t\t\trevision: workflow.revision,\n\t\t\t\t});\n\t\t\t}\n\t\t\tstate = { workflows };\n\t\t\tfor (const transition of transitions) {\n\t\t\t\tapi.session?.appendEntry(WORKFLOW_TRANSITION_ENTRY, transition);\n\t\t\t}\n\t\t\tapi.session?.appendEntry(WORKFLOW_STATE_ENTRY, state);\n\t\t\tvoid api\n\t\t\t\t.publish({ type: \"workflow.changed\", version: 1, correlationId: WORKFLOW_STATE_ENTRY, data: state })\n\t\t\t\t.catch(() => undefined);\n\t\t} catch {\n\t\t\t// Containment is best-effort by design.\n\t\t}\n\t};\n\n\t// §5 rule 8 recovery: state replayed from workflow.state, then the\n\t// load-time reevaluation (总则 1 fallback moment #1). Runs synchronously at\n\t// factory time; failures are contained, never blocking plugin load.\n\ttry {\n\t\tconst run = reevaluateAll(state, deps, \"load-reeval\");\n\t\tif (run.transitions.length > 0) {\n\t\t\tstate = run.state;\n\t\t\tpersistRun(run);\n\t\t\tvoid announce(run).catch(() => undefined);\n\t\t}\n\t} catch (error) {\n\t\tquarantineEngineError(error, \"load-reeval\");\n\t}\n\n\tconst setTool = api.registerTool({\n\t\tname: \"workflow_set\",\n\t\tlabel: \"Set workflow\",\n\t\tpromptGuidelines: [\n\t\t\tWORKFLOW_APPLICABILITY_GUIDE,\n\t\t\tWORKFLOW_AUTHORING_GUIDE,\n\t\t\tWORKFLOW_EXECUTION_GUIDE,\n\t\t\tWORKFLOW_CORRECTION_GUIDE,\n\t\t\tWORKFLOW_REVIEW_GUIDE,\n\t\t\tWORKFLOW_CONCURRENCY_GUIDE,\n\t\t\tWORKFLOW_INCREMENTAL_DEMAND_GUIDE,\n\t\t\tWORKFLOW_EXECUTOR_GUIDE,\n\t\t],\n\t\tdescription:\n\t\t\t\"Create or update a workflow: a stage dependency graph (DAG) with per-stage objectives, acceptance evidence, and soft goal/plan references. On update, an omitted workflow-level optional field (title/goalId/maxRetreats/maxActiveStages) keeps its stored value; stages are always submitted in full\",\n\t\tparameters: workflowSetSchema,\n\t\trenderResult: toolResultRenderer,\n\t\texecute: async (first: unknown, second?: unknown) => {\n\t\t\tconst parsed = parseWorkflowSetInput(objectInput(first, second));\n\t\t\tvalidateStageGraph(parsed.stages);\n\t\t\tconst current = state.workflows.find((workflow) => workflow.id === parsed.id);\n\t\t\tif (current !== undefined && parsed.revision <= current.revision) {\n\t\t\t\tthrow new Error(`stale workflow revision: ${parsed.id}`);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tcurrent !== undefined &&\n\t\t\t\t(current.status === \"completed\" || current.status === \"failed\" || current.status === \"cancelled\")\n\t\t\t) {\n\t\t\t\tthrow new Error(`workflow is terminal and cannot be updated: ${parsed.id}`);\n\t\t\t}\n\t\t\tconst diff = planWorkflowSpecUpdate(current, parsed);\n\t\t\tconst run = applyWorkflowSet(state, parsed, diff, deps);\n\t\t\tstate = run.state;\n\t\t\tif (run.transitions.length > 0 || diff.created) persistRun(run);\n\t\t\tif (run.transitions.length > 0 || diff.created) await announce(run);\n\t\t\tconst response: WorkflowToolResponse = { ...state };\n\t\t\tif (run.closures.length > 0) response.closures = run.closures;\n\t\t\tconst warnings = collectSoftReferenceWarnings(\n\t\t\t\tapi,\n\t\t\t\tparsed.goalId,\n\t\t\t\tparsed.stages.flatMap((stage) =>\n\t\t\t\t\tstage.planId !== undefined ? [{ stageId: stage.id, planId: stage.planId }] : [],\n\t\t\t\t),\n\t\t\t\tparsed.stages.flatMap((stage) =>\n\t\t\t\t\tstage.role !== undefined ? [{ stageId: stage.id, role: stage.role }] : [],\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (warnings.length > 0) response.warning = warnings.join(\"; \");\n\t\t\treturn response;\n\t\t},\n\t});\n\n\tconst advanceTool = api.registerTool({\n\t\tname: \"workflow_advance\",\n\t\tlabel: \"Advance workflow\",\n\t\tdescription:\n\t\t\t\"Drive a workflow: start it, record acceptance evidence, inspect waiting conditions and coverage, approve/reject reviews, retreat, complete checkpoints, skip stages, or cancel\",\n\t\tparameters: workflowAdvanceSchema,\n\t\trenderResult: toolResultRenderer,\n\t\texecute: async (first: unknown, second?: unknown) => {\n\t\t\tconst parsed = parseAdvanceInput(objectInput(first, second));\n\t\t\t// Illegal conversions throw structured errors (§5 rule 6) surfaced\n\t\t\t// as tool errors unchanged; legal ones return the full snapshot\n\t\t\t// (the goal_set full-state response pattern).\n\t\t\tconst run = applyAdvance(state, parsed, deps);\n\t\t\tstate = run.state;\n\t\t\tif (run.transitions.length > 0) {\n\t\t\t\tpersistRun(run);\n\t\t\t\tawait announce(run);\n\t\t\t}\n\t\t\tconst response: WorkflowToolResponse = { ...state };\n\t\t\tif (parsed.action === \"inspect\") {\n\t\t\t\t// §5 rule 6 single-workflow semantics: applyAdvance has already\n\t\t\t\t// rejected unknown ids, so the target always resolves here.\n\t\t\t\tconst target = state.workflows.find((workflow) => workflow.id === parsed.workflowId);\n\t\t\t\tif (target !== undefined) response.inspections = [inspectWorkflow(target, deps)];\n\t\t\t}\n\t\t\tif (run.closures.length > 0) response.closures = run.closures;\n\t\t\treturn response;\n\t\t},\n\t});\n\n\tconst workflowCommand = api.registerCommand({\n\t\tname: \"workflow\",\n\t\tdescription: \"Workflow overview: dependency tree and status (default), why it is stopped, or the transition log\",\n\t\targumentHint: \"[view | why | log [N]]\",\n\t\texecute: async (args: string) => {\n\t\t\ttry {\n\t\t\t\tconst trimmed = args.trim();\n\t\t\t\tif (trimmed === \"\" || trimmed === \"view\") {\n\t\t\t\t\treturn { status: \"success\" as const, message: renderPanorama(state, deps) };\n\t\t\t\t}\n\t\t\t\tif (trimmed === \"why\") {\n\t\t\t\t\treturn { status: \"success\" as const, message: renderWhy(state, deps) };\n\t\t\t\t}\n\t\t\t\tconst logMatch = /^log(?:\\s+(\\d+))?$/.exec(trimmed);\n\t\t\t\tif (logMatch !== null) {\n\t\t\t\t\tconst limit = logMatch[1] === undefined ? 20 : Math.max(1, Number.parseInt(logMatch[1], 10));\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: \"success\" as const,\n\t\t\t\t\t\tmessage: renderLog(api.session?.getBranchEntries() ?? [], limit),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn { status: \"error\" as const, message: \"Usage: /workflow [view | why | log [N]]\" };\n\t\t\t} catch (error) {\n\t\t\t\t// A slash command must not crash the host (memory /promote precedent).\n\t\t\t\treturn { status: \"error\" as const, message: error instanceof Error ? error.message : String(error) };\n\t\t\t}\n\t\t},\n\t});\n\n\t// The repository's first event subscription (§7). The payload is ignored:\n\t// reevaluation always reads the current branch entries (§5 总则 1).\n\tconst planChanged = api.subscribe(\"plan.changed\", async () => {\n\t\ttry {\n\t\t\tconst run = reevaluateAll(state, deps, \"plan.changed\");\n\t\t\tif (run.transitions.length === 0) return;\n\t\t\tstate = run.state;\n\t\t\tpersistRun(run);\n\t\t\tawait announce(run);\n\t\t} catch (error) {\n\t\t\tquarantineEngineError(error, \"plan.changed\");\n\t\t}\n\t});\n\n\treturn [setTool, advanceTool, workflowCommand, planChanged];\n}\n"]}
package/dist/index.js CHANGED
@@ -30,9 +30,26 @@ export { WORKFLOW_STATE_ENTRY, WORKFLOW_TRANSITION_ENTRY } from "./types.js";
30
30
  const planStateEntry = "plan.state";
31
31
  /** Session protocol key of the goal plugin's snapshot entry (plugins/goal). */
32
32
  const goalStateEntry = "goal.state";
33
+ /**
34
+ * Session protocol key of the subagent-delegate plugin's session role snapshot
35
+ * (plugins/subagent-delegate, D-081 item 7: `{ version: 1, roles: [{ id, ... }] }`
36
+ * append-only full snapshots). Same literal-coupling precedent as plan/goal
37
+ * above: the workflow plugin never imports the owning plugin.
38
+ */
39
+ const subagentRoleStateEntry = "subagent.roles.state";
40
+ /**
41
+ * Role id shape: the same expression as the authoritative reader's
42
+ * ROLE_ID_PATTERN (plugins/subagent-delegate `readSessionRoles`). Same
43
+ * cross-plugin literal-coupling precedent as the entry keys above — the
44
+ * workflow plugin never imports the owning plugin. The residual difference is
45
+ * field-level only: subagent-delegate validates the whole role record and drops
46
+ * invalid entries, while this side reads the id alone — so the divergence can
47
+ * only under-warn, never false-warn.
48
+ */
49
+ const roleIdPattern = /^[a-z0-9][a-z0-9_-]{0,31}$/;
33
50
  const DIAGNOSTICS_CAP = 50;
34
51
  /** §3.1 applicability, verbatim scope (when to use / not to use a workflow). */
35
- const WORKFLOW_APPLICABILITY_GUIDE = "Workflow applicability — use a workflow only when ALL of these hold: (1) the goal needs at least 3 stages that can be accepted independently (you can write each stage's objective/acceptance); (2) the stages have a real dependency structure (branching or joins), not a plain sequence; (3) independent branches can be pushed in parallel — parallelism is a core payoff, purely linear multi-stage work belongs to goal/plan/todo; (4) the task spans multiple turns/days and is valuable enough to justify process overhead (definition, evidence recording, stage gates). Counterexamples that keep goal/plan/todo or direct execution: single-step tasks; two- or three-step linear tasks; exploratory work with no presettable dependency graph; one-shot questions. A workflow does not exclude the three layers — each stage still uses plan/todo for its execution checklist. Never escalate a small task to a workflow by default.";
52
+ const WORKFLOW_APPLICABILITY_GUIDE = "Workflow applicability — use a workflow only when ALL of these hold: (1) the goal needs at least 3 stages that can be accepted independently (you can write each stage's objective/acceptance); (2) the stages have a real dependency structure (branching or joins), not a plain sequence; (3) independent branches can be pushed in parallel — parallelism is a core payoff, purely linear multi-stage work belongs to goal/plan/todo; (4) the task spans multiple turns/days and is valuable enough to justify process overhead (definition, evidence recording, stage gates). Counterexamples that keep goal/plan/todo or direct execution: single-step tasks; two- or three-step linear tasks; exploratory work with no presettable dependency graph; one-shot questions. A workflow does not exclude the three layers — each stage still uses plan/todo for its execution checklist. Never escalate a small task to a workflow by default. When the user explicitly asks for workflow orchestration, still run this weighing: if the task genuinely misses the bar, skipping is fine — but say so in one line why you stayed on goal/plan/todo (an unexplained skip reads as ignoring the request); conversely, do not build one just because it was asked when the task is small (over-orchestration costs more than it saves). (2026-10-04 user ruling after a dogfood where an explicitly requested orchestration was silently skipped.)";
36
53
  const WORKFLOW_AUTHORING_GUIDE = "Define each stage by a verifiable end state, not by a work list or a role: objective.statement says what must be achieved; acceptance items list the evidence that proves it, referenced from real tool output. Stop splitting at the first verifiable boundary — finer handoffs lose more to interface drift than they gain in drift protection.";
37
54
  const WORKFLOW_EXECUTION_GUIDE = "After plan_set marks a stage's plan completed, run workflow_advance inspect to see unlocked stages and acceptance coverage. Record evidence (workflow_advance record-evidence) before completion is evaluated; lower-bound stages (no acceptance declared) need no record-evidence. When multiple stages are active, delegate one subagent per stage following stage.role and push them in parallel.";
38
55
  const WORKFLOW_CORRECTION_GUIDE = "On user corrections: run workflow_advance inspect first to confirm the current state, then route by what changed — amend the stage spec (active/awaiting_review stages, amendNote required), append a stage, skip, retreat, or cancel — and report the adjustment and its impact range back to the user.";
@@ -264,6 +281,43 @@ function buildLastStageTransition(api) {
264
281
  }
265
282
  return map;
266
283
  }
284
+ /**
285
+ * Role ids from the current branch's latest READABLE `subagent.roles.state`
286
+ * snapshot (D-081 item 7). Fallback is symmetric with the authoritative reader
287
+ * (subagent-delegate `readSessionRoles`): a snapshot-level corruption (data not
288
+ * an object / version mismatch / roles not an array) steps back to the previous
289
+ * readable snapshot, and only a fully unreadable table degrades to the empty
290
+ * set. Individual malformed role records are skipped — including ids that do
291
+ * not match `roleIdPattern` — keeping the remaining ids of an otherwise
292
+ * readable snapshot. Silent by contract: the
293
+ * subagent-delegate plugin owns the degradation warning on its side, and a
294
+ * second report here would double-warn for the same fact — this side only
295
+ * tolerates.
296
+ */
297
+ function loadSessionRoleIds(api) {
298
+ const entries = api.session?.getBranchEntries() ?? [];
299
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
300
+ const entry = entries[index];
301
+ if (entry.customType !== subagentRoleStateEntry)
302
+ continue;
303
+ const data = entry.data;
304
+ if (!data || typeof data !== "object" || Array.isArray(data))
305
+ continue;
306
+ const snapshot = data;
307
+ if (snapshot.version !== 1 || !Array.isArray(snapshot.roles))
308
+ continue;
309
+ const known = new Set();
310
+ for (const role of snapshot.roles) {
311
+ if (!role || typeof role !== "object" || Array.isArray(role))
312
+ continue;
313
+ const id = role.id;
314
+ if (typeof id === "string" && roleIdPattern.test(id))
315
+ known.add(id);
316
+ }
317
+ return known;
318
+ }
319
+ return new Set();
320
+ }
267
321
  /** Soft-reference warnings for the tool response only (never the persisted state, #10). */
268
322
  function collectSoftReferenceWarnings(api, goalId, planIds, roles) {
269
323
  const warnings = [];
@@ -276,12 +330,19 @@ function collectSoftReferenceWarnings(api, goalId, planIds, roles) {
276
330
  "the stage stays held until a reevaluation finds the plan");
277
331
  }
278
332
  }
333
+ // Two verifiable role sources (D-081 item 7): the host's suite role
334
+ // associations (disk roles) plus this branch's session role table. A host
335
+ // without the listing AND an empty session table verifies nothing, so the
336
+ // advice stays unverified — no warning (缺面不告警). An explicitly empty
337
+ // association face ([]) still warns for every role.
338
+ const associated = api.host?.roleAssociations;
339
+ const sessionRoleIds = roles.length > 0 ? loadSessionRoleIds(api) : new Set();
340
+ const merged = new Set([...(associated ?? []), ...sessionRoleIds]);
279
341
  for (const item of roles) {
280
- // The host's suite role associations are the only verifiable role face;
281
- // hosts without the listing leave the advice unverified (no warning).
282
- const associated = api.host?.roleAssociations;
283
- if (associated !== undefined && !associated.includes(item.role)) {
284
- warnings.push(`stage ${item.stageId}: role "${item.role}" is not associated with the active suite; delegation advice treated as unspecified`);
342
+ if ((associated !== undefined || sessionRoleIds.size > 0) && !merged.has(item.role)) {
343
+ warnings.push(`stage ${item.stageId}: role "${item.role}" is neither associated with the active suite nor ` +
344
+ "registered in this session; delegation advice treated as unspecified — register it with " +
345
+ "subagent_role_set or add it to the suite's roles associations");
285
346
  }
286
347
  }
287
348
  return warnings;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Workflow orchestration capability plugin (M1 of\n * `docs/design/工作流编排插件设计.md`): a fourth self-contained state machine\n * orchestrating a stage dependency graph on top of the goal/plan/todo layers.\n *\n * Persistence protocol, structurally identical to the goal/plan/todo plugins:\n * append-only `workflow.state` full snapshots (the single rebuild source) plus\n * one `workflow.transition` audit entry per conversion, announced as\n * `workflow.changed` (version 1, correlationId \"workflow.state\", data = the\n * complete WorkflowState snapshot).\n *\n * This plugin is the repository's FIRST event subscriber (§7): `plan.changed`\n * is used as a trigger signal only — its in-memory data may be misaligned\n * with the current branch, so every reevaluation reads the plan facts from\n * the current branch entries. The handler is fully isolated (§5 总则 3): any\n * failure lands in diagnostics + a `handler.error` transition and never\n * rethrows at the publisher (an unisolated throw would pierce plan_set's tool\n * return). Soft references (goalId / planId / role) never fail a write (#10):\n * they surface as response warnings; a dangling planId additionally holds the\n * stage's activation until a reevaluation finds the plan.\n */\n\nimport type { CapabilityAPI, DisposableRegistration } from \"@agent-forge/plugin-sdk\";\nimport { Type } from \"typebox\";\nimport {\n\ttype AdvanceAction,\n\ttype AdvanceInput,\n\tapplyAdvance,\n\tapplyWorkflowSet,\n\ttype EngineDeps,\n\ttype EngineRun,\n\ttype EvidenceItem,\n\tinspectWorkflow,\n\ttype PlanFacts,\n\treevaluateAll,\n\ttype WorkflowClosure,\n\ttype WorkflowInspection,\n} from \"./engine.ts\";\nimport { createWorkflowToolResultRenderer, renderLog, renderPanorama, renderWhy } from \"./render.ts\";\nimport {\n\ttype TransitionReasonCode,\n\tWORKFLOW_STATE_ENTRY,\n\tWORKFLOW_TRANSITION_ENTRY,\n\ttype WorkflowState,\n\ttype WorkflowTransitionEntry,\n\ttype WorkflowTransitionTrigger,\n} from \"./types.ts\";\nimport { asObject, asText, parseWorkflowSetInput, planWorkflowSpecUpdate, validateStageGraph } from \"./validate.ts\";\n\nexport type {\n\tAcceptanceCoverage,\n\tAdvanceAction,\n\tAdvanceInput,\n\tClosureEvidenceItem,\n\tEngineDeps,\n\tEngineRun,\n\tEvidenceItem,\n\tPlanFacts,\n\tStageInspection,\n\tWorkflowClosure,\n\tWorkflowInspection,\n} from \"./engine.ts\";\nexport { cascadeLimitFor, computeWaitingOn, reevaluateAll } from \"./engine.ts\";\nexport type {\n\tStageRuntime,\n\tStageSpec,\n\tTransitionReason,\n\tTransitionReasonCode,\n\tWorkflowRecord,\n\tWorkflowState,\n\tWorkflowTransitionEntry,\n\tWorkflowTransitionTrigger,\n} from \"./types.ts\";\nexport { WORKFLOW_STATE_ENTRY, WORKFLOW_TRANSITION_ENTRY } from \"./types.ts\";\n\n/** Session protocol key of the plan plugin's snapshot entry (plugins/plan). */\nconst planStateEntry = \"plan.state\";\n/** Session protocol key of the goal plugin's snapshot entry (plugins/goal). */\nconst goalStateEntry = \"goal.state\";\nconst DIAGNOSTICS_CAP = 50;\n\n/** §3.1 applicability, verbatim scope (when to use / not to use a workflow). */\nconst WORKFLOW_APPLICABILITY_GUIDE =\n\t\"Workflow applicability — use a workflow only when ALL of these hold: (1) the goal needs at least 3 stages that can be accepted independently (you can write each stage's objective/acceptance); (2) the stages have a real dependency structure (branching or joins), not a plain sequence; (3) independent branches can be pushed in parallel — parallelism is a core payoff, purely linear multi-stage work belongs to goal/plan/todo; (4) the task spans multiple turns/days and is valuable enough to justify process overhead (definition, evidence recording, stage gates). Counterexamples that keep goal/plan/todo or direct execution: single-step tasks; two- or three-step linear tasks; exploratory work with no presettable dependency graph; one-shot questions. A workflow does not exclude the three layers — each stage still uses plan/todo for its execution checklist. Never escalate a small task to a workflow by default.\";\n\nconst WORKFLOW_AUTHORING_GUIDE =\n\t\"Define each stage by a verifiable end state, not by a work list or a role: objective.statement says what must be achieved; acceptance items list the evidence that proves it, referenced from real tool output. Stop splitting at the first verifiable boundary — finer handoffs lose more to interface drift than they gain in drift protection.\";\n\nconst WORKFLOW_EXECUTION_GUIDE =\n\t\"After plan_set marks a stage's plan completed, run workflow_advance inspect to see unlocked stages and acceptance coverage. Record evidence (workflow_advance record-evidence) before completion is evaluated; lower-bound stages (no acceptance declared) need no record-evidence. When multiple stages are active, delegate one subagent per stage following stage.role and push them in parallel.\";\n\nconst WORKFLOW_CORRECTION_GUIDE =\n\t\"On user corrections: run workflow_advance inspect first to confirm the current state, then route by what changed — amend the stage spec (active/awaiting_review stages, amendNote required), append a stage, skip, retreat, or cancel — and report the adjustment and its impact range back to the user.\";\n\nconst WORKFLOW_REVIEW_GUIDE =\n\t\"Stages declared advance: 'review' stop at awaiting_review. Before approve you must present the result and its evidence to the user and get explicit confirmation — never approve on your own (the engine does not authorize; the discipline lives here).\";\n\nconst WORKFLOW_CONCURRENCY_GUIDE =\n\t\"Do not send workflow_set in the same parallel batch as plan_set: amends and completion evaluation race (completed freezes first; if the amend loses, recover via retreat).\";\n\nconst WORKFLOW_INCREMENTAL_DEMAND_GUIDE =\n\t\"When a new batch of requirements arrives (incremental features on top of shipped work), default to creating a NEW workflow to carry it — reuse the same goalId and keep the completed workflow's history intact; never pull a completed workflow back into motion. Retreat or append a corrective stage only when the new requirements overturn already completed output.\";\n\nconst WORKFLOW_EXECUTOR_GUIDE =\n\t\"When delegating a stage to a subagent, pass that subagent's session/task id as the executor field of record-evidence — it is recorded on the stage (advisory) so /workflow log shows who drove it.\";\n\nconst acceptanceSchema = Type.Object({\n\tid: Type.String({ description: \"Acceptance item id; evidence references this id\" }),\n\tstatement: Type.String({ description: \"Verifiable completion criterion for this item\" }),\n});\n\nconst stageSchema = Type.Object({\n\tid: Type.String({ description: \"Stable stage id, unique within the workflow\" }),\n\ttitle: Type.String({ description: \"Human-readable stage title\" }),\n\tobjective: Type.Object({\n\t\tstatement: Type.String({\n\t\t\tdescription: \"Verifiable end state this stage must reach (required, non-empty)\",\n\t\t}),\n\t\tacceptance: Type.Optional(\n\t\t\tType.Array(acceptanceSchema, {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Completion criteria items; when empty (or omitted) the lower bound is the referenced plan's completion evidence\",\n\t\t\t}),\n\t\t),\n\t}),\n\tdependsOn: Type.Optional(\n\t\tType.Array(Type.String(), { description: \"Hard prerequisite stage ids; empty array = root stage\" }),\n\t),\n\tplanId: Type.Optional(Type.String({ description: \"Soft reference to a plan id; missing = pure checkpoint stage\" })),\n\tkind: Type.Optional(\n\t\tType.Union([Type.Literal(\"read\"), Type.Literal(\"write\")], {\n\t\t\tdescription: 'Stage kind; write stages are scope-checked, default \"write\"',\n\t\t}),\n\t),\n\tscope: Type.Optional(\n\t\tType.Array(Type.String(), {\n\t\t\tdescription: \"Write scope tags (modules/dirs); missing = global (intersects every other write stage)\",\n\t\t}),\n\t),\n\trole: Type.Optional(\n\t\tType.String({ description: \"Delegation advice: role id for the subagent recommended to drive this stage\" }),\n\t),\n\tadvance: Type.Optional(\n\t\tType.Union([Type.Literal(\"auto\"), Type.Literal(\"review\")], {\n\t\t\tdescription:\n\t\t\t\t'\"auto\" completes on evaluation, \"review\" stops at awaiting_review (planId stages only), default \"auto\"',\n\t\t}),\n\t),\n\tretryLimit: Type.Optional(\n\t\tType.Integer({ description: \"In-place retry limit before onFail/workflow failure, default 2\", minimum: 0 }),\n\t),\n\tonFail: Type.Optional(\n\t\tType.String({ description: \"Explicit cross-stage retreat target (a topologically earlier stage id)\" }),\n\t),\n\tamendNote: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Correction reason; required when an active/awaiting_review stage's spec changes\",\n\t\t}),\n\t),\n});\n\nconst workflowSetSchema = Type.Object({\n\tid: Type.String({ description: \"Stable id of the workflow to create or update\" }),\n\ttitle: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Human-readable workflow title; required on create, omitting on update keeps the stored value\",\n\t\t}),\n\t),\n\trevision: Type.Integer({\n\t\tdescription: \"New revision counter; must be greater than the stored revision of an existing workflow\",\n\t\tminimum: 0,\n\t}),\n\tgoalId: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Soft reference to a goal id; the closure evidence summary is addressed to this goal. Omitting on update keeps the stored value\",\n\t\t}),\n\t),\n\tmaxRetreats: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription:\n\t\t\t\t\"Workflow lifetime retreat budget before failure, default 3. Omitting on update keeps the stored value\",\n\t\t\tminimum: 0,\n\t\t}),\n\t),\n\tmaxActiveStages: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Parallel activation cap, default 4. Omitting on update keeps the stored value\",\n\t\t\tminimum: 1,\n\t\t}),\n\t),\n\tstages: Type.Array(stageSchema, {\n\t\tdescription: \"Full stage list (whole submit, not a patch); the engine aligns runtime by stage id\",\n\t}),\n});\n\nconst workflowAdvanceSchema = Type.Object({\n\tworkflowId: Type.String({ description: \"Target workflow id\" }),\n\taction: Type.Union(\n\t\t[\n\t\t\tType.Literal(\"start\"),\n\t\t\tType.Literal(\"record-evidence\"),\n\t\t\tType.Literal(\"inspect\"),\n\t\t\tType.Literal(\"approve\"),\n\t\t\tType.Literal(\"reject\"),\n\t\t\tType.Literal(\"retreat\"),\n\t\t\tType.Literal(\"complete\"),\n\t\t\tType.Literal(\"skip\"),\n\t\t\tType.Literal(\"cancel\"),\n\t\t],\n\t\t{ description: \"Explicit action to perform\" },\n\t),\n\tstageId: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Stage id: the evidence/approve/reject/complete/skip target, or the retreat target stage to roll back to\",\n\t\t}),\n\t),\n\texecutor: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Advisory executor id (e.g. subagent session/task id) driving the stage; record-only, last-write-wins, never affects evaluation\",\n\t\t}),\n\t),\n\tevidence: Type.Optional(\n\t\tType.Array(\n\t\t\tType.Object({\n\t\t\t\tacceptanceId: Type.Optional(\n\t\t\t\t\tType.String({ description: \"Acceptance item id; required in acceptance mode\" }),\n\t\t\t\t),\n\t\t\t\tref: Type.String({ description: \"Evidence reference (tool output, file, artifact)\" }),\n\t\t\t}),\n\t\t\t{ description: \"Evidence items for record-evidence/approve/complete\" },\n\t\t),\n\t),\n\tnote: Type.Optional(\n\t\tType.String({ description: \"Optional note recorded in the transition detail (reject/retreat/skip/cancel)\" }),\n\t),\n});\n\nconst ADVANCE_ACTIONS: readonly string[] = [\n\t\"start\",\n\t\"record-evidence\",\n\t\"inspect\",\n\t\"approve\",\n\t\"reject\",\n\t\"retreat\",\n\t\"complete\",\n\t\"skip\",\n\t\"cancel\",\n];\n\nfunction objectInput(first: unknown, second: unknown): Record<string, unknown> {\n\tconst value = second === undefined ? first : second;\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\tthrow new Error(\"Workflow input must be an object\");\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction parseAdvanceInput(value: Record<string, unknown>): AdvanceInput {\n\tconst workflowId = asText(value.workflowId, \"workflowId\");\n\tconst action = value.action;\n\tif (typeof action !== \"string\" || !ADVANCE_ACTIONS.includes(action)) {\n\t\tthrow new Error(`action must be one of ${ADVANCE_ACTIONS.join(\", \")}`);\n\t}\n\tconst stageId = value.stageId === undefined ? undefined : asText(value.stageId, \"stageId\");\n\tconst note = value.note === undefined ? undefined : asText(value.note, \"note\");\n\tconst executor = value.executor === undefined ? undefined : asText(value.executor, \"executor\");\n\tlet evidence: EvidenceItem[] | undefined;\n\tif (value.evidence !== undefined) {\n\t\tif (!Array.isArray(value.evidence)) throw new Error(\"evidence must be an array of {acceptanceId?, ref}\");\n\t\tevidence = value.evidence.map((item) => {\n\t\t\tconst entry = asObject(item, \"evidence item\");\n\t\t\tconst ref = asText(entry.ref, \"evidence item ref\");\n\t\t\tconst acceptanceId =\n\t\t\t\tentry.acceptanceId === undefined ? undefined : asText(entry.acceptanceId, \"evidence item acceptanceId\");\n\t\t\treturn acceptanceId === undefined ? { ref } : { acceptanceId, ref };\n\t\t});\n\t}\n\treturn {\n\t\tworkflowId,\n\t\taction: action as AdvanceAction,\n\t\t...(stageId !== undefined ? { stageId } : {}),\n\t\t...(evidence !== undefined ? { evidence } : {}),\n\t\t...(note !== undefined ? { note } : {}),\n\t\t...(executor !== undefined ? { executor } : {}),\n\t};\n}\n\nfunction loadState(api: CapabilityAPI): WorkflowState {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tconst entry = [...entries].reverse().find((item) => item.customType === WORKFLOW_STATE_ENTRY);\n\tif (!entry?.data || typeof entry.data !== \"object\") return { workflows: [] };\n\tconst data = entry.data as Partial<WorkflowState>;\n\treturn { workflows: Array.isArray(data.workflows) ? [...data.workflows] : [] };\n}\n\n/**\n * Plan facts read from the current branch's LATEST `plan.state` snapshot (the\n * same latest-snapshot semantics as the plan plugin's own loadState). The\n * plan.changed payload is never trusted for state — only as a trigger.\n *\n * Uncached by design: identity-keyed caching collided across test hosts and\n * hooking invalidation into appendEntry crosses the persistence boundary; the\n * O(stages x entries) rescan is the price of snapshot-driven truth. Revisit\n * only with a per-run cache channel if long-session profiling demands it.\n */\nfunction loadPlansSnapshot(api: CapabilityAPI): Map<string, PlanFacts> {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tfor (let index = entries.length - 1; index >= 0; index -= 1) {\n\t\tconst entry = entries[index] as { customType?: string; data?: unknown } | undefined;\n\t\tif (entry?.customType !== planStateEntry) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return new Map();\n\t\tconst plans = (data as { plans?: unknown }).plans;\n\t\tif (!Array.isArray(plans)) return new Map();\n\t\tconst facts = new Map<string, PlanFacts>();\n\t\tfor (const plan of plans) {\n\t\t\tif (!plan || typeof plan !== \"object\") continue;\n\t\t\tconst record = plan as { id?: unknown; status?: unknown; evidence?: unknown };\n\t\t\tif (typeof record.id !== \"string\") continue;\n\t\t\tconst status =\n\t\t\t\trecord.status === \"draft\" ||\n\t\t\t\trecord.status === \"active\" ||\n\t\t\t\trecord.status === \"completed\" ||\n\t\t\t\trecord.status === \"cancelled\"\n\t\t\t\t\t? record.status\n\t\t\t\t\t: \"draft\";\n\t\t\tconst evidence = Array.isArray(record.evidence)\n\t\t\t\t? record.evidence.filter((item): item is string => typeof item === \"string\")\n\t\t\t\t: [];\n\t\t\tfacts.set(record.id, { id: record.id, status, evidence });\n\t\t}\n\t\treturn facts;\n\t}\n\treturn new Map();\n}\n\n/** Whether the latest `<entryType>` snapshot on the branch knows `id` in its `idsField` array (advisory, #10). */\nfunction idKnownToSession(api: CapabilityAPI, entryType: string, idsField: string, id: string): boolean {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tfor (let index = entries.length - 1; index >= 0; index -= 1) {\n\t\tconst entry = entries[index];\n\t\tif (entry.customType !== entryType) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return false;\n\t\tconst records = (data as Record<string, unknown>)[idsField];\n\t\tif (!Array.isArray(records)) return false;\n\t\treturn records.some(\n\t\t\t(record) => record !== null && typeof record === \"object\" && (record as { id?: unknown }).id === id,\n\t\t);\n\t}\n\treturn false;\n}\n\n/**\n * Seeds the held-dedup view (§5 总则 5) from the branch audit stream: the\n * last transition per stage, whatever its code — a repeat held emission is\n * dropped only when it matches that last code and detail.\n */\nfunction buildLastStageTransition(api: CapabilityAPI): Map<string, { code: TransitionReasonCode; detail?: string }> {\n\tconst map = new Map<string, { code: TransitionReasonCode; detail?: string }>();\n\tfor (const entry of api.session?.getBranchEntries() ?? []) {\n\t\tif (entry.customType !== WORKFLOW_TRANSITION_ENTRY) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) continue;\n\t\tconst record = data as Partial<WorkflowTransitionEntry>;\n\t\tif (typeof record.workflowId !== \"string\" || typeof record.stageId !== \"string\") continue;\n\t\tconst reason = record.reason as { code?: unknown; detail?: unknown } | undefined;\n\t\tif (!reason || typeof reason !== \"object\" || typeof reason.code !== \"string\") continue;\n\t\tmap.set(`${record.workflowId}::${record.stageId}`, {\n\t\t\tcode: reason.code as TransitionReasonCode,\n\t\t\tdetail: typeof reason.detail === \"string\" ? reason.detail : undefined,\n\t\t});\n\t}\n\treturn map;\n}\n\n/** Soft-reference warnings for the tool response only (never the persisted state, #10). */\nfunction collectSoftReferenceWarnings(\n\tapi: CapabilityAPI,\n\tgoalId: string | undefined,\n\tplanIds: { stageId: string; planId: string }[],\n\troles: { stageId: string; role: string }[],\n): string[] {\n\tconst warnings: string[] = [];\n\tif (goalId !== undefined && !idKnownToSession(api, goalStateEntry, \"goals\", goalId)) {\n\t\twarnings.push(\n\t\t\t`goalId \"${goalId}\" does not match any goal in this session's goal state; the workflow keeps the soft reference`,\n\t\t);\n\t}\n\tfor (const item of planIds) {\n\t\tif (!idKnownToSession(api, planStateEntry, \"plans\", item.planId)) {\n\t\t\twarnings.push(\n\t\t\t\t`stage ${item.stageId}: planId \"${item.planId}\" does not match any plan in this session's plan state; ` +\n\t\t\t\t\t\"the stage stays held until a reevaluation finds the plan\",\n\t\t\t);\n\t\t}\n\t}\n\tfor (const item of roles) {\n\t\t// The host's suite role associations are the only verifiable role face;\n\t\t// hosts without the listing leave the advice unverified (no warning).\n\t\tconst associated = api.host?.roleAssociations;\n\t\tif (associated !== undefined && !associated.includes(item.role)) {\n\t\t\twarnings.push(\n\t\t\t\t`stage ${item.stageId}: role \"${item.role}\" is not associated with the active suite; delegation advice treated as unspecified`,\n\t\t\t);\n\t\t}\n\t}\n\treturn warnings;\n}\n\ninterface WorkflowToolResponse extends WorkflowState {\n\tclosures?: WorkflowClosure[];\n\tinspections?: WorkflowInspection[];\n\twarning?: string;\n}\n\nexport function createCapability(\n\tapi: CapabilityAPI,\n): Promise<readonly DisposableRegistration[]> | readonly DisposableRegistration[] {\n\tlet state = loadState(api);\n\tconst deps: EngineDeps = {\n\t\tgetPlan: (planId) => loadPlansSnapshot(api).get(planId),\n\t\tlastStageTransition: buildLastStageTransition(api),\n\t};\n\t// Shared structural renderResult (render.ts): the host TUI gets the\n\t// panorama face for both tools' JSON-serialized responses.\n\tconst toolResultRenderer = createWorkflowToolResultRenderer(deps);\n\n\t// Persistence boundary (§4): audit entries first, then the full snapshot —\n\t// a crash mid-batch replays from the older snapshot while the audit stays\n\t// intact. appendEntry failures propagate unchanged (§5 总则 5: no swallow);\n\t// publish rejections surface to the tool caller (or are contained on the\n\t// load/subscriber paths below).\n\tconst persistRun = (run: EngineRun): void => {\n\t\tfor (const transition of run.transitions) {\n\t\t\tapi.session?.appendEntry(WORKFLOW_TRANSITION_ENTRY, transition);\n\t\t}\n\t\tapi.session?.appendEntry(WORKFLOW_STATE_ENTRY, run.state);\n\t};\n\tconst announce = (run: EngineRun): Promise<unknown> =>\n\t\tapi.publish({ type: \"workflow.changed\", version: 1, correlationId: WORKFLOW_STATE_ENTRY, data: run.state });\n\n\t/**\n\t * §5 总则 3 containment: land the failure in diagnostics + a handler.error\n\t * transition (detail = original error text) and never rethrow. Best-effort\n\t * — an already-failing boundary must not turn isolation into a second crash.\n\t */\n\tconst quarantineEngineError = (error: unknown, trigger: WorkflowTransitionTrigger): void => {\n\t\ttry {\n\t\t\tif (state.workflows.length === 0) return;\n\t\t\tconst original = error instanceof Error ? error.message : String(error);\n\t\t\tconst text = `${trigger} handler failed: ${original}`;\n\t\t\tconst workflows = state.workflows.map((workflow) => structuredClone(workflow));\n\t\t\tconst transitions: WorkflowTransitionEntry[] = [];\n\t\t\tfor (const workflow of workflows) {\n\t\t\t\tif (workflow.diagnostics.length >= DIAGNOSTICS_CAP) workflow.diagnostics.shift();\n\t\t\t\tworkflow.diagnostics.push(text);\n\t\t\t\tworkflow.revision += 1;\n\t\t\t\ttransitions.push({\n\t\t\t\t\tworkflowId: workflow.id,\n\t\t\t\t\tfrom: workflow.status,\n\t\t\t\t\tto: workflow.status,\n\t\t\t\t\ttrigger,\n\t\t\t\t\treason: { code: \"handler.error\", message: text, detail: original },\n\t\t\t\t\trevision: workflow.revision,\n\t\t\t\t});\n\t\t\t}\n\t\t\tstate = { workflows };\n\t\t\tfor (const transition of transitions) {\n\t\t\t\tapi.session?.appendEntry(WORKFLOW_TRANSITION_ENTRY, transition);\n\t\t\t}\n\t\t\tapi.session?.appendEntry(WORKFLOW_STATE_ENTRY, state);\n\t\t\tvoid api\n\t\t\t\t.publish({ type: \"workflow.changed\", version: 1, correlationId: WORKFLOW_STATE_ENTRY, data: state })\n\t\t\t\t.catch(() => undefined);\n\t\t} catch {\n\t\t\t// Containment is best-effort by design.\n\t\t}\n\t};\n\n\t// §5 rule 8 recovery: state replayed from workflow.state, then the\n\t// load-time reevaluation (总则 1 fallback moment #1). Runs synchronously at\n\t// factory time; failures are contained, never blocking plugin load.\n\ttry {\n\t\tconst run = reevaluateAll(state, deps, \"load-reeval\");\n\t\tif (run.transitions.length > 0) {\n\t\t\tstate = run.state;\n\t\t\tpersistRun(run);\n\t\t\tvoid announce(run).catch(() => undefined);\n\t\t}\n\t} catch (error) {\n\t\tquarantineEngineError(error, \"load-reeval\");\n\t}\n\n\tconst setTool = api.registerTool({\n\t\tname: \"workflow_set\",\n\t\tlabel: \"Set workflow\",\n\t\tpromptGuidelines: [\n\t\t\tWORKFLOW_APPLICABILITY_GUIDE,\n\t\t\tWORKFLOW_AUTHORING_GUIDE,\n\t\t\tWORKFLOW_EXECUTION_GUIDE,\n\t\t\tWORKFLOW_CORRECTION_GUIDE,\n\t\t\tWORKFLOW_REVIEW_GUIDE,\n\t\t\tWORKFLOW_CONCURRENCY_GUIDE,\n\t\t\tWORKFLOW_INCREMENTAL_DEMAND_GUIDE,\n\t\t\tWORKFLOW_EXECUTOR_GUIDE,\n\t\t],\n\t\tdescription:\n\t\t\t\"Create or update a workflow: a stage dependency graph (DAG) with per-stage objectives, acceptance evidence, and soft goal/plan references. On update, an omitted workflow-level optional field (title/goalId/maxRetreats/maxActiveStages) keeps its stored value; stages are always submitted in full\",\n\t\tparameters: workflowSetSchema,\n\t\trenderResult: toolResultRenderer,\n\t\texecute: async (first: unknown, second?: unknown) => {\n\t\t\tconst parsed = parseWorkflowSetInput(objectInput(first, second));\n\t\t\tvalidateStageGraph(parsed.stages);\n\t\t\tconst current = state.workflows.find((workflow) => workflow.id === parsed.id);\n\t\t\tif (current !== undefined && parsed.revision <= current.revision) {\n\t\t\t\tthrow new Error(`stale workflow revision: ${parsed.id}`);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tcurrent !== undefined &&\n\t\t\t\t(current.status === \"completed\" || current.status === \"failed\" || current.status === \"cancelled\")\n\t\t\t) {\n\t\t\t\tthrow new Error(`workflow is terminal and cannot be updated: ${parsed.id}`);\n\t\t\t}\n\t\t\tconst diff = planWorkflowSpecUpdate(current, parsed);\n\t\t\tconst run = applyWorkflowSet(state, parsed, diff, deps);\n\t\t\tstate = run.state;\n\t\t\tif (run.transitions.length > 0 || diff.created) persistRun(run);\n\t\t\tif (run.transitions.length > 0 || diff.created) await announce(run);\n\t\t\tconst response: WorkflowToolResponse = { ...state };\n\t\t\tif (run.closures.length > 0) response.closures = run.closures;\n\t\t\tconst warnings = collectSoftReferenceWarnings(\n\t\t\t\tapi,\n\t\t\t\tparsed.goalId,\n\t\t\t\tparsed.stages.flatMap((stage) =>\n\t\t\t\t\tstage.planId !== undefined ? [{ stageId: stage.id, planId: stage.planId }] : [],\n\t\t\t\t),\n\t\t\t\tparsed.stages.flatMap((stage) =>\n\t\t\t\t\tstage.role !== undefined ? [{ stageId: stage.id, role: stage.role }] : [],\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (warnings.length > 0) response.warning = warnings.join(\"; \");\n\t\t\treturn response;\n\t\t},\n\t});\n\n\tconst advanceTool = api.registerTool({\n\t\tname: \"workflow_advance\",\n\t\tlabel: \"Advance workflow\",\n\t\tdescription:\n\t\t\t\"Drive a workflow: start it, record acceptance evidence, inspect waiting conditions and coverage, approve/reject reviews, retreat, complete checkpoints, skip stages, or cancel\",\n\t\tparameters: workflowAdvanceSchema,\n\t\trenderResult: toolResultRenderer,\n\t\texecute: async (first: unknown, second?: unknown) => {\n\t\t\tconst parsed = parseAdvanceInput(objectInput(first, second));\n\t\t\t// Illegal conversions throw structured errors (§5 rule 6) surfaced\n\t\t\t// as tool errors unchanged; legal ones return the full snapshot\n\t\t\t// (the goal_set full-state response pattern).\n\t\t\tconst run = applyAdvance(state, parsed, deps);\n\t\t\tstate = run.state;\n\t\t\tif (run.transitions.length > 0) {\n\t\t\t\tpersistRun(run);\n\t\t\t\tawait announce(run);\n\t\t\t}\n\t\t\tconst response: WorkflowToolResponse = { ...state };\n\t\t\tif (parsed.action === \"inspect\") {\n\t\t\t\t// §5 rule 6 single-workflow semantics: applyAdvance has already\n\t\t\t\t// rejected unknown ids, so the target always resolves here.\n\t\t\t\tconst target = state.workflows.find((workflow) => workflow.id === parsed.workflowId);\n\t\t\t\tif (target !== undefined) response.inspections = [inspectWorkflow(target, deps)];\n\t\t\t}\n\t\t\tif (run.closures.length > 0) response.closures = run.closures;\n\t\t\treturn response;\n\t\t},\n\t});\n\n\tconst workflowCommand = api.registerCommand({\n\t\tname: \"workflow\",\n\t\tdescription: \"Workflow overview: dependency tree and status (default), why it is stopped, or the transition log\",\n\t\targumentHint: \"[view | why | log [N]]\",\n\t\texecute: async (args: string) => {\n\t\t\ttry {\n\t\t\t\tconst trimmed = args.trim();\n\t\t\t\tif (trimmed === \"\" || trimmed === \"view\") {\n\t\t\t\t\treturn { status: \"success\" as const, message: renderPanorama(state, deps) };\n\t\t\t\t}\n\t\t\t\tif (trimmed === \"why\") {\n\t\t\t\t\treturn { status: \"success\" as const, message: renderWhy(state, deps) };\n\t\t\t\t}\n\t\t\t\tconst logMatch = /^log(?:\\s+(\\d+))?$/.exec(trimmed);\n\t\t\t\tif (logMatch !== null) {\n\t\t\t\t\tconst limit = logMatch[1] === undefined ? 20 : Math.max(1, Number.parseInt(logMatch[1], 10));\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: \"success\" as const,\n\t\t\t\t\t\tmessage: renderLog(api.session?.getBranchEntries() ?? [], limit),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn { status: \"error\" as const, message: \"Usage: /workflow [view | why | log [N]]\" };\n\t\t\t} catch (error) {\n\t\t\t\t// A slash command must not crash the host (memory /promote precedent).\n\t\t\t\treturn { status: \"error\" as const, message: error instanceof Error ? error.message : String(error) };\n\t\t\t}\n\t\t},\n\t});\n\n\t// The repository's first event subscription (§7). The payload is ignored:\n\t// reevaluation always reads the current branch entries (§5 总则 1).\n\tconst planChanged = api.subscribe(\"plan.changed\", async () => {\n\t\ttry {\n\t\t\tconst run = reevaluateAll(state, deps, \"plan.changed\");\n\t\t\tif (run.transitions.length === 0) return;\n\t\t\tstate = run.state;\n\t\t\tpersistRun(run);\n\t\t\tawait announce(run);\n\t\t} catch (error) {\n\t\t\tquarantineEngineError(error, \"plan.changed\");\n\t\t}\n\t});\n\n\treturn [setTool, advanceTool, workflowCommand, planChanged];\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Workflow orchestration capability plugin (M1 of\n * `docs/design/工作流编排插件设计.md`): a fourth self-contained state machine\n * orchestrating a stage dependency graph on top of the goal/plan/todo layers.\n *\n * Persistence protocol, structurally identical to the goal/plan/todo plugins:\n * append-only `workflow.state` full snapshots (the single rebuild source) plus\n * one `workflow.transition` audit entry per conversion, announced as\n * `workflow.changed` (version 1, correlationId \"workflow.state\", data = the\n * complete WorkflowState snapshot).\n *\n * This plugin is the repository's FIRST event subscriber (§7): `plan.changed`\n * is used as a trigger signal only — its in-memory data may be misaligned\n * with the current branch, so every reevaluation reads the plan facts from\n * the current branch entries. The handler is fully isolated (§5 总则 3): any\n * failure lands in diagnostics + a `handler.error` transition and never\n * rethrows at the publisher (an unisolated throw would pierce plan_set's tool\n * return). Soft references (goalId / planId / role) never fail a write (#10):\n * they surface as response warnings; a dangling planId additionally holds the\n * stage's activation until a reevaluation finds the plan.\n */\n\nimport type { CapabilityAPI, DisposableRegistration } from \"@agent-forge/plugin-sdk\";\nimport { Type } from \"typebox\";\nimport {\n\ttype AdvanceAction,\n\ttype AdvanceInput,\n\tapplyAdvance,\n\tapplyWorkflowSet,\n\ttype EngineDeps,\n\ttype EngineRun,\n\ttype EvidenceItem,\n\tinspectWorkflow,\n\ttype PlanFacts,\n\treevaluateAll,\n\ttype WorkflowClosure,\n\ttype WorkflowInspection,\n} from \"./engine.ts\";\nimport { createWorkflowToolResultRenderer, renderLog, renderPanorama, renderWhy } from \"./render.ts\";\nimport {\n\ttype TransitionReasonCode,\n\tWORKFLOW_STATE_ENTRY,\n\tWORKFLOW_TRANSITION_ENTRY,\n\ttype WorkflowState,\n\ttype WorkflowTransitionEntry,\n\ttype WorkflowTransitionTrigger,\n} from \"./types.ts\";\nimport { asObject, asText, parseWorkflowSetInput, planWorkflowSpecUpdate, validateStageGraph } from \"./validate.ts\";\n\nexport type {\n\tAcceptanceCoverage,\n\tAdvanceAction,\n\tAdvanceInput,\n\tClosureEvidenceItem,\n\tEngineDeps,\n\tEngineRun,\n\tEvidenceItem,\n\tPlanFacts,\n\tStageInspection,\n\tWorkflowClosure,\n\tWorkflowInspection,\n} from \"./engine.ts\";\nexport { cascadeLimitFor, computeWaitingOn, reevaluateAll } from \"./engine.ts\";\nexport type {\n\tStageRuntime,\n\tStageSpec,\n\tTransitionReason,\n\tTransitionReasonCode,\n\tWorkflowRecord,\n\tWorkflowState,\n\tWorkflowTransitionEntry,\n\tWorkflowTransitionTrigger,\n} from \"./types.ts\";\nexport { WORKFLOW_STATE_ENTRY, WORKFLOW_TRANSITION_ENTRY } from \"./types.ts\";\n\n/** Session protocol key of the plan plugin's snapshot entry (plugins/plan). */\nconst planStateEntry = \"plan.state\";\n/** Session protocol key of the goal plugin's snapshot entry (plugins/goal). */\nconst goalStateEntry = \"goal.state\";\n/**\n * Session protocol key of the subagent-delegate plugin's session role snapshot\n * (plugins/subagent-delegate, D-081 item 7: `{ version: 1, roles: [{ id, ... }] }`\n * append-only full snapshots). Same literal-coupling precedent as plan/goal\n * above: the workflow plugin never imports the owning plugin.\n */\nconst subagentRoleStateEntry = \"subagent.roles.state\";\n/**\n * Role id shape: the same expression as the authoritative reader's\n * ROLE_ID_PATTERN (plugins/subagent-delegate `readSessionRoles`). Same\n * cross-plugin literal-coupling precedent as the entry keys above — the\n * workflow plugin never imports the owning plugin. The residual difference is\n * field-level only: subagent-delegate validates the whole role record and drops\n * invalid entries, while this side reads the id alone — so the divergence can\n * only under-warn, never false-warn.\n */\nconst roleIdPattern = /^[a-z0-9][a-z0-9_-]{0,31}$/;\nconst DIAGNOSTICS_CAP = 50;\n\n/** §3.1 applicability, verbatim scope (when to use / not to use a workflow). */\nconst WORKFLOW_APPLICABILITY_GUIDE =\n\t\"Workflow applicability — use a workflow only when ALL of these hold: (1) the goal needs at least 3 stages that can be accepted independently (you can write each stage's objective/acceptance); (2) the stages have a real dependency structure (branching or joins), not a plain sequence; (3) independent branches can be pushed in parallel — parallelism is a core payoff, purely linear multi-stage work belongs to goal/plan/todo; (4) the task spans multiple turns/days and is valuable enough to justify process overhead (definition, evidence recording, stage gates). Counterexamples that keep goal/plan/todo or direct execution: single-step tasks; two- or three-step linear tasks; exploratory work with no presettable dependency graph; one-shot questions. A workflow does not exclude the three layers — each stage still uses plan/todo for its execution checklist. Never escalate a small task to a workflow by default. When the user explicitly asks for workflow orchestration, still run this weighing: if the task genuinely misses the bar, skipping is fine — but say so in one line why you stayed on goal/plan/todo (an unexplained skip reads as ignoring the request); conversely, do not build one just because it was asked when the task is small (over-orchestration costs more than it saves). (2026-10-04 user ruling after a dogfood where an explicitly requested orchestration was silently skipped.)\";\n\nconst WORKFLOW_AUTHORING_GUIDE =\n\t\"Define each stage by a verifiable end state, not by a work list or a role: objective.statement says what must be achieved; acceptance items list the evidence that proves it, referenced from real tool output. Stop splitting at the first verifiable boundary — finer handoffs lose more to interface drift than they gain in drift protection.\";\n\nconst WORKFLOW_EXECUTION_GUIDE =\n\t\"After plan_set marks a stage's plan completed, run workflow_advance inspect to see unlocked stages and acceptance coverage. Record evidence (workflow_advance record-evidence) before completion is evaluated; lower-bound stages (no acceptance declared) need no record-evidence. When multiple stages are active, delegate one subagent per stage following stage.role and push them in parallel.\";\n\nconst WORKFLOW_CORRECTION_GUIDE =\n\t\"On user corrections: run workflow_advance inspect first to confirm the current state, then route by what changed — amend the stage spec (active/awaiting_review stages, amendNote required), append a stage, skip, retreat, or cancel — and report the adjustment and its impact range back to the user.\";\n\nconst WORKFLOW_REVIEW_GUIDE =\n\t\"Stages declared advance: 'review' stop at awaiting_review. Before approve you must present the result and its evidence to the user and get explicit confirmation — never approve on your own (the engine does not authorize; the discipline lives here).\";\n\nconst WORKFLOW_CONCURRENCY_GUIDE =\n\t\"Do not send workflow_set in the same parallel batch as plan_set: amends and completion evaluation race (completed freezes first; if the amend loses, recover via retreat).\";\n\nconst WORKFLOW_INCREMENTAL_DEMAND_GUIDE =\n\t\"When a new batch of requirements arrives (incremental features on top of shipped work), default to creating a NEW workflow to carry it — reuse the same goalId and keep the completed workflow's history intact; never pull a completed workflow back into motion. Retreat or append a corrective stage only when the new requirements overturn already completed output.\";\n\nconst WORKFLOW_EXECUTOR_GUIDE =\n\t\"When delegating a stage to a subagent, pass that subagent's session/task id as the executor field of record-evidence — it is recorded on the stage (advisory) so /workflow log shows who drove it.\";\n\nconst acceptanceSchema = Type.Object({\n\tid: Type.String({ description: \"Acceptance item id; evidence references this id\" }),\n\tstatement: Type.String({ description: \"Verifiable completion criterion for this item\" }),\n});\n\nconst stageSchema = Type.Object({\n\tid: Type.String({ description: \"Stable stage id, unique within the workflow\" }),\n\ttitle: Type.String({ description: \"Human-readable stage title\" }),\n\tobjective: Type.Object({\n\t\tstatement: Type.String({\n\t\t\tdescription: \"Verifiable end state this stage must reach (required, non-empty)\",\n\t\t}),\n\t\tacceptance: Type.Optional(\n\t\t\tType.Array(acceptanceSchema, {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Completion criteria items; when empty (or omitted) the lower bound is the referenced plan's completion evidence\",\n\t\t\t}),\n\t\t),\n\t}),\n\tdependsOn: Type.Optional(\n\t\tType.Array(Type.String(), { description: \"Hard prerequisite stage ids; empty array = root stage\" }),\n\t),\n\tplanId: Type.Optional(Type.String({ description: \"Soft reference to a plan id; missing = pure checkpoint stage\" })),\n\tkind: Type.Optional(\n\t\tType.Union([Type.Literal(\"read\"), Type.Literal(\"write\")], {\n\t\t\tdescription: 'Stage kind; write stages are scope-checked, default \"write\"',\n\t\t}),\n\t),\n\tscope: Type.Optional(\n\t\tType.Array(Type.String(), {\n\t\t\tdescription: \"Write scope tags (modules/dirs); missing = global (intersects every other write stage)\",\n\t\t}),\n\t),\n\trole: Type.Optional(\n\t\tType.String({ description: \"Delegation advice: role id for the subagent recommended to drive this stage\" }),\n\t),\n\tadvance: Type.Optional(\n\t\tType.Union([Type.Literal(\"auto\"), Type.Literal(\"review\")], {\n\t\t\tdescription:\n\t\t\t\t'\"auto\" completes on evaluation, \"review\" stops at awaiting_review (planId stages only), default \"auto\"',\n\t\t}),\n\t),\n\tretryLimit: Type.Optional(\n\t\tType.Integer({ description: \"In-place retry limit before onFail/workflow failure, default 2\", minimum: 0 }),\n\t),\n\tonFail: Type.Optional(\n\t\tType.String({ description: \"Explicit cross-stage retreat target (a topologically earlier stage id)\" }),\n\t),\n\tamendNote: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Correction reason; required when an active/awaiting_review stage's spec changes\",\n\t\t}),\n\t),\n});\n\nconst workflowSetSchema = Type.Object({\n\tid: Type.String({ description: \"Stable id of the workflow to create or update\" }),\n\ttitle: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Human-readable workflow title; required on create, omitting on update keeps the stored value\",\n\t\t}),\n\t),\n\trevision: Type.Integer({\n\t\tdescription: \"New revision counter; must be greater than the stored revision of an existing workflow\",\n\t\tminimum: 0,\n\t}),\n\tgoalId: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Soft reference to a goal id; the closure evidence summary is addressed to this goal. Omitting on update keeps the stored value\",\n\t\t}),\n\t),\n\tmaxRetreats: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription:\n\t\t\t\t\"Workflow lifetime retreat budget before failure, default 3. Omitting on update keeps the stored value\",\n\t\t\tminimum: 0,\n\t\t}),\n\t),\n\tmaxActiveStages: Type.Optional(\n\t\tType.Integer({\n\t\t\tdescription: \"Parallel activation cap, default 4. Omitting on update keeps the stored value\",\n\t\t\tminimum: 1,\n\t\t}),\n\t),\n\tstages: Type.Array(stageSchema, {\n\t\tdescription: \"Full stage list (whole submit, not a patch); the engine aligns runtime by stage id\",\n\t}),\n});\n\nconst workflowAdvanceSchema = Type.Object({\n\tworkflowId: Type.String({ description: \"Target workflow id\" }),\n\taction: Type.Union(\n\t\t[\n\t\t\tType.Literal(\"start\"),\n\t\t\tType.Literal(\"record-evidence\"),\n\t\t\tType.Literal(\"inspect\"),\n\t\t\tType.Literal(\"approve\"),\n\t\t\tType.Literal(\"reject\"),\n\t\t\tType.Literal(\"retreat\"),\n\t\t\tType.Literal(\"complete\"),\n\t\t\tType.Literal(\"skip\"),\n\t\t\tType.Literal(\"cancel\"),\n\t\t],\n\t\t{ description: \"Explicit action to perform\" },\n\t),\n\tstageId: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Stage id: the evidence/approve/reject/complete/skip target, or the retreat target stage to roll back to\",\n\t\t}),\n\t),\n\texecutor: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Advisory executor id (e.g. subagent session/task id) driving the stage; record-only, last-write-wins, never affects evaluation\",\n\t\t}),\n\t),\n\tevidence: Type.Optional(\n\t\tType.Array(\n\t\t\tType.Object({\n\t\t\t\tacceptanceId: Type.Optional(\n\t\t\t\t\tType.String({ description: \"Acceptance item id; required in acceptance mode\" }),\n\t\t\t\t),\n\t\t\t\tref: Type.String({ description: \"Evidence reference (tool output, file, artifact)\" }),\n\t\t\t}),\n\t\t\t{ description: \"Evidence items for record-evidence/approve/complete\" },\n\t\t),\n\t),\n\tnote: Type.Optional(\n\t\tType.String({ description: \"Optional note recorded in the transition detail (reject/retreat/skip/cancel)\" }),\n\t),\n});\n\nconst ADVANCE_ACTIONS: readonly string[] = [\n\t\"start\",\n\t\"record-evidence\",\n\t\"inspect\",\n\t\"approve\",\n\t\"reject\",\n\t\"retreat\",\n\t\"complete\",\n\t\"skip\",\n\t\"cancel\",\n];\n\nfunction objectInput(first: unknown, second: unknown): Record<string, unknown> {\n\tconst value = second === undefined ? first : second;\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\tthrow new Error(\"Workflow input must be an object\");\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction parseAdvanceInput(value: Record<string, unknown>): AdvanceInput {\n\tconst workflowId = asText(value.workflowId, \"workflowId\");\n\tconst action = value.action;\n\tif (typeof action !== \"string\" || !ADVANCE_ACTIONS.includes(action)) {\n\t\tthrow new Error(`action must be one of ${ADVANCE_ACTIONS.join(\", \")}`);\n\t}\n\tconst stageId = value.stageId === undefined ? undefined : asText(value.stageId, \"stageId\");\n\tconst note = value.note === undefined ? undefined : asText(value.note, \"note\");\n\tconst executor = value.executor === undefined ? undefined : asText(value.executor, \"executor\");\n\tlet evidence: EvidenceItem[] | undefined;\n\tif (value.evidence !== undefined) {\n\t\tif (!Array.isArray(value.evidence)) throw new Error(\"evidence must be an array of {acceptanceId?, ref}\");\n\t\tevidence = value.evidence.map((item) => {\n\t\t\tconst entry = asObject(item, \"evidence item\");\n\t\t\tconst ref = asText(entry.ref, \"evidence item ref\");\n\t\t\tconst acceptanceId =\n\t\t\t\tentry.acceptanceId === undefined ? undefined : asText(entry.acceptanceId, \"evidence item acceptanceId\");\n\t\t\treturn acceptanceId === undefined ? { ref } : { acceptanceId, ref };\n\t\t});\n\t}\n\treturn {\n\t\tworkflowId,\n\t\taction: action as AdvanceAction,\n\t\t...(stageId !== undefined ? { stageId } : {}),\n\t\t...(evidence !== undefined ? { evidence } : {}),\n\t\t...(note !== undefined ? { note } : {}),\n\t\t...(executor !== undefined ? { executor } : {}),\n\t};\n}\n\nfunction loadState(api: CapabilityAPI): WorkflowState {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tconst entry = [...entries].reverse().find((item) => item.customType === WORKFLOW_STATE_ENTRY);\n\tif (!entry?.data || typeof entry.data !== \"object\") return { workflows: [] };\n\tconst data = entry.data as Partial<WorkflowState>;\n\treturn { workflows: Array.isArray(data.workflows) ? [...data.workflows] : [] };\n}\n\n/**\n * Plan facts read from the current branch's LATEST `plan.state` snapshot (the\n * same latest-snapshot semantics as the plan plugin's own loadState). The\n * plan.changed payload is never trusted for state — only as a trigger.\n *\n * Uncached by design: identity-keyed caching collided across test hosts and\n * hooking invalidation into appendEntry crosses the persistence boundary; the\n * O(stages x entries) rescan is the price of snapshot-driven truth. Revisit\n * only with a per-run cache channel if long-session profiling demands it.\n */\nfunction loadPlansSnapshot(api: CapabilityAPI): Map<string, PlanFacts> {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tfor (let index = entries.length - 1; index >= 0; index -= 1) {\n\t\tconst entry = entries[index] as { customType?: string; data?: unknown } | undefined;\n\t\tif (entry?.customType !== planStateEntry) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return new Map();\n\t\tconst plans = (data as { plans?: unknown }).plans;\n\t\tif (!Array.isArray(plans)) return new Map();\n\t\tconst facts = new Map<string, PlanFacts>();\n\t\tfor (const plan of plans) {\n\t\t\tif (!plan || typeof plan !== \"object\") continue;\n\t\t\tconst record = plan as { id?: unknown; status?: unknown; evidence?: unknown };\n\t\t\tif (typeof record.id !== \"string\") continue;\n\t\t\tconst status =\n\t\t\t\trecord.status === \"draft\" ||\n\t\t\t\trecord.status === \"active\" ||\n\t\t\t\trecord.status === \"completed\" ||\n\t\t\t\trecord.status === \"cancelled\"\n\t\t\t\t\t? record.status\n\t\t\t\t\t: \"draft\";\n\t\t\tconst evidence = Array.isArray(record.evidence)\n\t\t\t\t? record.evidence.filter((item): item is string => typeof item === \"string\")\n\t\t\t\t: [];\n\t\t\tfacts.set(record.id, { id: record.id, status, evidence });\n\t\t}\n\t\treturn facts;\n\t}\n\treturn new Map();\n}\n\n/** Whether the latest `<entryType>` snapshot on the branch knows `id` in its `idsField` array (advisory, #10). */\nfunction idKnownToSession(api: CapabilityAPI, entryType: string, idsField: string, id: string): boolean {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tfor (let index = entries.length - 1; index >= 0; index -= 1) {\n\t\tconst entry = entries[index];\n\t\tif (entry.customType !== entryType) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return false;\n\t\tconst records = (data as Record<string, unknown>)[idsField];\n\t\tif (!Array.isArray(records)) return false;\n\t\treturn records.some(\n\t\t\t(record) => record !== null && typeof record === \"object\" && (record as { id?: unknown }).id === id,\n\t\t);\n\t}\n\treturn false;\n}\n\n/**\n * Seeds the held-dedup view (§5 总则 5) from the branch audit stream: the\n * last transition per stage, whatever its code — a repeat held emission is\n * dropped only when it matches that last code and detail.\n */\nfunction buildLastStageTransition(api: CapabilityAPI): Map<string, { code: TransitionReasonCode; detail?: string }> {\n\tconst map = new Map<string, { code: TransitionReasonCode; detail?: string }>();\n\tfor (const entry of api.session?.getBranchEntries() ?? []) {\n\t\tif (entry.customType !== WORKFLOW_TRANSITION_ENTRY) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) continue;\n\t\tconst record = data as Partial<WorkflowTransitionEntry>;\n\t\tif (typeof record.workflowId !== \"string\" || typeof record.stageId !== \"string\") continue;\n\t\tconst reason = record.reason as { code?: unknown; detail?: unknown } | undefined;\n\t\tif (!reason || typeof reason !== \"object\" || typeof reason.code !== \"string\") continue;\n\t\tmap.set(`${record.workflowId}::${record.stageId}`, {\n\t\t\tcode: reason.code as TransitionReasonCode,\n\t\t\tdetail: typeof reason.detail === \"string\" ? reason.detail : undefined,\n\t\t});\n\t}\n\treturn map;\n}\n\n/**\n * Role ids from the current branch's latest READABLE `subagent.roles.state`\n * snapshot (D-081 item 7). Fallback is symmetric with the authoritative reader\n * (subagent-delegate `readSessionRoles`): a snapshot-level corruption (data not\n * an object / version mismatch / roles not an array) steps back to the previous\n * readable snapshot, and only a fully unreadable table degrades to the empty\n * set. Individual malformed role records are skipped — including ids that do\n * not match `roleIdPattern` — keeping the remaining ids of an otherwise\n * readable snapshot. Silent by contract: the\n * subagent-delegate plugin owns the degradation warning on its side, and a\n * second report here would double-warn for the same fact — this side only\n * tolerates.\n */\nfunction loadSessionRoleIds(api: CapabilityAPI): Set<string> {\n\tconst entries = api.session?.getBranchEntries() ?? [];\n\tfor (let index = entries.length - 1; index >= 0; index -= 1) {\n\t\tconst entry = entries[index];\n\t\tif (entry.customType !== subagentRoleStateEntry) continue;\n\t\tconst data = entry.data;\n\t\tif (!data || typeof data !== \"object\" || Array.isArray(data)) continue;\n\t\tconst snapshot = data as { version?: unknown; roles?: unknown };\n\t\tif (snapshot.version !== 1 || !Array.isArray(snapshot.roles)) continue;\n\t\tconst known = new Set<string>();\n\t\tfor (const role of snapshot.roles) {\n\t\t\tif (!role || typeof role !== \"object\" || Array.isArray(role)) continue;\n\t\t\tconst id = (role as { id?: unknown }).id;\n\t\t\tif (typeof id === \"string\" && roleIdPattern.test(id)) known.add(id);\n\t\t}\n\t\treturn known;\n\t}\n\treturn new Set();\n}\n\n/** Soft-reference warnings for the tool response only (never the persisted state, #10). */\nfunction collectSoftReferenceWarnings(\n\tapi: CapabilityAPI,\n\tgoalId: string | undefined,\n\tplanIds: { stageId: string; planId: string }[],\n\troles: { stageId: string; role: string }[],\n): string[] {\n\tconst warnings: string[] = [];\n\tif (goalId !== undefined && !idKnownToSession(api, goalStateEntry, \"goals\", goalId)) {\n\t\twarnings.push(\n\t\t\t`goalId \"${goalId}\" does not match any goal in this session's goal state; the workflow keeps the soft reference`,\n\t\t);\n\t}\n\tfor (const item of planIds) {\n\t\tif (!idKnownToSession(api, planStateEntry, \"plans\", item.planId)) {\n\t\t\twarnings.push(\n\t\t\t\t`stage ${item.stageId}: planId \"${item.planId}\" does not match any plan in this session's plan state; ` +\n\t\t\t\t\t\"the stage stays held until a reevaluation finds the plan\",\n\t\t\t);\n\t\t}\n\t}\n\t// Two verifiable role sources (D-081 item 7): the host's suite role\n\t// associations (disk roles) plus this branch's session role table. A host\n\t// without the listing AND an empty session table verifies nothing, so the\n\t// advice stays unverified — no warning (缺面不告警). An explicitly empty\n\t// association face ([]) still warns for every role.\n\tconst associated = api.host?.roleAssociations;\n\tconst sessionRoleIds = roles.length > 0 ? loadSessionRoleIds(api) : new Set<string>();\n\tconst merged = new Set([...(associated ?? []), ...sessionRoleIds]);\n\tfor (const item of roles) {\n\t\tif ((associated !== undefined || sessionRoleIds.size > 0) && !merged.has(item.role)) {\n\t\t\twarnings.push(\n\t\t\t\t`stage ${item.stageId}: role \"${item.role}\" is neither associated with the active suite nor ` +\n\t\t\t\t\t\"registered in this session; delegation advice treated as unspecified — register it with \" +\n\t\t\t\t\t\"subagent_role_set or add it to the suite's roles associations\",\n\t\t\t);\n\t\t}\n\t}\n\treturn warnings;\n}\n\ninterface WorkflowToolResponse extends WorkflowState {\n\tclosures?: WorkflowClosure[];\n\tinspections?: WorkflowInspection[];\n\twarning?: string;\n}\n\nexport function createCapability(\n\tapi: CapabilityAPI,\n): Promise<readonly DisposableRegistration[]> | readonly DisposableRegistration[] {\n\tlet state = loadState(api);\n\tconst deps: EngineDeps = {\n\t\tgetPlan: (planId) => loadPlansSnapshot(api).get(planId),\n\t\tlastStageTransition: buildLastStageTransition(api),\n\t};\n\t// Shared structural renderResult (render.ts): the host TUI gets the\n\t// panorama face for both tools' JSON-serialized responses.\n\tconst toolResultRenderer = createWorkflowToolResultRenderer(deps);\n\n\t// Persistence boundary (§4): audit entries first, then the full snapshot —\n\t// a crash mid-batch replays from the older snapshot while the audit stays\n\t// intact. appendEntry failures propagate unchanged (§5 总则 5: no swallow);\n\t// publish rejections surface to the tool caller (or are contained on the\n\t// load/subscriber paths below).\n\tconst persistRun = (run: EngineRun): void => {\n\t\tfor (const transition of run.transitions) {\n\t\t\tapi.session?.appendEntry(WORKFLOW_TRANSITION_ENTRY, transition);\n\t\t}\n\t\tapi.session?.appendEntry(WORKFLOW_STATE_ENTRY, run.state);\n\t};\n\tconst announce = (run: EngineRun): Promise<unknown> =>\n\t\tapi.publish({ type: \"workflow.changed\", version: 1, correlationId: WORKFLOW_STATE_ENTRY, data: run.state });\n\n\t/**\n\t * §5 总则 3 containment: land the failure in diagnostics + a handler.error\n\t * transition (detail = original error text) and never rethrow. Best-effort\n\t * — an already-failing boundary must not turn isolation into a second crash.\n\t */\n\tconst quarantineEngineError = (error: unknown, trigger: WorkflowTransitionTrigger): void => {\n\t\ttry {\n\t\t\tif (state.workflows.length === 0) return;\n\t\t\tconst original = error instanceof Error ? error.message : String(error);\n\t\t\tconst text = `${trigger} handler failed: ${original}`;\n\t\t\tconst workflows = state.workflows.map((workflow) => structuredClone(workflow));\n\t\t\tconst transitions: WorkflowTransitionEntry[] = [];\n\t\t\tfor (const workflow of workflows) {\n\t\t\t\tif (workflow.diagnostics.length >= DIAGNOSTICS_CAP) workflow.diagnostics.shift();\n\t\t\t\tworkflow.diagnostics.push(text);\n\t\t\t\tworkflow.revision += 1;\n\t\t\t\ttransitions.push({\n\t\t\t\t\tworkflowId: workflow.id,\n\t\t\t\t\tfrom: workflow.status,\n\t\t\t\t\tto: workflow.status,\n\t\t\t\t\ttrigger,\n\t\t\t\t\treason: { code: \"handler.error\", message: text, detail: original },\n\t\t\t\t\trevision: workflow.revision,\n\t\t\t\t});\n\t\t\t}\n\t\t\tstate = { workflows };\n\t\t\tfor (const transition of transitions) {\n\t\t\t\tapi.session?.appendEntry(WORKFLOW_TRANSITION_ENTRY, transition);\n\t\t\t}\n\t\t\tapi.session?.appendEntry(WORKFLOW_STATE_ENTRY, state);\n\t\t\tvoid api\n\t\t\t\t.publish({ type: \"workflow.changed\", version: 1, correlationId: WORKFLOW_STATE_ENTRY, data: state })\n\t\t\t\t.catch(() => undefined);\n\t\t} catch {\n\t\t\t// Containment is best-effort by design.\n\t\t}\n\t};\n\n\t// §5 rule 8 recovery: state replayed from workflow.state, then the\n\t// load-time reevaluation (总则 1 fallback moment #1). Runs synchronously at\n\t// factory time; failures are contained, never blocking plugin load.\n\ttry {\n\t\tconst run = reevaluateAll(state, deps, \"load-reeval\");\n\t\tif (run.transitions.length > 0) {\n\t\t\tstate = run.state;\n\t\t\tpersistRun(run);\n\t\t\tvoid announce(run).catch(() => undefined);\n\t\t}\n\t} catch (error) {\n\t\tquarantineEngineError(error, \"load-reeval\");\n\t}\n\n\tconst setTool = api.registerTool({\n\t\tname: \"workflow_set\",\n\t\tlabel: \"Set workflow\",\n\t\tpromptGuidelines: [\n\t\t\tWORKFLOW_APPLICABILITY_GUIDE,\n\t\t\tWORKFLOW_AUTHORING_GUIDE,\n\t\t\tWORKFLOW_EXECUTION_GUIDE,\n\t\t\tWORKFLOW_CORRECTION_GUIDE,\n\t\t\tWORKFLOW_REVIEW_GUIDE,\n\t\t\tWORKFLOW_CONCURRENCY_GUIDE,\n\t\t\tWORKFLOW_INCREMENTAL_DEMAND_GUIDE,\n\t\t\tWORKFLOW_EXECUTOR_GUIDE,\n\t\t],\n\t\tdescription:\n\t\t\t\"Create or update a workflow: a stage dependency graph (DAG) with per-stage objectives, acceptance evidence, and soft goal/plan references. On update, an omitted workflow-level optional field (title/goalId/maxRetreats/maxActiveStages) keeps its stored value; stages are always submitted in full\",\n\t\tparameters: workflowSetSchema,\n\t\trenderResult: toolResultRenderer,\n\t\texecute: async (first: unknown, second?: unknown) => {\n\t\t\tconst parsed = parseWorkflowSetInput(objectInput(first, second));\n\t\t\tvalidateStageGraph(parsed.stages);\n\t\t\tconst current = state.workflows.find((workflow) => workflow.id === parsed.id);\n\t\t\tif (current !== undefined && parsed.revision <= current.revision) {\n\t\t\t\tthrow new Error(`stale workflow revision: ${parsed.id}`);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tcurrent !== undefined &&\n\t\t\t\t(current.status === \"completed\" || current.status === \"failed\" || current.status === \"cancelled\")\n\t\t\t) {\n\t\t\t\tthrow new Error(`workflow is terminal and cannot be updated: ${parsed.id}`);\n\t\t\t}\n\t\t\tconst diff = planWorkflowSpecUpdate(current, parsed);\n\t\t\tconst run = applyWorkflowSet(state, parsed, diff, deps);\n\t\t\tstate = run.state;\n\t\t\tif (run.transitions.length > 0 || diff.created) persistRun(run);\n\t\t\tif (run.transitions.length > 0 || diff.created) await announce(run);\n\t\t\tconst response: WorkflowToolResponse = { ...state };\n\t\t\tif (run.closures.length > 0) response.closures = run.closures;\n\t\t\tconst warnings = collectSoftReferenceWarnings(\n\t\t\t\tapi,\n\t\t\t\tparsed.goalId,\n\t\t\t\tparsed.stages.flatMap((stage) =>\n\t\t\t\t\tstage.planId !== undefined ? [{ stageId: stage.id, planId: stage.planId }] : [],\n\t\t\t\t),\n\t\t\t\tparsed.stages.flatMap((stage) =>\n\t\t\t\t\tstage.role !== undefined ? [{ stageId: stage.id, role: stage.role }] : [],\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (warnings.length > 0) response.warning = warnings.join(\"; \");\n\t\t\treturn response;\n\t\t},\n\t});\n\n\tconst advanceTool = api.registerTool({\n\t\tname: \"workflow_advance\",\n\t\tlabel: \"Advance workflow\",\n\t\tdescription:\n\t\t\t\"Drive a workflow: start it, record acceptance evidence, inspect waiting conditions and coverage, approve/reject reviews, retreat, complete checkpoints, skip stages, or cancel\",\n\t\tparameters: workflowAdvanceSchema,\n\t\trenderResult: toolResultRenderer,\n\t\texecute: async (first: unknown, second?: unknown) => {\n\t\t\tconst parsed = parseAdvanceInput(objectInput(first, second));\n\t\t\t// Illegal conversions throw structured errors (§5 rule 6) surfaced\n\t\t\t// as tool errors unchanged; legal ones return the full snapshot\n\t\t\t// (the goal_set full-state response pattern).\n\t\t\tconst run = applyAdvance(state, parsed, deps);\n\t\t\tstate = run.state;\n\t\t\tif (run.transitions.length > 0) {\n\t\t\t\tpersistRun(run);\n\t\t\t\tawait announce(run);\n\t\t\t}\n\t\t\tconst response: WorkflowToolResponse = { ...state };\n\t\t\tif (parsed.action === \"inspect\") {\n\t\t\t\t// §5 rule 6 single-workflow semantics: applyAdvance has already\n\t\t\t\t// rejected unknown ids, so the target always resolves here.\n\t\t\t\tconst target = state.workflows.find((workflow) => workflow.id === parsed.workflowId);\n\t\t\t\tif (target !== undefined) response.inspections = [inspectWorkflow(target, deps)];\n\t\t\t}\n\t\t\tif (run.closures.length > 0) response.closures = run.closures;\n\t\t\treturn response;\n\t\t},\n\t});\n\n\tconst workflowCommand = api.registerCommand({\n\t\tname: \"workflow\",\n\t\tdescription: \"Workflow overview: dependency tree and status (default), why it is stopped, or the transition log\",\n\t\targumentHint: \"[view | why | log [N]]\",\n\t\texecute: async (args: string) => {\n\t\t\ttry {\n\t\t\t\tconst trimmed = args.trim();\n\t\t\t\tif (trimmed === \"\" || trimmed === \"view\") {\n\t\t\t\t\treturn { status: \"success\" as const, message: renderPanorama(state, deps) };\n\t\t\t\t}\n\t\t\t\tif (trimmed === \"why\") {\n\t\t\t\t\treturn { status: \"success\" as const, message: renderWhy(state, deps) };\n\t\t\t\t}\n\t\t\t\tconst logMatch = /^log(?:\\s+(\\d+))?$/.exec(trimmed);\n\t\t\t\tif (logMatch !== null) {\n\t\t\t\t\tconst limit = logMatch[1] === undefined ? 20 : Math.max(1, Number.parseInt(logMatch[1], 10));\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: \"success\" as const,\n\t\t\t\t\t\tmessage: renderLog(api.session?.getBranchEntries() ?? [], limit),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn { status: \"error\" as const, message: \"Usage: /workflow [view | why | log [N]]\" };\n\t\t\t} catch (error) {\n\t\t\t\t// A slash command must not crash the host (memory /promote precedent).\n\t\t\t\treturn { status: \"error\" as const, message: error instanceof Error ? error.message : String(error) };\n\t\t\t}\n\t\t},\n\t});\n\n\t// The repository's first event subscription (§7). The payload is ignored:\n\t// reevaluation always reads the current branch entries (§5 总则 1).\n\tconst planChanged = api.subscribe(\"plan.changed\", async () => {\n\t\ttry {\n\t\t\tconst run = reevaluateAll(state, deps, \"plan.changed\");\n\t\t\tif (run.transitions.length === 0) return;\n\t\t\tstate = run.state;\n\t\t\tpersistRun(run);\n\t\t\tawait announce(run);\n\t\t} catch (error) {\n\t\t\tquarantineEngineError(error, \"plan.changed\");\n\t\t}\n\t});\n\n\treturn [setTool, advanceTool, workflowCommand, planChanged];\n}\n"]}
package/dist/render.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * /workflow command text rendering (§6 text face, §9 L1/L3): panorama
3
- * (indented dependency tree + per-stage status + waiting conditions +
4
- * parallel active count + retreat count + active diagnostics), why (one line
5
- * per waiting stage), and log (transition stream, newest first, reason codes
6
- * and amend records included). Plain text only — every host shape can render
7
- * it, no TUI required.
3
+ * (indented dependency tree + per-stage status + declared delegation role +
4
+ * waiting conditions + parallel active count + retreat count + active
5
+ * diagnostics), why (one line per waiting stage, with the declared role), and
6
+ * log (transition stream, newest first, reason codes and amend records
7
+ * included). Plain text only — every host shape can render it, no TUI
8
+ * required.
8
9
  *
9
10
  * Also hosts the structural renderResult face for workflow_set /
10
11
  * workflow_advance tool results: the host TUI serializes the tool response to
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAEN,KAAK,UAAU,EAGf,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAKN,KAAK,aAAa,EAElB,MAAM,YAAY,CAAC;AAmCpB;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CA4BlH;AAED,sGAAqG;AACrG,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAsBxE;AAgBD,sHAAqH;AACrH,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAgB5G;AAMD,yFAAyF;AACzF,UAAU,sBAAsB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,cAAc;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,sBAAsB,EAAE,CAAC;CACpD;AAED,UAAU,uBAAuB;IAChC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAED,UAAU,SAAS;IAClB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACxC;AAED,UAAU,iBAAiB;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CACjC;AAqCD;;;;;GAKG;AACH,cAAM,sBAAsB;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,KAAK,CAAY;IAEzB,YAAY,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAGzC;IAED,0EAA0E;IAC1E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAI3C;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAa9B;IAED,UAAU,IAAI,IAAI,CAEjB;CACD;AAED,gFAA8E;AAC9E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,CAQ3E;AAED,sGAAoG;AACpG,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,kBAAkB,EAAE,GAAG,MAAM,CAgBpF;AAwCD,oEAAoE;AACpE,MAAM,MAAM,0BAA0B,GAAG,CACxC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,iBAAiB,KACtB,sBAAsB,GAAG,SAAS,CAAC;AAExC;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,UAAU,GAAG,0BAA0B,CAkC7F","sourcesContent":["/**\n * /workflow command text rendering (§6 text face, §9 L1/L3): panorama\n * (indented dependency tree + per-stage status + waiting conditions +\n * parallel active count + retreat count + active diagnostics), why (one line\n * per waiting stage), and log (transition stream, newest first, reason codes\n * and amend records included). Plain text only — every host shape can render\n * it, no TUI required.\n *\n * Also hosts the structural renderResult face for workflow_set /\n * workflow_advance tool results: the host TUI serializes the tool response to\n * JSON text and hands it back here; this module re-renders it as the panorama\n * (collapsed: no diagnostics; expanded: diagnostics + closures + inspections)\n * through the structural Component shape — no TUI package import (the plugin\n * hosting boundary).\n */\n\nimport {\n\tcomputeWaitingOn,\n\ttype EngineDeps,\n\tmaxActiveOf,\n\tmaxRetreatsOf,\n\ttype WorkflowClosure,\n\ttype WorkflowInspection,\n} from \"./engine.ts\";\nimport {\n\ttype StageRuntime,\n\ttype StageSpec,\n\tWORKFLOW_TRANSITION_ENTRY,\n\ttype WorkflowRecord,\n\ttype WorkflowState,\n\ttype WorkflowTransitionEntry,\n} from \"./types.ts\";\n\nconst runtimeOf = (workflow: WorkflowRecord, stageId: string): StageRuntime | undefined =>\n\tworkflow.runtime.find((item) => item.stageId === stageId);\n\nfunction renderStageNode(\n\tworkflow: WorkflowRecord,\n\tspec: StageSpec,\n\tdepth: number,\n\tdeps: EngineDeps,\n\tlines: string[],\n\tvisited: Set<string>,\n): void {\n\tconst pad = \" \".repeat(depth + 1);\n\tif (visited.has(spec.id)) {\n\t\tlines.push(`${pad}-> ${spec.id} (shown above)`);\n\t\treturn;\n\t}\n\tvisited.add(spec.id);\n\tconst runtime = runtimeOf(workflow, spec.id);\n\tconst bits = [`- ${spec.id} [${runtime?.status ?? \"pending\"}] ${spec.title}`];\n\tif (runtime !== undefined) {\n\t\tconst waiting = computeWaitingOn(workflow, spec, runtime, deps);\n\t\tif (waiting !== null) bits.push(`— waiting: ${waiting}`);\n\t\tif (runtime.attempts > 0) bits.push(`· attempts ${runtime.attempts}`);\n\t\tif (runtime.evidence.length > 0) bits.push(`· evidence ${runtime.evidence.length}`);\n\t}\n\tlines.push(`${pad}${bits.join(\" \")}`);\n\t// DAG tree: children are dependents in declaration order; diamond edges\n\t// collapse into a back-reference line instead of duplicating the subtree.\n\tfor (const child of workflow.stages.filter((item) => item.dependsOn.includes(spec.id))) {\n\t\trenderStageNode(workflow, child, depth + 1, deps, lines, visited);\n\t}\n}\n\n/**\n * Panorama: one block per workflow. `options.diagnostics: false` drops the\n * diagnostics block (the collapsed tool-result face); the default keeps the\n * /workflow command output unchanged.\n */\nexport function renderPanorama(state: WorkflowState, deps: EngineDeps, options?: { diagnostics?: boolean }): string {\n\tif (state.workflows.length === 0) {\n\t\treturn \"no workflows in this session; create one with workflow_set\";\n\t}\n\treturn state.workflows\n\t\t.map((workflow) => {\n\t\t\tconst lines: string[] = [];\n\t\t\tconst active = workflow.runtime.filter((item) => item.status === \"active\").length;\n\t\t\tlines.push(\n\t\t\t\t`workflow \"${workflow.title}\" (${workflow.id}) — ${workflow.status} · revision ${workflow.revision} · active ${active}/${maxActiveOf(workflow)} · retreats ${workflow.retreatCount}/${maxRetreatsOf(workflow)}`,\n\t\t\t);\n\t\t\tif (workflow.goalId !== undefined) lines.push(` goal: ${workflow.goalId}`);\n\t\t\tconst visited = new Set<string>();\n\t\t\tfor (const spec of workflow.stages.filter((item) => item.dependsOn.length === 0)) {\n\t\t\t\trenderStageNode(workflow, spec, 0, deps, lines, visited);\n\t\t\t}\n\t\t\t// Stages unreachable from a root (defensive: cannot happen on a\n\t\t\t// validated acyclic graph) still render, top-level.\n\t\t\tfor (const spec of workflow.stages) {\n\t\t\t\tif (!visited.has(spec.id)) renderStageNode(workflow, spec, 0, deps, lines, visited);\n\t\t\t}\n\t\t\tif (options?.diagnostics !== false && workflow.diagnostics.length > 0) {\n\t\t\t\tlines.push(\" diagnostics:\");\n\t\t\t\tfor (const diagnostic of workflow.diagnostics) lines.push(` - ${diagnostic}`);\n\t\t\t}\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\n/** Why it is stopped: every stage with an unmet wait condition, one line each (§6 /workflow why). */\nexport function renderWhy(state: WorkflowState, deps: EngineDeps): string {\n\tconst pending = state.workflows.filter(\n\t\t(workflow) => workflow.status !== \"completed\" && workflow.status !== \"cancelled\",\n\t);\n\tif (pending.length === 0) return \"no open workflows; nothing is waiting\";\n\treturn pending\n\t\t.map((workflow) => {\n\t\t\tconst lines = [`workflow ${workflow.id} — ${workflow.status}`];\n\t\t\tlet any = false;\n\t\t\tfor (const spec of workflow.stages) {\n\t\t\t\tconst runtime = runtimeOf(workflow, spec.id);\n\t\t\t\tif (runtime === undefined) continue;\n\t\t\t\tconst waiting = computeWaitingOn(workflow, spec, runtime, deps);\n\t\t\t\tif (waiting === null) continue;\n\t\t\t\tlines.push(` - ${spec.id} [${runtime.status}] waiting: ${waiting}`);\n\t\t\t\tany = true;\n\t\t\t}\n\t\t\tif (!any) lines.push(\" no stage is waiting on an unmet condition\");\n\t\t\tfor (const diagnostic of workflow.diagnostics) lines.push(` ! ${diagnostic}`);\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\nfunction isTransitionEntry(data: unknown): data is WorkflowTransitionEntry {\n\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return false;\n\tconst record = data as Partial<WorkflowTransitionEntry>;\n\treturn (\n\t\ttypeof record.workflowId === \"string\" &&\n\t\ttypeof record.from === \"string\" &&\n\t\ttypeof record.to === \"string\" &&\n\t\ttypeof record.revision === \"number\" &&\n\t\trecord.reason !== undefined &&\n\t\ttypeof record.reason === \"object\" &&\n\t\ttypeof (record.reason as { code?: unknown }).code === \"string\"\n\t);\n}\n\n/** Transition stream, newest first (§8.1 \"which conversion went wrong\"): revision anchor + trigger + reason code. */\nexport function renderLog(entries: readonly { customType?: string; data?: unknown }[], limit: number): string {\n\tconst transitions: WorkflowTransitionEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.customType !== WORKFLOW_TRANSITION_ENTRY) continue;\n\t\tif (isTransitionEntry(entry.data)) transitions.push(entry.data);\n\t}\n\tif (transitions.length === 0) return \"no workflow transitions recorded\";\n\treturn transitions\n\t\t.slice(-limit)\n\t\t.reverse()\n\t\t.map((transition) => {\n\t\t\tconst stage = transition.stageId !== undefined ? `/${transition.stageId}` : \"\";\n\t\t\tconst detail = transition.reason.detail !== undefined ? ` — ${transition.reason.detail}` : \"\";\n\t\t\treturn `#${transition.revision} ${transition.workflowId}${stage} ${transition.from} -> ${transition.to} [${transition.trigger}] ${transition.reason.code}: ${transition.reason.message}${detail}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Structural renderResult (tool-result panorama face)\n// ---------------------------------------------------------------------------\n\n/** Structural host faces: declared locally so this module stays free of host imports. */\ninterface ToolResultTextPartLike {\n\treadonly type: string;\n\treadonly text?: string;\n}\n\ninterface ToolResultLike {\n\treadonly content: readonly ToolResultTextPartLike[];\n}\n\ninterface RenderResultOptionsLike {\n\treadonly expanded: boolean;\n\treadonly isPartial: boolean;\n}\n\ninterface ThemeLike {\n\tfg(color: string, text: string): string;\n}\n\ninterface RenderContextLike {\n\treadonly lastComponent?: unknown;\n}\n\n/**\n * Simplified East Asian fullwidth detection (wcwidth wide ranges, subset):\n * code points in these ranges occupy 2 terminal cells.\n */\nconst isWideCodePoint = (codePoint: number): boolean =>\n\t(codePoint >= 0x1100 && codePoint <= 0x115f) ||\n\t(codePoint >= 0x2e80 && codePoint <= 0xa4cf) ||\n\t(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||\n\t(codePoint >= 0xf900 && codePoint <= 0xfaff) ||\n\t(codePoint >= 0xfe30 && codePoint <= 0xfe4f) ||\n\t(codePoint >= 0xff00 && codePoint <= 0xff60) ||\n\t(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||\n\t(codePoint >= 0x20000 && codePoint <= 0x3fffd);\n\n/** Fold one logical line to `width` cells; wide code points count 2. */\nfunction foldLine(line: string, width: number): string[] {\n\tif (width <= 0) return [line];\n\tconst folded: string[] = [];\n\tlet current = \"\";\n\tlet used = 0;\n\tfor (const char of line) {\n\t\tconst cells = isWideCodePoint(char.codePointAt(0) ?? 0) ? 2 : 1;\n\t\tif (current !== \"\" && used + cells > width) {\n\t\t\tfolded.push(current);\n\t\t\tcurrent = char;\n\t\t\tused = cells;\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t\tused += cells;\n\t\t}\n\t}\n\tfolded.push(current);\n\treturn folded;\n}\n\n/**\n * Structural tool-result component (host Component shape without importing\n * the TUI package): holds plain text plus a theme, colors `warning: ` lines\n * with the warning color and everything else as tool output, folds lines to\n * the render width, and caches the folded output per width.\n */\nclass WorkflowToolResultText {\n\tprivate readonly cache = new Map<number, string[]>();\n\tprivate text: string;\n\tprivate theme: ThemeLike;\n\n\tconstructor(text: string, theme: ThemeLike) {\n\t\tthis.text = text;\n\t\tthis.theme = theme;\n\t}\n\n\t/** New payload (or theme) on component reuse: drop every cached width. */\n\tupdate(text: string, theme: ThemeLike): void {\n\t\tthis.text = text;\n\t\tthis.theme = theme;\n\t\tthis.cache.clear();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst cached = this.cache.get(width);\n\t\tif (cached !== undefined) return cached;\n\t\tconst lines: string[] = [];\n\t\tfor (const rawLine of this.text.split(\"\\n\")) {\n\t\t\tfor (const folded of foldLine(rawLine, width)) {\n\t\t\t\tlines.push(\n\t\t\t\t\tfolded.startsWith(\"warning: \") ? this.theme.fg(\"warning\", folded) : this.theme.fg(\"toolOutput\", folded),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthis.cache.set(width, lines);\n\t\treturn lines;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cache.clear();\n\t}\n}\n\n/** Closures: one line per closure — the goal-achievement evidence summary. */\nexport function renderClosures(closures: readonly WorkflowClosure[]): string {\n\treturn closures\n\t\t.map((closure) => {\n\t\t\tconst goal = closure.goalId !== undefined ? ` (goal ${closure.goalId})` : \"\";\n\t\t\tconst evidence = closure.evidence.map((item) => `${item.stageId}/${item.acceptanceId}=${item.ref}`).join(\", \");\n\t\t\treturn `closure ${closure.workflowId}${goal}: ${evidence}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\n/** Inspections: one block per inspection — per-stage waiting conditions and acceptance coverage. */\nexport function renderInspections(inspections: readonly WorkflowInspection[]): string {\n\treturn inspections\n\t\t.map((inspection) => {\n\t\t\tconst lines = [`inspect ${inspection.workflowId} [${inspection.status}]`];\n\t\t\tfor (const stage of inspection.stages) {\n\t\t\t\tlet line = ` ${stage.stageId} [${stage.status}] attempts ${stage.attempts}`;\n\t\t\t\tif (stage.waitingOn !== null) line += ` — waiting: ${stage.waitingOn}`;\n\t\t\t\tif (stage.planId !== undefined) line += ` · plan ${stage.planId} (${stage.planStatus ?? \"unknown\"})`;\n\t\t\t\tlines.push(line);\n\t\t\t\tfor (const item of stage.acceptance) {\n\t\t\t\t\tlines.push(` ${item.id}: ${item.covered ? `covered (${item.refs.length} refs)` : \"missing\"}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\n/** Parsed tool response view: the state base plus the optional response-only sections. */\ninterface WorkflowToolResponseView {\n\tstate: WorkflowState;\n\tclosures?: WorkflowClosure[];\n\tinspections?: WorkflowInspection[];\n\twarning?: string;\n}\n\n/**\n * Parses the host-serialized tool response. Only the `workflows` array key is\n * required (the state base); anything else is not a workflow tool response and\n * the caller falls back to rendering the raw text. Present-but-mistyped extra\n * fields (warning/closures/inspections) are shape mismatches and also reject\n * the parse (the todo/goal/plan gate semantics). Malformed records inside a\n * valid envelope are the renderer's territory, which catches them and falls\n * back to the raw text itself.\n */\nfunction parseWorkflowToolResponse(text: string): WorkflowToolResponseView | undefined {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(text);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\tconst record = parsed as { workflows?: unknown; closures?: unknown; inspections?: unknown; warning?: unknown };\n\tif (!Array.isArray(record.workflows)) return undefined;\n\tif (record.closures !== undefined && !Array.isArray(record.closures)) return undefined;\n\tif (record.inspections !== undefined && !Array.isArray(record.inspections)) return undefined;\n\tif (record.warning !== undefined && typeof record.warning !== \"string\") return undefined;\n\treturn {\n\t\tstate: { workflows: record.workflows as WorkflowRecord[] },\n\t\t...(record.closures === undefined ? {} : { closures: record.closures as WorkflowClosure[] }),\n\t\t...(record.inspections === undefined ? {} : { inspections: record.inspections as WorkflowInspection[] }),\n\t\t...(record.warning === undefined ? {} : { warning: record.warning }),\n\t};\n}\n\n/** Structural renderResult signature as the host TUI invokes it. */\nexport type WorkflowToolResultRenderer = (\n\tresult: ToolResultLike,\n\toptions: RenderResultOptionsLike,\n\ttheme: ThemeLike,\n\tcontext: RenderContextLike,\n) => WorkflowToolResultText | undefined;\n\n/**\n * Shared renderResult of workflow_set and workflow_advance. Collapsed: the\n * panorama without diagnostics plus the warning line. Expanded: the full\n * panorama, closures, inspections, and the warning line (always last).\n * Non-workflow payloads and malformed records render as the raw text; a\n * missing or empty text part declines (undefined) so the host hides the\n * result. `context.lastComponent` instances are reused with the new payload.\n */\nexport function createWorkflowToolResultRenderer(deps: EngineDeps): WorkflowToolResultRenderer {\n\treturn (result, options, theme, context) => {\n\t\tconst text = result.content.find((part) => part.type === \"text\")?.text;\n\t\tif (text === undefined || text === \"\") return undefined;\n\t\tconst parsed = parseWorkflowToolResponse(text);\n\t\tlet body: string;\n\t\tif (parsed === undefined) {\n\t\t\tbody = text;\n\t\t} else {\n\t\t\t// The parse gate is envelope-level, so a parsed response may still\n\t\t\t// hold malformed records (a workflow without stages, a closure\n\t\t\t// without evidence). Rendering those must not throw out of the\n\t\t\t// plugin: fall back to the raw text, the same path as a parse failure.\n\t\t\ttry {\n\t\t\t\tconst sections = [\n\t\t\t\t\trenderPanorama(parsed.state, deps, options.expanded ? undefined : { diagnostics: false }),\n\t\t\t\t];\n\t\t\t\tif (options.expanded) {\n\t\t\t\t\tif (parsed.closures !== undefined) sections.push(renderClosures(parsed.closures));\n\t\t\t\t\tif (parsed.inspections !== undefined) sections.push(renderInspections(parsed.inspections));\n\t\t\t\t}\n\t\t\t\tif (parsed.warning !== undefined) sections.push(`warning: ${parsed.warning}`);\n\t\t\t\tbody = sections.join(\"\\n\\n\");\n\t\t\t} catch {\n\t\t\t\tbody = text;\n\t\t\t}\n\t\t}\n\t\tconst last = context.lastComponent;\n\t\tif (last instanceof WorkflowToolResultText) {\n\t\t\tlast.update(body, theme);\n\t\t\treturn last;\n\t\t}\n\t\treturn new WorkflowToolResultText(body, theme);\n\t};\n}\n"]}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAEN,KAAK,UAAU,EAGf,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAKN,KAAK,aAAa,EAElB,MAAM,YAAY,CAAC;AAoCpB;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CA4BlH;AAED,sGAAqG;AACrG,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAuBxE;AAgBD,sHAAqH;AACrH,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAgB5G;AAMD,yFAAyF;AACzF,UAAU,sBAAsB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,cAAc;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,sBAAsB,EAAE,CAAC;CACpD;AAED,UAAU,uBAAuB;IAChC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAED,UAAU,SAAS;IAClB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACxC;AAED,UAAU,iBAAiB;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CACjC;AAqCD;;;;;GAKG;AACH,cAAM,sBAAsB;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,KAAK,CAAY;IAEzB,YAAY,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAGzC;IAED,0EAA0E;IAC1E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAI3C;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAa9B;IAED,UAAU,IAAI,IAAI,CAEjB;CACD;AAED,gFAA8E;AAC9E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,CAQ3E;AAED,sGAAoG;AACpG,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,kBAAkB,EAAE,GAAG,MAAM,CAgBpF;AAwCD,oEAAoE;AACpE,MAAM,MAAM,0BAA0B,GAAG,CACxC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,iBAAiB,KACtB,sBAAsB,GAAG,SAAS,CAAC;AAExC;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,UAAU,GAAG,0BAA0B,CAkC7F","sourcesContent":["/**\n * /workflow command text rendering (§6 text face, §9 L1/L3): panorama\n * (indented dependency tree + per-stage status + declared delegation role +\n * waiting conditions + parallel active count + retreat count + active\n * diagnostics), why (one line per waiting stage, with the declared role), and\n * log (transition stream, newest first, reason codes and amend records\n * included). Plain text only — every host shape can render it, no TUI\n * required.\n *\n * Also hosts the structural renderResult face for workflow_set /\n * workflow_advance tool results: the host TUI serializes the tool response to\n * JSON text and hands it back here; this module re-renders it as the panorama\n * (collapsed: no diagnostics; expanded: diagnostics + closures + inspections)\n * through the structural Component shape — no TUI package import (the plugin\n * hosting boundary).\n */\n\nimport {\n\tcomputeWaitingOn,\n\ttype EngineDeps,\n\tmaxActiveOf,\n\tmaxRetreatsOf,\n\ttype WorkflowClosure,\n\ttype WorkflowInspection,\n} from \"./engine.ts\";\nimport {\n\ttype StageRuntime,\n\ttype StageSpec,\n\tWORKFLOW_TRANSITION_ENTRY,\n\ttype WorkflowRecord,\n\ttype WorkflowState,\n\ttype WorkflowTransitionEntry,\n} from \"./types.ts\";\n\nconst runtimeOf = (workflow: WorkflowRecord, stageId: string): StageRuntime | undefined =>\n\tworkflow.runtime.find((item) => item.stageId === stageId);\n\nfunction renderStageNode(\n\tworkflow: WorkflowRecord,\n\tspec: StageSpec,\n\tdepth: number,\n\tdeps: EngineDeps,\n\tlines: string[],\n\tvisited: Set<string>,\n): void {\n\tconst pad = \" \".repeat(depth + 1);\n\tif (visited.has(spec.id)) {\n\t\tlines.push(`${pad}-> ${spec.id} (shown above)`);\n\t\treturn;\n\t}\n\tvisited.add(spec.id);\n\tconst runtime = runtimeOf(workflow, spec.id);\n\tconst bits = [`- ${spec.id} [${runtime?.status ?? \"pending\"}] ${spec.title}`];\n\tif (spec.role !== undefined) bits.push(`· role ${spec.role}`);\n\tif (runtime !== undefined) {\n\t\tconst waiting = computeWaitingOn(workflow, spec, runtime, deps);\n\t\tif (waiting !== null) bits.push(`— waiting: ${waiting}`);\n\t\tif (runtime.attempts > 0) bits.push(`· attempts ${runtime.attempts}`);\n\t\tif (runtime.evidence.length > 0) bits.push(`· evidence ${runtime.evidence.length}`);\n\t}\n\tlines.push(`${pad}${bits.join(\" \")}`);\n\t// DAG tree: children are dependents in declaration order; diamond edges\n\t// collapse into a back-reference line instead of duplicating the subtree.\n\tfor (const child of workflow.stages.filter((item) => item.dependsOn.includes(spec.id))) {\n\t\trenderStageNode(workflow, child, depth + 1, deps, lines, visited);\n\t}\n}\n\n/**\n * Panorama: one block per workflow. `options.diagnostics: false` drops the\n * diagnostics block (the collapsed tool-result face); the default keeps the\n * /workflow command output unchanged.\n */\nexport function renderPanorama(state: WorkflowState, deps: EngineDeps, options?: { diagnostics?: boolean }): string {\n\tif (state.workflows.length === 0) {\n\t\treturn \"no workflows in this session; create one with workflow_set\";\n\t}\n\treturn state.workflows\n\t\t.map((workflow) => {\n\t\t\tconst lines: string[] = [];\n\t\t\tconst active = workflow.runtime.filter((item) => item.status === \"active\").length;\n\t\t\tlines.push(\n\t\t\t\t`workflow \"${workflow.title}\" (${workflow.id}) — ${workflow.status} · revision ${workflow.revision} · active ${active}/${maxActiveOf(workflow)} · retreats ${workflow.retreatCount}/${maxRetreatsOf(workflow)}`,\n\t\t\t);\n\t\t\tif (workflow.goalId !== undefined) lines.push(` goal: ${workflow.goalId}`);\n\t\t\tconst visited = new Set<string>();\n\t\t\tfor (const spec of workflow.stages.filter((item) => item.dependsOn.length === 0)) {\n\t\t\t\trenderStageNode(workflow, spec, 0, deps, lines, visited);\n\t\t\t}\n\t\t\t// Stages unreachable from a root (defensive: cannot happen on a\n\t\t\t// validated acyclic graph) still render, top-level.\n\t\t\tfor (const spec of workflow.stages) {\n\t\t\t\tif (!visited.has(spec.id)) renderStageNode(workflow, spec, 0, deps, lines, visited);\n\t\t\t}\n\t\t\tif (options?.diagnostics !== false && workflow.diagnostics.length > 0) {\n\t\t\t\tlines.push(\" diagnostics:\");\n\t\t\t\tfor (const diagnostic of workflow.diagnostics) lines.push(` - ${diagnostic}`);\n\t\t\t}\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\n/** Why it is stopped: every stage with an unmet wait condition, one line each (§6 /workflow why). */\nexport function renderWhy(state: WorkflowState, deps: EngineDeps): string {\n\tconst pending = state.workflows.filter(\n\t\t(workflow) => workflow.status !== \"completed\" && workflow.status !== \"cancelled\",\n\t);\n\tif (pending.length === 0) return \"no open workflows; nothing is waiting\";\n\treturn pending\n\t\t.map((workflow) => {\n\t\t\tconst lines = [`workflow ${workflow.id} — ${workflow.status}`];\n\t\t\tlet any = false;\n\t\t\tfor (const spec of workflow.stages) {\n\t\t\t\tconst runtime = runtimeOf(workflow, spec.id);\n\t\t\t\tif (runtime === undefined) continue;\n\t\t\t\tconst waiting = computeWaitingOn(workflow, spec, runtime, deps);\n\t\t\t\tif (waiting === null) continue;\n\t\t\t\tconst role = spec.role !== undefined ? ` · role ${spec.role}` : \"\";\n\t\t\t\tlines.push(` - ${spec.id} [${runtime.status}]${role} waiting: ${waiting}`);\n\t\t\t\tany = true;\n\t\t\t}\n\t\t\tif (!any) lines.push(\" no stage is waiting on an unmet condition\");\n\t\t\tfor (const diagnostic of workflow.diagnostics) lines.push(` ! ${diagnostic}`);\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\nfunction isTransitionEntry(data: unknown): data is WorkflowTransitionEntry {\n\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return false;\n\tconst record = data as Partial<WorkflowTransitionEntry>;\n\treturn (\n\t\ttypeof record.workflowId === \"string\" &&\n\t\ttypeof record.from === \"string\" &&\n\t\ttypeof record.to === \"string\" &&\n\t\ttypeof record.revision === \"number\" &&\n\t\trecord.reason !== undefined &&\n\t\ttypeof record.reason === \"object\" &&\n\t\ttypeof (record.reason as { code?: unknown }).code === \"string\"\n\t);\n}\n\n/** Transition stream, newest first (§8.1 \"which conversion went wrong\"): revision anchor + trigger + reason code. */\nexport function renderLog(entries: readonly { customType?: string; data?: unknown }[], limit: number): string {\n\tconst transitions: WorkflowTransitionEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.customType !== WORKFLOW_TRANSITION_ENTRY) continue;\n\t\tif (isTransitionEntry(entry.data)) transitions.push(entry.data);\n\t}\n\tif (transitions.length === 0) return \"no workflow transitions recorded\";\n\treturn transitions\n\t\t.slice(-limit)\n\t\t.reverse()\n\t\t.map((transition) => {\n\t\t\tconst stage = transition.stageId !== undefined ? `/${transition.stageId}` : \"\";\n\t\t\tconst detail = transition.reason.detail !== undefined ? ` — ${transition.reason.detail}` : \"\";\n\t\t\treturn `#${transition.revision} ${transition.workflowId}${stage} ${transition.from} -> ${transition.to} [${transition.trigger}] ${transition.reason.code}: ${transition.reason.message}${detail}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Structural renderResult (tool-result panorama face)\n// ---------------------------------------------------------------------------\n\n/** Structural host faces: declared locally so this module stays free of host imports. */\ninterface ToolResultTextPartLike {\n\treadonly type: string;\n\treadonly text?: string;\n}\n\ninterface ToolResultLike {\n\treadonly content: readonly ToolResultTextPartLike[];\n}\n\ninterface RenderResultOptionsLike {\n\treadonly expanded: boolean;\n\treadonly isPartial: boolean;\n}\n\ninterface ThemeLike {\n\tfg(color: string, text: string): string;\n}\n\ninterface RenderContextLike {\n\treadonly lastComponent?: unknown;\n}\n\n/**\n * Simplified East Asian fullwidth detection (wcwidth wide ranges, subset):\n * code points in these ranges occupy 2 terminal cells.\n */\nconst isWideCodePoint = (codePoint: number): boolean =>\n\t(codePoint >= 0x1100 && codePoint <= 0x115f) ||\n\t(codePoint >= 0x2e80 && codePoint <= 0xa4cf) ||\n\t(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||\n\t(codePoint >= 0xf900 && codePoint <= 0xfaff) ||\n\t(codePoint >= 0xfe30 && codePoint <= 0xfe4f) ||\n\t(codePoint >= 0xff00 && codePoint <= 0xff60) ||\n\t(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||\n\t(codePoint >= 0x20000 && codePoint <= 0x3fffd);\n\n/** Fold one logical line to `width` cells; wide code points count 2. */\nfunction foldLine(line: string, width: number): string[] {\n\tif (width <= 0) return [line];\n\tconst folded: string[] = [];\n\tlet current = \"\";\n\tlet used = 0;\n\tfor (const char of line) {\n\t\tconst cells = isWideCodePoint(char.codePointAt(0) ?? 0) ? 2 : 1;\n\t\tif (current !== \"\" && used + cells > width) {\n\t\t\tfolded.push(current);\n\t\t\tcurrent = char;\n\t\t\tused = cells;\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t\tused += cells;\n\t\t}\n\t}\n\tfolded.push(current);\n\treturn folded;\n}\n\n/**\n * Structural tool-result component (host Component shape without importing\n * the TUI package): holds plain text plus a theme, colors `warning: ` lines\n * with the warning color and everything else as tool output, folds lines to\n * the render width, and caches the folded output per width.\n */\nclass WorkflowToolResultText {\n\tprivate readonly cache = new Map<number, string[]>();\n\tprivate text: string;\n\tprivate theme: ThemeLike;\n\n\tconstructor(text: string, theme: ThemeLike) {\n\t\tthis.text = text;\n\t\tthis.theme = theme;\n\t}\n\n\t/** New payload (or theme) on component reuse: drop every cached width. */\n\tupdate(text: string, theme: ThemeLike): void {\n\t\tthis.text = text;\n\t\tthis.theme = theme;\n\t\tthis.cache.clear();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst cached = this.cache.get(width);\n\t\tif (cached !== undefined) return cached;\n\t\tconst lines: string[] = [];\n\t\tfor (const rawLine of this.text.split(\"\\n\")) {\n\t\t\tfor (const folded of foldLine(rawLine, width)) {\n\t\t\t\tlines.push(\n\t\t\t\t\tfolded.startsWith(\"warning: \") ? this.theme.fg(\"warning\", folded) : this.theme.fg(\"toolOutput\", folded),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthis.cache.set(width, lines);\n\t\treturn lines;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cache.clear();\n\t}\n}\n\n/** Closures: one line per closure — the goal-achievement evidence summary. */\nexport function renderClosures(closures: readonly WorkflowClosure[]): string {\n\treturn closures\n\t\t.map((closure) => {\n\t\t\tconst goal = closure.goalId !== undefined ? ` (goal ${closure.goalId})` : \"\";\n\t\t\tconst evidence = closure.evidence.map((item) => `${item.stageId}/${item.acceptanceId}=${item.ref}`).join(\", \");\n\t\t\treturn `closure ${closure.workflowId}${goal}: ${evidence}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\n/** Inspections: one block per inspection — per-stage waiting conditions and acceptance coverage. */\nexport function renderInspections(inspections: readonly WorkflowInspection[]): string {\n\treturn inspections\n\t\t.map((inspection) => {\n\t\t\tconst lines = [`inspect ${inspection.workflowId} [${inspection.status}]`];\n\t\t\tfor (const stage of inspection.stages) {\n\t\t\t\tlet line = ` ${stage.stageId} [${stage.status}] attempts ${stage.attempts}`;\n\t\t\t\tif (stage.waitingOn !== null) line += ` — waiting: ${stage.waitingOn}`;\n\t\t\t\tif (stage.planId !== undefined) line += ` · plan ${stage.planId} (${stage.planStatus ?? \"unknown\"})`;\n\t\t\t\tlines.push(line);\n\t\t\t\tfor (const item of stage.acceptance) {\n\t\t\t\t\tlines.push(` ${item.id}: ${item.covered ? `covered (${item.refs.length} refs)` : \"missing\"}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\n/** Parsed tool response view: the state base plus the optional response-only sections. */\ninterface WorkflowToolResponseView {\n\tstate: WorkflowState;\n\tclosures?: WorkflowClosure[];\n\tinspections?: WorkflowInspection[];\n\twarning?: string;\n}\n\n/**\n * Parses the host-serialized tool response. Only the `workflows` array key is\n * required (the state base); anything else is not a workflow tool response and\n * the caller falls back to rendering the raw text. Present-but-mistyped extra\n * fields (warning/closures/inspections) are shape mismatches and also reject\n * the parse (the todo/goal/plan gate semantics). Malformed records inside a\n * valid envelope are the renderer's territory, which catches them and falls\n * back to the raw text itself.\n */\nfunction parseWorkflowToolResponse(text: string): WorkflowToolResponseView | undefined {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(text);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\tconst record = parsed as { workflows?: unknown; closures?: unknown; inspections?: unknown; warning?: unknown };\n\tif (!Array.isArray(record.workflows)) return undefined;\n\tif (record.closures !== undefined && !Array.isArray(record.closures)) return undefined;\n\tif (record.inspections !== undefined && !Array.isArray(record.inspections)) return undefined;\n\tif (record.warning !== undefined && typeof record.warning !== \"string\") return undefined;\n\treturn {\n\t\tstate: { workflows: record.workflows as WorkflowRecord[] },\n\t\t...(record.closures === undefined ? {} : { closures: record.closures as WorkflowClosure[] }),\n\t\t...(record.inspections === undefined ? {} : { inspections: record.inspections as WorkflowInspection[] }),\n\t\t...(record.warning === undefined ? {} : { warning: record.warning }),\n\t};\n}\n\n/** Structural renderResult signature as the host TUI invokes it. */\nexport type WorkflowToolResultRenderer = (\n\tresult: ToolResultLike,\n\toptions: RenderResultOptionsLike,\n\ttheme: ThemeLike,\n\tcontext: RenderContextLike,\n) => WorkflowToolResultText | undefined;\n\n/**\n * Shared renderResult of workflow_set and workflow_advance. Collapsed: the\n * panorama without diagnostics plus the warning line. Expanded: the full\n * panorama, closures, inspections, and the warning line (always last).\n * Non-workflow payloads and malformed records render as the raw text; a\n * missing or empty text part declines (undefined) so the host hides the\n * result. `context.lastComponent` instances are reused with the new payload.\n */\nexport function createWorkflowToolResultRenderer(deps: EngineDeps): WorkflowToolResultRenderer {\n\treturn (result, options, theme, context) => {\n\t\tconst text = result.content.find((part) => part.type === \"text\")?.text;\n\t\tif (text === undefined || text === \"\") return undefined;\n\t\tconst parsed = parseWorkflowToolResponse(text);\n\t\tlet body: string;\n\t\tif (parsed === undefined) {\n\t\t\tbody = text;\n\t\t} else {\n\t\t\t// The parse gate is envelope-level, so a parsed response may still\n\t\t\t// hold malformed records (a workflow without stages, a closure\n\t\t\t// without evidence). Rendering those must not throw out of the\n\t\t\t// plugin: fall back to the raw text, the same path as a parse failure.\n\t\t\ttry {\n\t\t\t\tconst sections = [\n\t\t\t\t\trenderPanorama(parsed.state, deps, options.expanded ? undefined : { diagnostics: false }),\n\t\t\t\t];\n\t\t\t\tif (options.expanded) {\n\t\t\t\t\tif (parsed.closures !== undefined) sections.push(renderClosures(parsed.closures));\n\t\t\t\t\tif (parsed.inspections !== undefined) sections.push(renderInspections(parsed.inspections));\n\t\t\t\t}\n\t\t\t\tif (parsed.warning !== undefined) sections.push(`warning: ${parsed.warning}`);\n\t\t\t\tbody = sections.join(\"\\n\\n\");\n\t\t\t} catch {\n\t\t\t\tbody = text;\n\t\t\t}\n\t\t}\n\t\tconst last = context.lastComponent;\n\t\tif (last instanceof WorkflowToolResultText) {\n\t\t\tlast.update(body, theme);\n\t\t\treturn last;\n\t\t}\n\t\treturn new WorkflowToolResultText(body, theme);\n\t};\n}\n"]}
package/dist/render.js CHANGED
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * /workflow command text rendering (§6 text face, §9 L1/L3): panorama
3
- * (indented dependency tree + per-stage status + waiting conditions +
4
- * parallel active count + retreat count + active diagnostics), why (one line
5
- * per waiting stage), and log (transition stream, newest first, reason codes
6
- * and amend records included). Plain text only — every host shape can render
7
- * it, no TUI required.
3
+ * (indented dependency tree + per-stage status + declared delegation role +
4
+ * waiting conditions + parallel active count + retreat count + active
5
+ * diagnostics), why (one line per waiting stage, with the declared role), and
6
+ * log (transition stream, newest first, reason codes and amend records
7
+ * included). Plain text only — every host shape can render it, no TUI
8
+ * required.
8
9
  *
9
10
  * Also hosts the structural renderResult face for workflow_set /
10
11
  * workflow_advance tool results: the host TUI serializes the tool response to
@@ -25,6 +26,8 @@ function renderStageNode(workflow, spec, depth, deps, lines, visited) {
25
26
  visited.add(spec.id);
26
27
  const runtime = runtimeOf(workflow, spec.id);
27
28
  const bits = [`- ${spec.id} [${runtime?.status ?? "pending"}] ${spec.title}`];
29
+ if (spec.role !== undefined)
30
+ bits.push(`· role ${spec.role}`);
28
31
  if (runtime !== undefined) {
29
32
  const waiting = computeWaitingOn(workflow, spec, runtime, deps);
30
33
  if (waiting !== null)
@@ -92,7 +95,8 @@ export function renderWhy(state, deps) {
92
95
  const waiting = computeWaitingOn(workflow, spec, runtime, deps);
93
96
  if (waiting === null)
94
97
  continue;
95
- lines.push(` - ${spec.id} [${runtime.status}] waiting: ${waiting}`);
98
+ const role = spec.role !== undefined ? ` · role ${spec.role}` : "";
99
+ lines.push(` - ${spec.id} [${runtime.status}]${role} waiting: ${waiting}`);
96
100
  any = true;
97
101
  }
98
102
  if (!any)
@@ -1 +1 @@
1
- {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * /workflow command text rendering (§6 text face, §9 L1/L3): panorama\n * (indented dependency tree + per-stage status + waiting conditions +\n * parallel active count + retreat count + active diagnostics), why (one line\n * per waiting stage), and log (transition stream, newest first, reason codes\n * and amend records included). Plain text only — every host shape can render\n * it, no TUI required.\n *\n * Also hosts the structural renderResult face for workflow_set /\n * workflow_advance tool results: the host TUI serializes the tool response to\n * JSON text and hands it back here; this module re-renders it as the panorama\n * (collapsed: no diagnostics; expanded: diagnostics + closures + inspections)\n * through the structural Component shape — no TUI package import (the plugin\n * hosting boundary).\n */\n\nimport {\n\tcomputeWaitingOn,\n\ttype EngineDeps,\n\tmaxActiveOf,\n\tmaxRetreatsOf,\n\ttype WorkflowClosure,\n\ttype WorkflowInspection,\n} from \"./engine.ts\";\nimport {\n\ttype StageRuntime,\n\ttype StageSpec,\n\tWORKFLOW_TRANSITION_ENTRY,\n\ttype WorkflowRecord,\n\ttype WorkflowState,\n\ttype WorkflowTransitionEntry,\n} from \"./types.ts\";\n\nconst runtimeOf = (workflow: WorkflowRecord, stageId: string): StageRuntime | undefined =>\n\tworkflow.runtime.find((item) => item.stageId === stageId);\n\nfunction renderStageNode(\n\tworkflow: WorkflowRecord,\n\tspec: StageSpec,\n\tdepth: number,\n\tdeps: EngineDeps,\n\tlines: string[],\n\tvisited: Set<string>,\n): void {\n\tconst pad = \" \".repeat(depth + 1);\n\tif (visited.has(spec.id)) {\n\t\tlines.push(`${pad}-> ${spec.id} (shown above)`);\n\t\treturn;\n\t}\n\tvisited.add(spec.id);\n\tconst runtime = runtimeOf(workflow, spec.id);\n\tconst bits = [`- ${spec.id} [${runtime?.status ?? \"pending\"}] ${spec.title}`];\n\tif (runtime !== undefined) {\n\t\tconst waiting = computeWaitingOn(workflow, spec, runtime, deps);\n\t\tif (waiting !== null) bits.push(`— waiting: ${waiting}`);\n\t\tif (runtime.attempts > 0) bits.push(`· attempts ${runtime.attempts}`);\n\t\tif (runtime.evidence.length > 0) bits.push(`· evidence ${runtime.evidence.length}`);\n\t}\n\tlines.push(`${pad}${bits.join(\" \")}`);\n\t// DAG tree: children are dependents in declaration order; diamond edges\n\t// collapse into a back-reference line instead of duplicating the subtree.\n\tfor (const child of workflow.stages.filter((item) => item.dependsOn.includes(spec.id))) {\n\t\trenderStageNode(workflow, child, depth + 1, deps, lines, visited);\n\t}\n}\n\n/**\n * Panorama: one block per workflow. `options.diagnostics: false` drops the\n * diagnostics block (the collapsed tool-result face); the default keeps the\n * /workflow command output unchanged.\n */\nexport function renderPanorama(state: WorkflowState, deps: EngineDeps, options?: { diagnostics?: boolean }): string {\n\tif (state.workflows.length === 0) {\n\t\treturn \"no workflows in this session; create one with workflow_set\";\n\t}\n\treturn state.workflows\n\t\t.map((workflow) => {\n\t\t\tconst lines: string[] = [];\n\t\t\tconst active = workflow.runtime.filter((item) => item.status === \"active\").length;\n\t\t\tlines.push(\n\t\t\t\t`workflow \"${workflow.title}\" (${workflow.id}) — ${workflow.status} · revision ${workflow.revision} · active ${active}/${maxActiveOf(workflow)} · retreats ${workflow.retreatCount}/${maxRetreatsOf(workflow)}`,\n\t\t\t);\n\t\t\tif (workflow.goalId !== undefined) lines.push(` goal: ${workflow.goalId}`);\n\t\t\tconst visited = new Set<string>();\n\t\t\tfor (const spec of workflow.stages.filter((item) => item.dependsOn.length === 0)) {\n\t\t\t\trenderStageNode(workflow, spec, 0, deps, lines, visited);\n\t\t\t}\n\t\t\t// Stages unreachable from a root (defensive: cannot happen on a\n\t\t\t// validated acyclic graph) still render, top-level.\n\t\t\tfor (const spec of workflow.stages) {\n\t\t\t\tif (!visited.has(spec.id)) renderStageNode(workflow, spec, 0, deps, lines, visited);\n\t\t\t}\n\t\t\tif (options?.diagnostics !== false && workflow.diagnostics.length > 0) {\n\t\t\t\tlines.push(\" diagnostics:\");\n\t\t\t\tfor (const diagnostic of workflow.diagnostics) lines.push(` - ${diagnostic}`);\n\t\t\t}\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\n/** Why it is stopped: every stage with an unmet wait condition, one line each (§6 /workflow why). */\nexport function renderWhy(state: WorkflowState, deps: EngineDeps): string {\n\tconst pending = state.workflows.filter(\n\t\t(workflow) => workflow.status !== \"completed\" && workflow.status !== \"cancelled\",\n\t);\n\tif (pending.length === 0) return \"no open workflows; nothing is waiting\";\n\treturn pending\n\t\t.map((workflow) => {\n\t\t\tconst lines = [`workflow ${workflow.id} — ${workflow.status}`];\n\t\t\tlet any = false;\n\t\t\tfor (const spec of workflow.stages) {\n\t\t\t\tconst runtime = runtimeOf(workflow, spec.id);\n\t\t\t\tif (runtime === undefined) continue;\n\t\t\t\tconst waiting = computeWaitingOn(workflow, spec, runtime, deps);\n\t\t\t\tif (waiting === null) continue;\n\t\t\t\tlines.push(` - ${spec.id} [${runtime.status}] waiting: ${waiting}`);\n\t\t\t\tany = true;\n\t\t\t}\n\t\t\tif (!any) lines.push(\" no stage is waiting on an unmet condition\");\n\t\t\tfor (const diagnostic of workflow.diagnostics) lines.push(` ! ${diagnostic}`);\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\nfunction isTransitionEntry(data: unknown): data is WorkflowTransitionEntry {\n\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return false;\n\tconst record = data as Partial<WorkflowTransitionEntry>;\n\treturn (\n\t\ttypeof record.workflowId === \"string\" &&\n\t\ttypeof record.from === \"string\" &&\n\t\ttypeof record.to === \"string\" &&\n\t\ttypeof record.revision === \"number\" &&\n\t\trecord.reason !== undefined &&\n\t\ttypeof record.reason === \"object\" &&\n\t\ttypeof (record.reason as { code?: unknown }).code === \"string\"\n\t);\n}\n\n/** Transition stream, newest first (§8.1 \"which conversion went wrong\"): revision anchor + trigger + reason code. */\nexport function renderLog(entries: readonly { customType?: string; data?: unknown }[], limit: number): string {\n\tconst transitions: WorkflowTransitionEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.customType !== WORKFLOW_TRANSITION_ENTRY) continue;\n\t\tif (isTransitionEntry(entry.data)) transitions.push(entry.data);\n\t}\n\tif (transitions.length === 0) return \"no workflow transitions recorded\";\n\treturn transitions\n\t\t.slice(-limit)\n\t\t.reverse()\n\t\t.map((transition) => {\n\t\t\tconst stage = transition.stageId !== undefined ? `/${transition.stageId}` : \"\";\n\t\t\tconst detail = transition.reason.detail !== undefined ? ` — ${transition.reason.detail}` : \"\";\n\t\t\treturn `#${transition.revision} ${transition.workflowId}${stage} ${transition.from} -> ${transition.to} [${transition.trigger}] ${transition.reason.code}: ${transition.reason.message}${detail}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Structural renderResult (tool-result panorama face)\n// ---------------------------------------------------------------------------\n\n/** Structural host faces: declared locally so this module stays free of host imports. */\ninterface ToolResultTextPartLike {\n\treadonly type: string;\n\treadonly text?: string;\n}\n\ninterface ToolResultLike {\n\treadonly content: readonly ToolResultTextPartLike[];\n}\n\ninterface RenderResultOptionsLike {\n\treadonly expanded: boolean;\n\treadonly isPartial: boolean;\n}\n\ninterface ThemeLike {\n\tfg(color: string, text: string): string;\n}\n\ninterface RenderContextLike {\n\treadonly lastComponent?: unknown;\n}\n\n/**\n * Simplified East Asian fullwidth detection (wcwidth wide ranges, subset):\n * code points in these ranges occupy 2 terminal cells.\n */\nconst isWideCodePoint = (codePoint: number): boolean =>\n\t(codePoint >= 0x1100 && codePoint <= 0x115f) ||\n\t(codePoint >= 0x2e80 && codePoint <= 0xa4cf) ||\n\t(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||\n\t(codePoint >= 0xf900 && codePoint <= 0xfaff) ||\n\t(codePoint >= 0xfe30 && codePoint <= 0xfe4f) ||\n\t(codePoint >= 0xff00 && codePoint <= 0xff60) ||\n\t(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||\n\t(codePoint >= 0x20000 && codePoint <= 0x3fffd);\n\n/** Fold one logical line to `width` cells; wide code points count 2. */\nfunction foldLine(line: string, width: number): string[] {\n\tif (width <= 0) return [line];\n\tconst folded: string[] = [];\n\tlet current = \"\";\n\tlet used = 0;\n\tfor (const char of line) {\n\t\tconst cells = isWideCodePoint(char.codePointAt(0) ?? 0) ? 2 : 1;\n\t\tif (current !== \"\" && used + cells > width) {\n\t\t\tfolded.push(current);\n\t\t\tcurrent = char;\n\t\t\tused = cells;\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t\tused += cells;\n\t\t}\n\t}\n\tfolded.push(current);\n\treturn folded;\n}\n\n/**\n * Structural tool-result component (host Component shape without importing\n * the TUI package): holds plain text plus a theme, colors `warning: ` lines\n * with the warning color and everything else as tool output, folds lines to\n * the render width, and caches the folded output per width.\n */\nclass WorkflowToolResultText {\n\tprivate readonly cache = new Map<number, string[]>();\n\tprivate text: string;\n\tprivate theme: ThemeLike;\n\n\tconstructor(text: string, theme: ThemeLike) {\n\t\tthis.text = text;\n\t\tthis.theme = theme;\n\t}\n\n\t/** New payload (or theme) on component reuse: drop every cached width. */\n\tupdate(text: string, theme: ThemeLike): void {\n\t\tthis.text = text;\n\t\tthis.theme = theme;\n\t\tthis.cache.clear();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst cached = this.cache.get(width);\n\t\tif (cached !== undefined) return cached;\n\t\tconst lines: string[] = [];\n\t\tfor (const rawLine of this.text.split(\"\\n\")) {\n\t\t\tfor (const folded of foldLine(rawLine, width)) {\n\t\t\t\tlines.push(\n\t\t\t\t\tfolded.startsWith(\"warning: \") ? this.theme.fg(\"warning\", folded) : this.theme.fg(\"toolOutput\", folded),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthis.cache.set(width, lines);\n\t\treturn lines;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cache.clear();\n\t}\n}\n\n/** Closures: one line per closure — the goal-achievement evidence summary. */\nexport function renderClosures(closures: readonly WorkflowClosure[]): string {\n\treturn closures\n\t\t.map((closure) => {\n\t\t\tconst goal = closure.goalId !== undefined ? ` (goal ${closure.goalId})` : \"\";\n\t\t\tconst evidence = closure.evidence.map((item) => `${item.stageId}/${item.acceptanceId}=${item.ref}`).join(\", \");\n\t\t\treturn `closure ${closure.workflowId}${goal}: ${evidence}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\n/** Inspections: one block per inspection — per-stage waiting conditions and acceptance coverage. */\nexport function renderInspections(inspections: readonly WorkflowInspection[]): string {\n\treturn inspections\n\t\t.map((inspection) => {\n\t\t\tconst lines = [`inspect ${inspection.workflowId} [${inspection.status}]`];\n\t\t\tfor (const stage of inspection.stages) {\n\t\t\t\tlet line = ` ${stage.stageId} [${stage.status}] attempts ${stage.attempts}`;\n\t\t\t\tif (stage.waitingOn !== null) line += ` — waiting: ${stage.waitingOn}`;\n\t\t\t\tif (stage.planId !== undefined) line += ` · plan ${stage.planId} (${stage.planStatus ?? \"unknown\"})`;\n\t\t\t\tlines.push(line);\n\t\t\t\tfor (const item of stage.acceptance) {\n\t\t\t\t\tlines.push(` ${item.id}: ${item.covered ? `covered (${item.refs.length} refs)` : \"missing\"}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\n/** Parsed tool response view: the state base plus the optional response-only sections. */\ninterface WorkflowToolResponseView {\n\tstate: WorkflowState;\n\tclosures?: WorkflowClosure[];\n\tinspections?: WorkflowInspection[];\n\twarning?: string;\n}\n\n/**\n * Parses the host-serialized tool response. Only the `workflows` array key is\n * required (the state base); anything else is not a workflow tool response and\n * the caller falls back to rendering the raw text. Present-but-mistyped extra\n * fields (warning/closures/inspections) are shape mismatches and also reject\n * the parse (the todo/goal/plan gate semantics). Malformed records inside a\n * valid envelope are the renderer's territory, which catches them and falls\n * back to the raw text itself.\n */\nfunction parseWorkflowToolResponse(text: string): WorkflowToolResponseView | undefined {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(text);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\tconst record = parsed as { workflows?: unknown; closures?: unknown; inspections?: unknown; warning?: unknown };\n\tif (!Array.isArray(record.workflows)) return undefined;\n\tif (record.closures !== undefined && !Array.isArray(record.closures)) return undefined;\n\tif (record.inspections !== undefined && !Array.isArray(record.inspections)) return undefined;\n\tif (record.warning !== undefined && typeof record.warning !== \"string\") return undefined;\n\treturn {\n\t\tstate: { workflows: record.workflows as WorkflowRecord[] },\n\t\t...(record.closures === undefined ? {} : { closures: record.closures as WorkflowClosure[] }),\n\t\t...(record.inspections === undefined ? {} : { inspections: record.inspections as WorkflowInspection[] }),\n\t\t...(record.warning === undefined ? {} : { warning: record.warning }),\n\t};\n}\n\n/** Structural renderResult signature as the host TUI invokes it. */\nexport type WorkflowToolResultRenderer = (\n\tresult: ToolResultLike,\n\toptions: RenderResultOptionsLike,\n\ttheme: ThemeLike,\n\tcontext: RenderContextLike,\n) => WorkflowToolResultText | undefined;\n\n/**\n * Shared renderResult of workflow_set and workflow_advance. Collapsed: the\n * panorama without diagnostics plus the warning line. Expanded: the full\n * panorama, closures, inspections, and the warning line (always last).\n * Non-workflow payloads and malformed records render as the raw text; a\n * missing or empty text part declines (undefined) so the host hides the\n * result. `context.lastComponent` instances are reused with the new payload.\n */\nexport function createWorkflowToolResultRenderer(deps: EngineDeps): WorkflowToolResultRenderer {\n\treturn (result, options, theme, context) => {\n\t\tconst text = result.content.find((part) => part.type === \"text\")?.text;\n\t\tif (text === undefined || text === \"\") return undefined;\n\t\tconst parsed = parseWorkflowToolResponse(text);\n\t\tlet body: string;\n\t\tif (parsed === undefined) {\n\t\t\tbody = text;\n\t\t} else {\n\t\t\t// The parse gate is envelope-level, so a parsed response may still\n\t\t\t// hold malformed records (a workflow without stages, a closure\n\t\t\t// without evidence). Rendering those must not throw out of the\n\t\t\t// plugin: fall back to the raw text, the same path as a parse failure.\n\t\t\ttry {\n\t\t\t\tconst sections = [\n\t\t\t\t\trenderPanorama(parsed.state, deps, options.expanded ? undefined : { diagnostics: false }),\n\t\t\t\t];\n\t\t\t\tif (options.expanded) {\n\t\t\t\t\tif (parsed.closures !== undefined) sections.push(renderClosures(parsed.closures));\n\t\t\t\t\tif (parsed.inspections !== undefined) sections.push(renderInspections(parsed.inspections));\n\t\t\t\t}\n\t\t\t\tif (parsed.warning !== undefined) sections.push(`warning: ${parsed.warning}`);\n\t\t\t\tbody = sections.join(\"\\n\\n\");\n\t\t\t} catch {\n\t\t\t\tbody = text;\n\t\t\t}\n\t\t}\n\t\tconst last = context.lastComponent;\n\t\tif (last instanceof WorkflowToolResultText) {\n\t\t\tlast.update(body, theme);\n\t\t\treturn last;\n\t\t}\n\t\treturn new WorkflowToolResultText(body, theme);\n\t};\n}\n"]}
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * /workflow command text rendering (§6 text face, §9 L1/L3): panorama\n * (indented dependency tree + per-stage status + declared delegation role +\n * waiting conditions + parallel active count + retreat count + active\n * diagnostics), why (one line per waiting stage, with the declared role), and\n * log (transition stream, newest first, reason codes and amend records\n * included). Plain text only — every host shape can render it, no TUI\n * required.\n *\n * Also hosts the structural renderResult face for workflow_set /\n * workflow_advance tool results: the host TUI serializes the tool response to\n * JSON text and hands it back here; this module re-renders it as the panorama\n * (collapsed: no diagnostics; expanded: diagnostics + closures + inspections)\n * through the structural Component shape — no TUI package import (the plugin\n * hosting boundary).\n */\n\nimport {\n\tcomputeWaitingOn,\n\ttype EngineDeps,\n\tmaxActiveOf,\n\tmaxRetreatsOf,\n\ttype WorkflowClosure,\n\ttype WorkflowInspection,\n} from \"./engine.ts\";\nimport {\n\ttype StageRuntime,\n\ttype StageSpec,\n\tWORKFLOW_TRANSITION_ENTRY,\n\ttype WorkflowRecord,\n\ttype WorkflowState,\n\ttype WorkflowTransitionEntry,\n} from \"./types.ts\";\n\nconst runtimeOf = (workflow: WorkflowRecord, stageId: string): StageRuntime | undefined =>\n\tworkflow.runtime.find((item) => item.stageId === stageId);\n\nfunction renderStageNode(\n\tworkflow: WorkflowRecord,\n\tspec: StageSpec,\n\tdepth: number,\n\tdeps: EngineDeps,\n\tlines: string[],\n\tvisited: Set<string>,\n): void {\n\tconst pad = \" \".repeat(depth + 1);\n\tif (visited.has(spec.id)) {\n\t\tlines.push(`${pad}-> ${spec.id} (shown above)`);\n\t\treturn;\n\t}\n\tvisited.add(spec.id);\n\tconst runtime = runtimeOf(workflow, spec.id);\n\tconst bits = [`- ${spec.id} [${runtime?.status ?? \"pending\"}] ${spec.title}`];\n\tif (spec.role !== undefined) bits.push(`· role ${spec.role}`);\n\tif (runtime !== undefined) {\n\t\tconst waiting = computeWaitingOn(workflow, spec, runtime, deps);\n\t\tif (waiting !== null) bits.push(`— waiting: ${waiting}`);\n\t\tif (runtime.attempts > 0) bits.push(`· attempts ${runtime.attempts}`);\n\t\tif (runtime.evidence.length > 0) bits.push(`· evidence ${runtime.evidence.length}`);\n\t}\n\tlines.push(`${pad}${bits.join(\" \")}`);\n\t// DAG tree: children are dependents in declaration order; diamond edges\n\t// collapse into a back-reference line instead of duplicating the subtree.\n\tfor (const child of workflow.stages.filter((item) => item.dependsOn.includes(spec.id))) {\n\t\trenderStageNode(workflow, child, depth + 1, deps, lines, visited);\n\t}\n}\n\n/**\n * Panorama: one block per workflow. `options.diagnostics: false` drops the\n * diagnostics block (the collapsed tool-result face); the default keeps the\n * /workflow command output unchanged.\n */\nexport function renderPanorama(state: WorkflowState, deps: EngineDeps, options?: { diagnostics?: boolean }): string {\n\tif (state.workflows.length === 0) {\n\t\treturn \"no workflows in this session; create one with workflow_set\";\n\t}\n\treturn state.workflows\n\t\t.map((workflow) => {\n\t\t\tconst lines: string[] = [];\n\t\t\tconst active = workflow.runtime.filter((item) => item.status === \"active\").length;\n\t\t\tlines.push(\n\t\t\t\t`workflow \"${workflow.title}\" (${workflow.id}) — ${workflow.status} · revision ${workflow.revision} · active ${active}/${maxActiveOf(workflow)} · retreats ${workflow.retreatCount}/${maxRetreatsOf(workflow)}`,\n\t\t\t);\n\t\t\tif (workflow.goalId !== undefined) lines.push(` goal: ${workflow.goalId}`);\n\t\t\tconst visited = new Set<string>();\n\t\t\tfor (const spec of workflow.stages.filter((item) => item.dependsOn.length === 0)) {\n\t\t\t\trenderStageNode(workflow, spec, 0, deps, lines, visited);\n\t\t\t}\n\t\t\t// Stages unreachable from a root (defensive: cannot happen on a\n\t\t\t// validated acyclic graph) still render, top-level.\n\t\t\tfor (const spec of workflow.stages) {\n\t\t\t\tif (!visited.has(spec.id)) renderStageNode(workflow, spec, 0, deps, lines, visited);\n\t\t\t}\n\t\t\tif (options?.diagnostics !== false && workflow.diagnostics.length > 0) {\n\t\t\t\tlines.push(\" diagnostics:\");\n\t\t\t\tfor (const diagnostic of workflow.diagnostics) lines.push(` - ${diagnostic}`);\n\t\t\t}\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\n/** Why it is stopped: every stage with an unmet wait condition, one line each (§6 /workflow why). */\nexport function renderWhy(state: WorkflowState, deps: EngineDeps): string {\n\tconst pending = state.workflows.filter(\n\t\t(workflow) => workflow.status !== \"completed\" && workflow.status !== \"cancelled\",\n\t);\n\tif (pending.length === 0) return \"no open workflows; nothing is waiting\";\n\treturn pending\n\t\t.map((workflow) => {\n\t\t\tconst lines = [`workflow ${workflow.id} — ${workflow.status}`];\n\t\t\tlet any = false;\n\t\t\tfor (const spec of workflow.stages) {\n\t\t\t\tconst runtime = runtimeOf(workflow, spec.id);\n\t\t\t\tif (runtime === undefined) continue;\n\t\t\t\tconst waiting = computeWaitingOn(workflow, spec, runtime, deps);\n\t\t\t\tif (waiting === null) continue;\n\t\t\t\tconst role = spec.role !== undefined ? ` · role ${spec.role}` : \"\";\n\t\t\t\tlines.push(` - ${spec.id} [${runtime.status}]${role} waiting: ${waiting}`);\n\t\t\t\tany = true;\n\t\t\t}\n\t\t\tif (!any) lines.push(\" no stage is waiting on an unmet condition\");\n\t\t\tfor (const diagnostic of workflow.diagnostics) lines.push(` ! ${diagnostic}`);\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\nfunction isTransitionEntry(data: unknown): data is WorkflowTransitionEntry {\n\tif (!data || typeof data !== \"object\" || Array.isArray(data)) return false;\n\tconst record = data as Partial<WorkflowTransitionEntry>;\n\treturn (\n\t\ttypeof record.workflowId === \"string\" &&\n\t\ttypeof record.from === \"string\" &&\n\t\ttypeof record.to === \"string\" &&\n\t\ttypeof record.revision === \"number\" &&\n\t\trecord.reason !== undefined &&\n\t\ttypeof record.reason === \"object\" &&\n\t\ttypeof (record.reason as { code?: unknown }).code === \"string\"\n\t);\n}\n\n/** Transition stream, newest first (§8.1 \"which conversion went wrong\"): revision anchor + trigger + reason code. */\nexport function renderLog(entries: readonly { customType?: string; data?: unknown }[], limit: number): string {\n\tconst transitions: WorkflowTransitionEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.customType !== WORKFLOW_TRANSITION_ENTRY) continue;\n\t\tif (isTransitionEntry(entry.data)) transitions.push(entry.data);\n\t}\n\tif (transitions.length === 0) return \"no workflow transitions recorded\";\n\treturn transitions\n\t\t.slice(-limit)\n\t\t.reverse()\n\t\t.map((transition) => {\n\t\t\tconst stage = transition.stageId !== undefined ? `/${transition.stageId}` : \"\";\n\t\t\tconst detail = transition.reason.detail !== undefined ? ` — ${transition.reason.detail}` : \"\";\n\t\t\treturn `#${transition.revision} ${transition.workflowId}${stage} ${transition.from} -> ${transition.to} [${transition.trigger}] ${transition.reason.code}: ${transition.reason.message}${detail}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Structural renderResult (tool-result panorama face)\n// ---------------------------------------------------------------------------\n\n/** Structural host faces: declared locally so this module stays free of host imports. */\ninterface ToolResultTextPartLike {\n\treadonly type: string;\n\treadonly text?: string;\n}\n\ninterface ToolResultLike {\n\treadonly content: readonly ToolResultTextPartLike[];\n}\n\ninterface RenderResultOptionsLike {\n\treadonly expanded: boolean;\n\treadonly isPartial: boolean;\n}\n\ninterface ThemeLike {\n\tfg(color: string, text: string): string;\n}\n\ninterface RenderContextLike {\n\treadonly lastComponent?: unknown;\n}\n\n/**\n * Simplified East Asian fullwidth detection (wcwidth wide ranges, subset):\n * code points in these ranges occupy 2 terminal cells.\n */\nconst isWideCodePoint = (codePoint: number): boolean =>\n\t(codePoint >= 0x1100 && codePoint <= 0x115f) ||\n\t(codePoint >= 0x2e80 && codePoint <= 0xa4cf) ||\n\t(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||\n\t(codePoint >= 0xf900 && codePoint <= 0xfaff) ||\n\t(codePoint >= 0xfe30 && codePoint <= 0xfe4f) ||\n\t(codePoint >= 0xff00 && codePoint <= 0xff60) ||\n\t(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||\n\t(codePoint >= 0x20000 && codePoint <= 0x3fffd);\n\n/** Fold one logical line to `width` cells; wide code points count 2. */\nfunction foldLine(line: string, width: number): string[] {\n\tif (width <= 0) return [line];\n\tconst folded: string[] = [];\n\tlet current = \"\";\n\tlet used = 0;\n\tfor (const char of line) {\n\t\tconst cells = isWideCodePoint(char.codePointAt(0) ?? 0) ? 2 : 1;\n\t\tif (current !== \"\" && used + cells > width) {\n\t\t\tfolded.push(current);\n\t\t\tcurrent = char;\n\t\t\tused = cells;\n\t\t} else {\n\t\t\tcurrent += char;\n\t\t\tused += cells;\n\t\t}\n\t}\n\tfolded.push(current);\n\treturn folded;\n}\n\n/**\n * Structural tool-result component (host Component shape without importing\n * the TUI package): holds plain text plus a theme, colors `warning: ` lines\n * with the warning color and everything else as tool output, folds lines to\n * the render width, and caches the folded output per width.\n */\nclass WorkflowToolResultText {\n\tprivate readonly cache = new Map<number, string[]>();\n\tprivate text: string;\n\tprivate theme: ThemeLike;\n\n\tconstructor(text: string, theme: ThemeLike) {\n\t\tthis.text = text;\n\t\tthis.theme = theme;\n\t}\n\n\t/** New payload (or theme) on component reuse: drop every cached width. */\n\tupdate(text: string, theme: ThemeLike): void {\n\t\tthis.text = text;\n\t\tthis.theme = theme;\n\t\tthis.cache.clear();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst cached = this.cache.get(width);\n\t\tif (cached !== undefined) return cached;\n\t\tconst lines: string[] = [];\n\t\tfor (const rawLine of this.text.split(\"\\n\")) {\n\t\t\tfor (const folded of foldLine(rawLine, width)) {\n\t\t\t\tlines.push(\n\t\t\t\t\tfolded.startsWith(\"warning: \") ? this.theme.fg(\"warning\", folded) : this.theme.fg(\"toolOutput\", folded),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthis.cache.set(width, lines);\n\t\treturn lines;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cache.clear();\n\t}\n}\n\n/** Closures: one line per closure — the goal-achievement evidence summary. */\nexport function renderClosures(closures: readonly WorkflowClosure[]): string {\n\treturn closures\n\t\t.map((closure) => {\n\t\t\tconst goal = closure.goalId !== undefined ? ` (goal ${closure.goalId})` : \"\";\n\t\t\tconst evidence = closure.evidence.map((item) => `${item.stageId}/${item.acceptanceId}=${item.ref}`).join(\", \");\n\t\t\treturn `closure ${closure.workflowId}${goal}: ${evidence}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\n/** Inspections: one block per inspection — per-stage waiting conditions and acceptance coverage. */\nexport function renderInspections(inspections: readonly WorkflowInspection[]): string {\n\treturn inspections\n\t\t.map((inspection) => {\n\t\t\tconst lines = [`inspect ${inspection.workflowId} [${inspection.status}]`];\n\t\t\tfor (const stage of inspection.stages) {\n\t\t\t\tlet line = ` ${stage.stageId} [${stage.status}] attempts ${stage.attempts}`;\n\t\t\t\tif (stage.waitingOn !== null) line += ` — waiting: ${stage.waitingOn}`;\n\t\t\t\tif (stage.planId !== undefined) line += ` · plan ${stage.planId} (${stage.planStatus ?? \"unknown\"})`;\n\t\t\t\tlines.push(line);\n\t\t\t\tfor (const item of stage.acceptance) {\n\t\t\t\t\tlines.push(` ${item.id}: ${item.covered ? `covered (${item.refs.length} refs)` : \"missing\"}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn lines.join(\"\\n\");\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\n/** Parsed tool response view: the state base plus the optional response-only sections. */\ninterface WorkflowToolResponseView {\n\tstate: WorkflowState;\n\tclosures?: WorkflowClosure[];\n\tinspections?: WorkflowInspection[];\n\twarning?: string;\n}\n\n/**\n * Parses the host-serialized tool response. Only the `workflows` array key is\n * required (the state base); anything else is not a workflow tool response and\n * the caller falls back to rendering the raw text. Present-but-mistyped extra\n * fields (warning/closures/inspections) are shape mismatches and also reject\n * the parse (the todo/goal/plan gate semantics). Malformed records inside a\n * valid envelope are the renderer's territory, which catches them and falls\n * back to the raw text itself.\n */\nfunction parseWorkflowToolResponse(text: string): WorkflowToolResponseView | undefined {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(text);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return undefined;\n\tconst record = parsed as { workflows?: unknown; closures?: unknown; inspections?: unknown; warning?: unknown };\n\tif (!Array.isArray(record.workflows)) return undefined;\n\tif (record.closures !== undefined && !Array.isArray(record.closures)) return undefined;\n\tif (record.inspections !== undefined && !Array.isArray(record.inspections)) return undefined;\n\tif (record.warning !== undefined && typeof record.warning !== \"string\") return undefined;\n\treturn {\n\t\tstate: { workflows: record.workflows as WorkflowRecord[] },\n\t\t...(record.closures === undefined ? {} : { closures: record.closures as WorkflowClosure[] }),\n\t\t...(record.inspections === undefined ? {} : { inspections: record.inspections as WorkflowInspection[] }),\n\t\t...(record.warning === undefined ? {} : { warning: record.warning }),\n\t};\n}\n\n/** Structural renderResult signature as the host TUI invokes it. */\nexport type WorkflowToolResultRenderer = (\n\tresult: ToolResultLike,\n\toptions: RenderResultOptionsLike,\n\ttheme: ThemeLike,\n\tcontext: RenderContextLike,\n) => WorkflowToolResultText | undefined;\n\n/**\n * Shared renderResult of workflow_set and workflow_advance. Collapsed: the\n * panorama without diagnostics plus the warning line. Expanded: the full\n * panorama, closures, inspections, and the warning line (always last).\n * Non-workflow payloads and malformed records render as the raw text; a\n * missing or empty text part declines (undefined) so the host hides the\n * result. `context.lastComponent` instances are reused with the new payload.\n */\nexport function createWorkflowToolResultRenderer(deps: EngineDeps): WorkflowToolResultRenderer {\n\treturn (result, options, theme, context) => {\n\t\tconst text = result.content.find((part) => part.type === \"text\")?.text;\n\t\tif (text === undefined || text === \"\") return undefined;\n\t\tconst parsed = parseWorkflowToolResponse(text);\n\t\tlet body: string;\n\t\tif (parsed === undefined) {\n\t\t\tbody = text;\n\t\t} else {\n\t\t\t// The parse gate is envelope-level, so a parsed response may still\n\t\t\t// hold malformed records (a workflow without stages, a closure\n\t\t\t// without evidence). Rendering those must not throw out of the\n\t\t\t// plugin: fall back to the raw text, the same path as a parse failure.\n\t\t\ttry {\n\t\t\t\tconst sections = [\n\t\t\t\t\trenderPanorama(parsed.state, deps, options.expanded ? undefined : { diagnostics: false }),\n\t\t\t\t];\n\t\t\t\tif (options.expanded) {\n\t\t\t\t\tif (parsed.closures !== undefined) sections.push(renderClosures(parsed.closures));\n\t\t\t\t\tif (parsed.inspections !== undefined) sections.push(renderInspections(parsed.inspections));\n\t\t\t\t}\n\t\t\t\tif (parsed.warning !== undefined) sections.push(`warning: ${parsed.warning}`);\n\t\t\t\tbody = sections.join(\"\\n\\n\");\n\t\t\t} catch {\n\t\t\t\tbody = text;\n\t\t\t}\n\t\t}\n\t\tconst last = context.lastComponent;\n\t\tif (last instanceof WorkflowToolResultText) {\n\t\t\tlast.update(body, theme);\n\t\t\treturn last;\n\t\t}\n\t\treturn new WorkflowToolResultText(body, theme);\n\t};\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-agent-forge/plugin-workflow",
3
- "version": "0.87.0",
3
+ "version": "0.88.1",
4
4
  "description": "Workflow orchestration capability plugin for Agent Forge (stage DAG state machine over session entries)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,11 +44,10 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "typebox": "1.3.27",
47
- "@ai-agent-forge/plugin-sdk": "^0.87.0"
47
+ "@ai-agent-forge/plugin-sdk": "^0.88.1"
48
48
  },
49
49
  "devDependencies": {
50
50
  "shx": "0.4.0",
51
- "vitest": "4.1.11",
52
- "@ai-agent-forge/agent-forge": "^0.87.0"
51
+ "vitest": "4.1.11"
53
52
  }
54
53
  }
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "agent-forge.plugin.workflow",
3
- "version": "0.87.0",
3
+ "version": "0.88.1",
4
4
  "apiVersion": "1-draft",
5
5
  "entry": "./dist/entry.js",
6
6
  "hostVersion": ">=0.84.4",