@dieulc/workflow 0.5.0 → 0.5.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/README.md CHANGED
@@ -41,7 +41,7 @@ Manage it with `pi list`, `pi update --extensions`, `pi remove npm:@dieulc/workf
41
41
  | `/review-prompt [global]` | Edit the reviewer's guidance (defaults to "align with the existing framework and industry best practice") |
42
42
  | `/review-status` | Show review phase, model, plan hash, findings and any error |
43
43
  | `/review` | Run one review round against the current plan file now |
44
- | `/commit` | Commit staged changes with an AI-summarized message (`-a`: stage tracked changes first). Confirmation-gated |
44
+ | `/commit` | Commit staged changes with an AI-summarized message (`-a`: stage tracked changes first). Confirmation-gated; the confirmation arms the commit exemption for that run |
45
45
  | `/commit-policy [never\|ask\|allow] [global\|project]` | View or set the commit policy (default `never`: the agent cannot `git add`/`commit`/`push` — you commit, or use `/commit`) |
46
46
 
47
47
  ## Model Profiles
@@ -267,7 +267,7 @@ Settings live under `workflow.reviewMode` in `~/.pi/agent/settings.json` (global
267
267
 
268
268
  ## Commit policy
269
269
 
270
- By default the agent **cannot touch your git history**: the tool gate deterministically blocks `git add`, `git commit` and `git push` — including compound commands (`git add -A && git commit -m x`), global-option forms (`git -C path add`) and `powershell` wrappers. You commit manually, or run `/commit` for an AI-summarized, confirmation-gated commit (its own `git commit` runs under a one-turn bypass; `push` is never bypassed). Read-only git (`status`, `log`, `diff`, `branch -a`, `config --get`, …) stays free, and plan mode keeps its stricter read-only rules regardless.
270
+ By default the agent **cannot touch your git history**: the tool gate deterministically blocks `git add`, `git commit` and `git push` — including compound commands (`git add -A && git commit -m x`), global-option forms (`git -C path add`) and `powershell` wrappers. You commit manually, or run `/commit` for an AI-summarized, confirmation-gated commit. Read-only git (`status`, `log`, `diff`, `branch -a`, `config --get`, …) stays free, and plan mode keeps its stricter read-only rules regardless.
271
271
 
272
272
  The default exists because prompt-only rules are soft: models habitually commit per plan step, and "never commit unless asked" instructions in CLAUDE.md-style files are famously ignored (anthropics/claude-code#58883, #68724, #7029). Claude Code, Codex CLI and OpenCode all treat agent-initiated commits as something to gate or approve — so the policy here is enforced where commands execute, not in prose:
273
273
 
@@ -288,6 +288,16 @@ The default exists because prompt-only rules are soft: models habitually commit
288
288
 
289
289
  The status line always shows the active policy (`▶ build 2/5 · commits:never`), so you can see at a glance what the agent may do to history.
290
290
 
291
+ ### `/commit` under any policy
292
+
293
+ `/commit` works under `never` and `allow`-during-a-plan because it is the user's own request. The flow is multi-turn (read the staged diff → confirm the message in a questionnaire → commit), so the exemption is **armed at the user's confirmation**, not when the flow starts:
294
+
295
+ - `/commit` records a *pending flow* and injects its agent turn. Nothing is exempted yet.
296
+ - When the confirmation questionnaire is answered **positively** (✅ Commit, or a custom message), the extension arms a scoped *commit authorization*. Choosing ❌ Cancel clears the flow and arms nothing.
297
+ - The authorization exempts **only `git commit`** — `git add` and `git push` stay blocked — and is valid until the run settles (`agent_settled`), additionally bounded by a 10-minute TTL and a 10-commit cap. It is also dropped on `session_shutdown` and on rewind (`session_tree`).
298
+
299
+ The confirmation step is required: with no positive answer no authorization exists and the policy keeps blocking. `-a` staging happens extension-side before the flow, outside the tool gate.
300
+
291
301
  ```text
292
302
  /commit-policy # status: mode, which scope supplied it, both settings paths
293
303
  /commit-policy never # set globally (~/.pi/agent/settings.json)
package/commit-policy.ts CHANGED
@@ -321,6 +321,176 @@ export function stepCommitDecision(
321
321
  };
322
322
  }
323
323
 
