@agimon-ai/doompi-workflow 0.0.1-alpha.125 → 0.0.1-alpha.127

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -108,12 +108,15 @@ session creates a child **workflow session** (provenance `workflow-session`), ne
108
108
  rail, and hands it the launch. The workflow session owns the run (`PI_SESSION_ID`), runs its step
109
109
  sessions, and has Workflow mode on, so its agent can troubleshoot and recover the run. The run's
110
110
  environment also carries `DOOMPI_WORKFLOW_LAUNCHER_SESSION_ID`, naming the launching session, which
111
- lists the run in its Activity group, is told when it ends, and may read its status.
111
+ lists the run in its Activity group, is told when it completes, and may read its status. Failures only
112
+ raise a UI toast there, without adding a message to the launching agent's context.
112
113
 
113
114
  - The workflow session's rail row shows the run (`workflow · build › test · 4m`, or a failure or
114
115
  pause marked like a pending question) through the host's per-session activity line.
115
- - A failure or finish is posted into the workflow session's conversation as a notice, without
116
- starting an agent turn. The owner agent also gets a live brief of its runs on every turn.
116
+ - A failure starts a turn in the workflow session to diagnose and report locally. Recovery requires
117
+ a user request or explicit launcher delegation. Completion is posted without starting a turn,
118
+ and completion after recovery still notifies the launcher. The owner agent also gets a live brief
119
+ of its runs on every turn.
117
120
  - After a success, a workflow session nobody interacted with asks its launcher to release it, and it
118
121
  becomes `stopped · open to wake`. A session whose run failed or paused, or whose agent you
119
122
  prompted, stays live until you remove it. Removing or restarting a live workflow session stops its
@@ -175,3 +178,9 @@ readiness discovery; dependent calls await that session's readiness. On shutdown
175
178
  also handles startup failure. Both use the same idempotent cleanup. A generation
176
179
  fence prevents retired callbacks from sending messages or updating the UI; only
177
180
  explicit resource teardown may use a retired UI context.
181
+
182
+ ## Step completion
183
+
184
+ In-process steps wait, without a timeout or decision reminders, for the child session to be idle with no queued messages or owned subagent/task/runner work. Cancellation interrupts this wait. Missing coordinator snapshots and holding-provider errors defer the decision gate; a failed inspection fails the step. Workflow monitoring itself does not hold completion.
185
+
186
+ Pi terminal steps must load this package: its native settled handler owns the decision gate, not generic `hooks.yaml`. It skips host-steered sessions and subagents, and defers the gate while owned background work remains. Missing pane coordinators retain the no-work fallback; snapshot failures defer gating with a diagnostic.
@@ -2,17 +2,18 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_workflowTerminal = require("../workflowTerminal-DeJ9BBta.cjs");
5
+ const require_workflowTerminal = require("../workflowTerminal-ILzqDAL9.cjs");
6
6
  const require_workflowLaunchCommand = require("../workflowLaunchCommand-DJyDB96d.cjs");
7
7
  let _agimon_ai_doompi_core_piExtension = require("@agimon-ai/doompi-core/piExtension");
8
8
  let _agimon_ai_doompi_core_extensionFile = require("@agimon-ai/doompi-core/extensionFile");
9
+ let _agimon_ai_doompi_core_backgroundWork = require("@agimon-ai/doompi-core/backgroundWork");
9
10
  let _agimon_ai_doompi_core_skills = require("@agimon-ai/doompi-core/skills");
10
11
  let _agimon_ai_doompi_core_uiHub = require("@agimon-ai/doompi-core/uiHub");
12
+ let _agimon_ai_workflow_mcp = require("@agimon-ai/workflow-mcp");
11
13
  let _agimon_ai_doompi_core_childProcess = require("@agimon-ai/doompi-core/childProcess");
12
14
  let node_fs = require("node:fs");
13
15
  let node_fs_promises = require("node:fs/promises");
14
16
  let node_path = require("node:path");
15
- let _agimon_ai_workflow_mcp = require("@agimon-ai/workflow-mcp");
16
17
  let zod = require("zod");
17
18
  let node_crypto = require("node:crypto");
18
19
  let _agimon_ai_doompi_ui_toolChrome = require("@agimon-ai/doompi-ui/toolChrome");