324
+ // ── /commit authorization (scoped exemption from the gate) ───────────
325
+ //
326
+ // The /commit flow is multi-turn (read diff → confirm via questionnaire →
327
+ // commit), so a single-turn exemption is always disarmed before the commit
328
+ // runs. Instead the flow starts a PendingCommitFlow and the authorization is
329
+ // armed only when the user CONFIRMS in the extension-owned questionnaire
330
+ // tool. The authorization is valid until the run settles, bounded by a TTL and
331
+ // a commit cap, and exempts ONLY `git commit` (never `add`/`push`).
332
+
333
+ /** How long a /commit flow may sit awaiting the user's decision. */
334
+ export const COMMIT_FLOW_TTL_MS = 30 * 60 * 1000;
335
+
336
+ /** How long an armed authorization stays valid (run-settle is the primary bound). */
337
+ export const COMMIT_AUTH_TTL_MS = 10 * 60 * 1000;
338
+
339
+ /** Defense-in-depth cap on commits made under one /commit authorization. */
340
+ export const COMMIT_AUTH_MAX_COMMITS = 10;
341
+
342
+ let commitFlowCounter = 0;
343
+
344
+ /** A /commit flow awaiting the user's confirmation. */
345
+ export interface PendingCommitFlow {
346
+ id: string;
347
+ startedAt: number;
348
+ expiresAt: number;
349
+ }
350
+
351
+ /** An armed exemption from the commit gate, created at the user's confirmation. */
352
+ export interface CommitAuthorizationState {
353
+ flowId: string;
354
+ armedAt: number;
355
+ expiresAt: number;
356
+ commits: number;
357
+ maxCommits: number;
358
+ }
359
+
360
+ /** Start a new /commit flow (id is unique within the process). */
361
+ export function startCommitFlow(
362
+ now: number,
363
+ ttlMs: number = COMMIT_FLOW_TTL_MS,
364
+ ): PendingCommitFlow {
365
+ commitFlowCounter += 1;
366
+ return {
367
+ id: `commit-${now}-${commitFlowCounter}`,
368
+ startedAt: now,
369
+ expiresAt: now + ttlMs,
370
+ };
371
+ }
372
+
373
+ /** Arm the exemption for one flow. Never throws. */
374
+ export function armCommitAuthorization(input: {
375
+ flowId: string;
376
+ now: number;
377
+ ttlMs?: number;
378
+ maxCommits?: number;
379
+ }): CommitAuthorizationState {
380
+ const ttlMs = input.ttlMs ?? COMMIT_AUTH_TTL_MS;
381
+ return {
382
+ flowId: input.flowId,
383
+ armedAt: input.now,
384
+ expiresAt: input.now + ttlMs,
385
+ commits: 0,
386
+ maxCommits: input.maxCommits ?? COMMIT_AUTH_MAX_COMMITS,
387
+ };
388
+ }
389
+
390
+ /** True only while an authorization is present, unexpired and under its cap. */
391
+ export function isAuthorizationActive(
392
+ auth: CommitAuthorizationState | null | undefined,
393
+ now: number,
394
+ ): boolean {
395
+ if (!auth) return false;
396
+ if (!(now < auth.expiresAt)) return false;
397
+ return auth.commits < auth.maxCommits;
398
+ }
399
+
400
+ /** A normalized questionnaire question (only the fields the detector needs). */
401
+ export interface CommitConfirmationQuestion {
402
+ question?: string;
403
+ header?: string;
404
+ options?: { label?: string; description?: string }[];
405
+ }
406
+
407
+ /**
408
+ * Is this questionnaire the /commit confirmation? Signals, in order: the
409
+ * "Commit" header, the canonical question text, or the "✅ Commit" option
410
+ * label. Deliberately tolerant — a missed match leaves the policy blocking
411
+ * (fail safe), so false negatives are a UX regression, false positives a risk.
412
+ */
413
+ export function isCommitConfirmation(
414
+ questions: readonly CommitConfirmationQuestion[] | undefined,
415
+ ): boolean {
416
+ if (!Array.isArray(questions)) return false;
417
+ return questions.some((q) => {
418
+ if (!q || typeof q !== "object") return false;
419
+ if (typeof q.header === "string" && /^commit$/i.test(q.header.trim())) {
420
+ return true;
421
+ }
422
+ if (
423
+ typeof q.question === "string" &&
424
+ /commit these staged changes/i.test(q.question)
425
+ ) {
426
+ return true;
427
+ }
428
+ return (q.options ?? []).some(
429
+ (o: { label?: string; description?: string }) =>
430
+ typeof o?.label === "string" && /✅\s*commit/i.test(o.label),
431
+ );
432
+ });
433
+ }
434
+
435
+ /** One questionnaire answer, as far as the decision needs to know. */
436
+ export interface CommitConfirmationAnswer {
437
+ answer?: string;
438
+ wasCustom?: boolean;
439
+ }
440
+
441
+ /**
442
+ * Did the user authorize the commit? Cancel (`cancelled` or a ❌/cancel-only
443
+ * label) never arms. Freeform text always arms (it is the user's own commit
444
+ * message) unless it is exactly a cancel token. A canned label arms unless it
445
+ * reads as cancel without commit.
446
+ */
447
+ export function isPositiveCommitDecision(input: {
448
+ cancelled?: boolean;
449
+ answer?: CommitConfirmationAnswer;
450
+ }): boolean {
451
+ if (input?.cancelled) return false;
452
+ const answer = input?.answer;
453
+ if (!answer) return false;
454
+ const label = String(answer.answer ?? "").trim();
455
+ if (!label) return false;
456
+ const isBareCancel = /^(❌\s*)?cancel[.!]?$/i.test(label);
457
+ if (isBareCancel) return false;
458
+ if (answer.wasCustom) return true;
459
+ if (/\bcancel\b/i.test(label) && !/\bcommit\b/i.test(label)) return false;
460
+ return true;
461
+ }
462
+
463
+ /** The commit-policy gate outcome for one tool call. */
464
+ export type CommitGateDecision = "allow" | "block" | "ask";
465
+
466
+ /** Git subcommands the commit policy gates (mirrors permissions.ts CommitPolicyHit). */
467
+ export type CommitGateHit = "add" | "commit" | "push";
468
+
469
+ export interface CommitGateInput {
470
+ policy: CommitPolicyMode;
471
+ hit: CommitGateHit;
472
+ hasTodos: boolean;
473
+ auth?: CommitAuthorizationState | null;
474
+ now: number;
475
+ }
476
+
477
+ /**
478
+ * The pure commit-policy decision. Ordering mirrors the original inline gate:
479
+ * an `allow` policy with no active plan owns nothing → allow; `ask` keeps
480
+ * fresh staging free; an armed authorization exempts ONLY `git commit`; then
481
+ * never/allow block and anything left (`ask`) prompts. Never throws.
482
+ */
483
+ export function decideCommitGate(input: CommitGateInput): CommitGateDecision {
484
+ const { policy, hit, hasTodos } = input;
485
+ if (policy === "allow" && !hasTodos) return "allow";
486
+ if (policy === "ask" && hit === "add") return "allow";
487
+ if (hit === "commit" && isAuthorizationActive(input.auth, input.now)) {
488
+ return "allow";
489
+ }
490
+ if (policy === "never" || policy === "allow") return "block";
491
+ return "ask";
492
+ }
493
+
324
494
  /**
325
495
  * Read-modify-write `workflow.commitPolicy` into a settings file, preserving
326
496
  * every other key in that file (Pi owns settings.json — we must not clobber it).
package/index.ts CHANGED
@@ -89,12 +89,24 @@ import {
89
89
  type WorkflowRole,
90
90
  } from "./permissions.ts";
91
91
  import {
92
+ COMMIT_AUTH_MAX_COMMITS,
93
+ COMMIT_AUTH_TTL_MS,
94
+ COMMIT_FLOW_TTL_MS,
95
+ armCommitAuthorization,
92
96
  commitConfigPaths,
97
+ decideCommitGate,
93
98
  describeCommitPolicyMode,
99
+ isAuthorizationActive,
100
+ isCommitConfirmation,
101
+ isPositiveCommitDecision,
94
102
  readCommitPolicyConfig,
103
+ startCommitFlow,
95
104
  stepCommitDecision,
96
105
  writeCommitPolicyConfig,
106
+ type CommitAuthorizationState,
107
+ type CommitGateHit,
97
108
  type CommitPolicyMode,
109
+ type PendingCommitFlow,
98
110
  type StepCommitCandidate,
99
111
  } from "./commit-policy.ts";
100
112
  import {
@@ -400,11 +412,19 @@ export default function workflowExtension(pi: ExtensionAPI) {
400
412
  let todoItems: TodoItem[] = [];
401
413
  // Plan file the current todo list was derived from (drives merge vs replace).
402
414
  let todoPlanPath: string | undefined;
403
- // Armed while the user-invoked /commit flow's agent turn is running: its
404
- // `git commit` bypasses the commit-policy gate (staging already happened
405
- // extension-side via piExecGit, outside the tool gate). Cleared on
406
- // turn_end/agent_settled so the window stays one turn wide.
407
- let commitBypassActive = false;
415
+ // ── /commit flow authorization ──
416
+ // The /commit flow is multi-turn (read diff → confirm → commit), so a
417
+ // single-turn exemption is always disarmed before the commit runs. Instead:
418
+ // pendingCommitFlow — set when /commit injects its agent turn, cleared on
419
+ // the user's confirmation (or a negative/cancel answer),
420
+ // so only THIS flow's questionnaire can arm.
421
+ // commitAuthorization — armed only when the user confirms in that
422
+ // questionnaire; exempts ONLY `git commit` (never
423
+ // add/push) until the run settles, bounded by a TTL
424
+ // and a commit cap.
425
+ // Both are cleared on agent_settled, session_shutdown and session_tree.
426
+ let pendingCommitFlow: PendingCommitFlow | null = null;
427
+ let commitAuthorization: CommitAuthorizationState | null = null;
408
428
  // ── Deterministic per-step commits (allow mode) ──
409
429
  // In 'allow' mode the EXTENSION — not the model — owns commit timing:
410
430
  // exactly one commit per batch of newly completed plan steps, made at the
@@ -2532,16 +2552,28 @@ export default function workflowExtension(pi: ExtensionAPI) {
2532
2552
 
2533
2553
  // Inject a user message → triggers an agent turn that proposes + confirms + commits
2534
2554
  const message = buildCommitPrompt(statSummary, stageAll);
2535
- // Arm the commit-policy bypass for exactly this agent turn: its `git
2536
- // commit` must run even under policy 'never'. Disarmed on turn_end /
2537
- // agent_settled, so the window is one turn wide.
2538
- commitBypassActive = true;
2539
- if ((pi as any).sendUserMessage) {
2540
- (pi as any).sendUserMessage(message);
2541
- } else {
2542
- (pi as any).sendMessage?.(
2543
- { customType: "workflow-commit", content: message, display: true },
2544
- { triggerTurn: true, deliverAs: "followUp" },
2555
+ // Start a commit flow. The exemption is NOT armed here: it is armed only
2556
+ // when the user confirms in THIS flow's questionnaire (see the
2557
+ // questionnaire tool), so an unconfirmed commit stays blocked under any
2558
+ // policy. A flow TTL bounds an abandoned confirmation.
2559
+ pendingCommitFlow = startCommitFlow(Date.now(), COMMIT_FLOW_TTL_MS);
2560
+ commitAuthorization = null;
2561
+ try {
2562
+ if ((pi as any).sendUserMessage) {
2563
+ await (pi as any).sendUserMessage(message);
2564
+ } else {
2565
+ await (pi as any).sendMessage?.(
2566
+ { customType: "workflow-commit", content: message, display: true },
2567
+ { triggerTurn: true, deliverAs: "followUp" },
2568
+ );
2569
+ }
2570
+ } catch (e: any) {
2571
+ // The turn never started → no questionnaire can arm; do not leave a
2572
+ // stale flow behind.
2573
+ pendingCommitFlow = null;
2574
+ ctx.ui.notify(
2575
+ `Failed to start the commit flow: ${String(e?.message ?? e ?? e)}`,
2576
+ "error",
2545
2577
  );
2546
2578
  }
2547
2579
  }
@@ -5801,6 +5833,34 @@ export default function workflowExtension(pi: ExtensionAPI) {
5801
5833
  overlayActive = false;
5802
5834
  }
5803
5835
 
5836
+ // /commit authorization: arm the commit-policy exemption ONLY when the
5837
+ // user confirms THIS flow's commit questionnaire. A cancel (or any
5838
+ // non-confirmation questionnaire) clears the pending flow and arms
5839
+ // nothing, so the policy keeps blocking. Headless never reaches here
5840
+ // (early return above), which is the intended fail-closed behaviour.
5841
+ if (pendingCommitFlow && isCommitConfirmation(questions)) {
5842
+ const cancelled = !result || result.cancelled === true;
5843
+ const answer =
5844
+ !result || result.cancelled ? undefined : result.answers?.[0];
5845
+ if (isPositiveCommitDecision({ cancelled, answer })) {
5846
+ commitAuthorization = armCommitAuthorization({
5847
+ flowId: pendingCommitFlow.id,
5848
+ now: Date.now(),
5849
+ ttlMs: COMMIT_AUTH_TTL_MS,
5850
+ maxCommits: COMMIT_AUTH_MAX_COMMITS,
5851
+ });
5852
+ try {
5853
+ ctx.ui.notify?.(
5854
+ "Commit authorized — this run may commit the staged changes.",
5855
+ "info",
5856
+ );
5857
+ } catch (_e) {
5858
+ void _e;
5859
+ }
5860
+ }
5861
+ pendingCommitFlow = null;
5862
+ }
5863
+
5804
5864
  if (!result || result.cancelled) {
5805
5865
  return {
5806
5866
  content: [{ type: "text", text: "User cancelled questionnaire" }],
@@ -6421,6 +6481,10 @@ export default function workflowExtension(pi: ExtensionAPI) {
6421
6481
  }
6422
6482
  ctx.ui.setStatus("workflow", undefined);
6423
6483
  ctx.ui.setWidget("workflow-todos", undefined);
6484
+ // Drop any /commit authorization or flow: a reload/quit must never leave
6485
+ // an exemption armed for whatever runs next.
6486
+ commitAuthorization = null;
6487
+ pendingCommitFlow = null;
6424
6488
  // Release the reviewer session (and its custom tools) on shutdown so a
6425
6489
  // reload or quit never leaves an orphaned in-process session behind.
6426
6490
  await teardownReview();
@@ -6432,6 +6496,9 @@ export default function workflowExtension(pi: ExtensionAPI) {
6432
6496
  todoItems = [];
6433
6497
  todoPlanPath = undefined;
6434
6498
  btwNotes = [];
6499
+ // A rewind must not preserve a /commit exemption across branches.
6500
+ commitAuthorization = null;
6501
+ pendingCommitFlow = null;
6435
6502
  try {
6436
6503
  const branch = ctx.sessionManager.getBranch();
6437
6504
  for (const e of branch as any[]) {
@@ -6468,32 +6535,52 @@ export default function workflowExtension(pi: ExtensionAPI) {
6468
6535
  * allow → automatic per-step commits while a plan todo list is active
6469
6536
  * (the extension owns commit timing — model-initiated git add/commit/push
6470
6537
  * are blocked); outside a plan the model may commit as before. The
6471
- * user-invoked /commit flow arms `commitBypassActive` so its own `git
6472
- * commit` runs (staging is extension-side; `push` and `add` are never
6473
- * bypassed).
6538
+ * user-invoked /commit flow arms `commitAuthorization` ONLY when the user
6539
+ * confirms its questionnaire; that exemption covers just `git commit` for
6540
+ * the rest of the run (staging is extension-side; `push` and `add` are
6541
+ * never exempted).
6542
+ *
6543
+ * The decision itself is the pure `decideCommitGate`; this wrapper supplies
6544
+ * the runtime inputs and renders the block reasons / ask overlay.
6474
6545
  */