@@ -23,6 +24,18 @@ let _agimon_ai_doompi_core_readiness = require("@agimon-ai/doompi-core/readiness
23
24
  let _agimon_ai_doompi_minor_mode = require("@agimon-ai/doompi-minor-mode");
24
25
  let _agimon_ai_doompi_telemetry = require("@agimon-ai/doompi-telemetry");
25
26
  let _earendil_works_pi_tui = require("@earendil-works/pi-tui");
27
+ //#region src/services/stepStopGate/index.ts
28
+ function isStepStopGateEligible(environment) {
29
+ return Boolean(environment[_agimon_ai_workflow_mcp.WORKFLOW_DECISION_FILE_ENV] && environment[_agimon_ai_workflow_mcp.WORKFLOW_DECISION_STEERING_ENV] !== _agimon_ai_workflow_mcp.WORKFLOW_DECISION_STEERING_HOST && !environment.PI_SUBAGENT_CHILD);
30
+ }
31
+ /** A pane's root agent may settle to wait without spending a workflow reminder. */
32
+ async function stepStopGate(environment, snapshot) {
33
+ if (!isStepStopGateEligible(environment)) return void 0;
34
+ if (snapshot?.items.some(_agimon_ai_doompi_core_backgroundWork.holdsSettledSession) || snapshot?.errors.some((error) => _agimon_ai_doompi_core_backgroundWork.BACKGROUND_WORK_HOLDING_PROVIDERS.includes(error.provider))) return void 0;
35
+ const gate = await (0, _agimon_ai_workflow_mcp.gateStepDecision)(environment);
36
+ return gate.action === "block" ? gate.message : void 0;
37
+ }
38
+ //#endregion
26
39
  //#region src/services/workflowTools/index.ts
27
40
  const PI_LAUNCH_FIELDS = {
28
41
  workflowPath: true,
@@ -3485,6 +3498,7 @@ function createWorkflowPiRuntime(pi, options) {
3485
3498
  const startedDuringSession = previous === void 0 && (sessionStartedAt === 0 || !Number.isFinite(startedAt) || startedAt >= sessionStartedAt);
3486
3499
  if (lifecycleNarrationReady && require_workflowTerminal.isSessionRun(record, sessionId)) narrateWorkflowTransition(narrationSink, record, previous === RUNNING_STATUS || previous === COMPLETED_STATUS || previous === ERROR_STAGE ? previous : void 0, startedDuringSession);
3487
3500
  if (!notifyChanges || !rootProcess || record.stage === RUNNING_STATUS) continue;
3501
+ if (record.stage === ERROR_STAGE && require_workflowTerminal.isLaunchedRun(record, sessionId)) continue;
3488
3502
  if (!deliveredTerminalRuns.has(identity) && (previous === RUNNING_STATUS || startedDuringSession)) pendingTerminalRuns.set(identity, record);
3489
3503
  }
3490
3504
  const retainedWork = new Map(mine.map((record) => [runIdentity(record), record]));
@@ -3513,8 +3527,7 @@ function createWorkflowPiRuntime(pi, options) {
3513
3527
  }
3514
3528
  const identity = runIdentity(record);
3515
3529
  const events = runEvents.get(identity) ?? progressEventsForRun(record, await (0, _agimon_ai_workflow_mcp.readWorkflowProgress)(registry.runDirectoryFor(record)));
3516
- const summary = require_workflowTerminal.finishedRunSummary(record, (0, _agimon_ai_workflow_mcp.summarizeWorkflowProgress)(events));
3517
- return require_workflowTerminal.isLaunchedRun(record, sessionId) ? `${summary}\n${require_workflowTerminal.launchedRunHint(record)}` : summary;
3530
+ return require_workflowTerminal.finishedRunSummary(record, (0, _agimon_ai_workflow_mcp.summarizeWorkflowProgress)(events));
3518
3531
  }));
3519
3532
  if (!refreshIsCurrent()) return;
3520
3533
  const message = (indexes) => ({
@@ -3536,8 +3549,8 @@ function createWorkflowPiRuntime(pi, options) {
3536
3549
  })
3537
3550
  }
3538
3551
  });
3539
- const quiet = terminalRecords.flatMap((record, index) => require_workflowTerminal.isOwnedDelegatedRun(record, sessionId) ? [index] : []);
3540
- const loud = terminalRecords.flatMap((record, index) => require_workflowTerminal.isOwnedDelegatedRun(record, sessionId) ? [] : [index]);
3552
+ const quiet = terminalRecords.flatMap((record, index) => require_workflowTerminal.isOwnedDelegatedRun(record, sessionId) && record.stage !== ERROR_STAGE ? [index] : []);
3553
+ const loud = terminalRecords.flatMap((record, index) => require_workflowTerminal.isOwnedDelegatedRun(record, sessionId) && record.stage !== ERROR_STAGE ? [] : [index]);
3541
3554
  if (quiet.length > 0) pi.sendMessage(message(quiet), { triggerTurn: false });
3542
3555
  if (loud.length > 0) pi.sendMessage(message(loud), {
3543
3556
  triggerTurn: true,
@@ -4521,6 +4534,7 @@ function createWorkflowPiRuntime(pi, options) {
4521
4534
  var index_cli_default = (({ pi, context, options, signal }) => {
4522
4535
  const environment = options?.environment ?? Object.freeze({ ...process.env });
4523
4536
  const dispatcher = require_workflowTerminal.isWorkflowDispatcherProcess(environment);
4537
+ let backgroundWork;
4524
4538
  const fence = require_workflowTerminal.createWorkflowFence(pi, signal);
4525
4539
  let workflowMode = false;
4526
4540
  let leader;
@@ -4547,6 +4561,13 @@ var index_cli_default = (({ pi, context, options, signal }) => {
4547
4561
  const selectedTools = dispatcher ? require_workflowTerminal.dispatcherTools(nativeTools, require_workflowTerminal.resolveDispatcherParentSession(environment), environment) : nativeTools;
4548
4562
  return {
4549
4563
  services: [...runtime.services ?? [], (cordis) => {
4564
+ cordis.inject([_agimon_ai_doompi_core_backgroundWork.DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {
4565
+ const service = (0, _agimon_ai_doompi_core_backgroundWork.readDoomBackgroundWorkService)(serviceContext);
4566
+ backgroundWork = service;
4567
+ return () => {
4568
+ if (backgroundWork === service) backgroundWork = void 0;
4569
+ };
4570
+ });
4550
4571
  cordis.inject([_agimon_ai_doompi_core_skills.DOOM_SKILL_SOURCES_SERVICE], (skillContext) => {
4551
4572
  const directory = require_workflowTerminal.workflowSkillDirectory();
4552
4573
  const contribution = (0, _agimon_ai_doompi_core_skills.requireDoomSkillSourcesService)(skillContext).register({
@@ -4573,7 +4594,32 @@ var index_cli_default = (({ pi, context, options, signal }) => {
4573
4594
  }))),
4574
4595
  commands: runtime.commands.map(([name, command]) => fence.command(name, command)),
4575
4596
  shortcuts: runtime.shortcuts?.map(([key, shortcut]) => fence.shortcut(key, shortcut)),
4576
- events: fence.events(runtime.events ?? {}),
4597
+ events: fence.events({
4598
+ ...runtime.events,
4599
+ async agent_settled(_event, ctx) {
4600
+ if (!fence.isCurrentInvocation() || !isStepStopGateEligible(environment)) return;
4601
+ let snapshot;
4602
+ try {
4603
+ snapshot = backgroundWork?.snapshot(ctx.sessionManager.getSessionId());
4604
+ } catch (error) {
4605
+ pi.sendMessage({
4606
+ customType: "workflow-step",
4607
+ content: `Cannot inspect step background work: ${String(error)}`,
4608
+ display: true
4609
+ });
4610
+ return;
4611
+ }
4612
+ const message = await stepStopGate(environment, snapshot);
4613
+ if (message && fence.isCurrentInvocation()) pi.sendMessage({
4614
+ customType: "workflow-step",
4615
+ content: message,
4616
+ display: true
4617
+ }, {
4618
+ triggerTurn: true,
4619
+ deliverAs: "followUp"
4620
+ });
4621
+ }
4622
+ }),
4577
4623
  toolRestrictions: [...runtime.toolRestrictions ?? [], ...dispatcher ? [{
4578
4624
  source: `${require_workflowTerminal.PACKAGE_SOURCE}/dispatcher`,
4579
4625
  restrict: require_workflowTerminal.dispatcherToolRestriction()
@@ -4618,6 +4664,9 @@ var root_cli_default = (0, _agimon_ai_doompi_core_extensionFile.defineRoot)((con
4618
4664
  //#region src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.ts
4619
4665
  var workflow_launch_cli_default = (0, _agimon_ai_doompi_core_extensionFile.defineRoutedContribution)((context) => context.root.commands[0], { cardinality: "optional" });
4620
4666
  //#endregion
4667
+ //#region src/extensions/workspaces/sessions/(backend)/hook/agent-settled.cli.ts
4668
+ var agent_settled_cli_default = (0, _agimon_ai_doompi_core_extensionFile.defineRoutedContribution)((context) => context.root.events?.agent_settled, { cardinality: "optional" });
4669
+ //#endregion
4621
4670
  //#region src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.ts
4622
4671
  var resources_discover_cli_default = (0, _agimon_ai_doompi_core_extensionFile.defineRoutedContribution)((context) => context.root.events?.resources_discover, { cardinality: "optional" });
4623
4672
  //#endregion
@@ -4750,6 +4799,7 @@ const extension = (0, _agimon_ai_doompi_core_piExtension.definePiExtension)("@ag
4750
4799
  return defined([at(workflow_launch_cli_default, contextSession)]);
4751
4800
  },
4752
4801
  events: {
4802
+ ...keyed("agent_settled", at(agent_settled_cli_default, contextSession)),
4753
4803
  ...keyed("resources_discover", at(resources_discover_cli_default, contextSession)),
4754
4804
  ...keyed("session_start", at(session_start_cli_default, contextSession))
4755
4805
  },