6475
6546
  async function commitPolicyGate(
6476
6547
  event: any,
6477
6548
  ctx: any,
6478
6549
  cwd: string,
6479
6550
  ): Promise<{ block: true; reason: string } | undefined> {
6480
- const policy = readCommitPolicyConfig(cwd, getAgentDir()).config.mode;
6481
- // allow outside a plan: no per-step automation to own commit timing, so
6482
- // the model keeps its previous freedom.
6483
- if (policy === "allow" && todoItems.length === 0) return undefined;
6484
6551
  const cmd = String(event?.input?.command ?? "");
6485
6552
  const hit = gitCommitPolicyHit(cmd);
6486
6553
  if (!hit) return undefined;
6487
- // `add` is only gated in `never` mode; `ask` prompts on commit/push only
6488
- // (staging alone pollutes nothing).
6489
- if (policy === "ask" && hit === "add") return undefined;
6490
- // /commit bypass: only the flow's own `git commit` is exempt — `add` and
6491
- // `push` are not.
6492
- if (commitBypassActive && hit === "commit") {
6554
+ const policy = readCommitPolicyConfig(cwd, getAgentDir()).config.mode;
6555
+ const now = Date.now();
6556
+ const decision = decideCommitGate({
6557
+ policy,
6558
+ hit: hit as CommitGateHit,
6559
+ hasTodos: todoItems.length > 0,
6560
+ auth: commitAuthorization,
6561
+ now,
6562
+ });
6563
+ if (decision === "allow") {
6564
+ // Count a granted `git commit` against the authorization, but only when
6565
+ // the authorization was actually the deciding factor (never/ask, or
6566
+ // allow-with-an-active-plan). `allow` outside a plan is free regardless.
6567
+ const authWasDecisive =
6568
+ hit === "commit" &&
6569
+ commitAuthorization !== null &&
6570
+ !(policy === "allow" && todoItems.length === 0);
6571
+ if (authWasDecisive && commitAuthorization) {
6572
+ const auth = commitAuthorization;
6573
+ if (!isAuthorizationActive(auth, now)) {
6574
+ commitAuthorization = null;
6575
+ } else {
6576
+ const next = { ...auth, commits: auth.commits + 1 };
6577
+ commitAuthorization = next.commits >= next.maxCommits ? null : next;
6578
+ }
6579
+ }
6493
6580
  return undefined;
6494
6581
  }
6495
6582
 
6496
- if (policy === "never" || policy === "allow") {
6583
+ if (decision === "block") {
6497
6584
  return {
6498
6585
  block: true,
6499
6586
  reason:
@@ -6507,7 +6594,7 @@ export default function workflowExtension(pi: ExtensionAPI) {
6507
6594
  cmd,
6508
6595
  };
6509
6596
  }
6510
- // policy === "ask" → confirmation overlay. Headless has no one to ask →
6597
+ // decision === "ask" → confirmation overlay. Headless has no one to ask →
6511
6598
  // fail closed rather than commit unbidden.
6512
6599
  const ui = ctx?.ui;
6513
6600
  if (!ui || !ctx.hasUI) {
@@ -6886,9 +6973,11 @@ export default function workflowExtension(pi: ExtensionAPI) {
6886
6973
  });
6887
6974
 
6888
6975
  pi.on("agent_settled", async () => {
6889
- // Belt-and-braces: the run is over (completed, aborted or errored) — the
6890
- // /commit bypass must not leak into whatever runs next.
6891
- commitBypassActive = false;
6976
+ // The run is over (completed, aborted or errored). The /commit flow lives
6977
+ // within one run, so both the armed exemption and any unconfirmed flow are
6978
+ // dropped here — nothing that runs next inherits them.
6979
+ commitAuthorization = null;
6980
+ pendingCommitFlow = null;
6892
6981
  // keep persisted CustomEntry for reload, clear in-memory tombstone only on explicit prune; for now no clear to survive fork
6893
6982
  });
6894
6983
 
@@ -7067,10 +7156,10 @@ export default function workflowExtension(pi: ExtensionAPI) {
7067
7156
 
7068
7157
  // Track progress (all modes, whenever todos exist)
7069
7158
  pi.on("turn_end", async (event, ctx) => {
7070
- // Disarm the /commit bypass as soon as any turn ends: the /commit flow's
7071
- // `git commit` runs as a tool call WITHIN its turn, so this never disarms
7072
- // too early — and nothing after the turn inherits the exemption.
7073
- commitBypassActive = false;
7159
+ // NOTE: do NOT disarm the /commit authorization here. `turn_end` fires
7160
+ // after EVERY assistant response, but /commit is multi-turn (read diff →
7161
+ // confirm → commit), so disarming per turn kills the exemption before the
7162
+ // commit turn. Lifetime is the run: `agent_settled` clears it.
7074
7163
  if (todoItems.length === 0) return;
7075
7164
  if (!isAssistantMessage(event.message as AgentMessage)) return;
7076
7165
  const text = getTextContent(event.message as AssistantMessage);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dieulc/workflow",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "keywords": ["pi-package", "workflow", "plan-mode", "review-mode", "questionnaire", "rewind"],