pi-usereq 0.50.0 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/core/extension-status.ts +13 -4
- package/src/index.ts +105 -71
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
ThemeColor,
|
|
15
15
|
} from "@mariozechner/pi-coding-agent";
|
|
16
16
|
import type { UseReqConfig } from "./config.js";
|
|
17
|
-
import type { PiNotifySoundLevel } from "./pi-notify.js";
|
|
17
|
+
import type { PiNotifyOutcome, PiNotifySoundLevel } from "./pi-notify.js";
|
|
18
18
|
import type { PromptCommandExecutionPlan } from "./prompt-command-runtime.js";
|
|
19
19
|
import {
|
|
20
20
|
restorePersistedPromptCommandRuntimeStateForSession,
|
|
@@ -77,6 +77,7 @@ export const PI_USEREQ_STATUS_HOOK_NAMES = [
|
|
|
77
77
|
"before_agent_start",
|
|
78
78
|
"agent_start",
|
|
79
79
|
"agent_end",
|
|
80
|
+
"agent_settled",
|
|
80
81
|
"turn_start",
|
|
81
82
|
"turn_end",
|
|
82
83
|
"message_start",
|
|
@@ -124,6 +125,7 @@ export interface PiUsereqStatusState {
|
|
|
124
125
|
runtimeSoundLevel: PiNotifySoundLevel | undefined;
|
|
125
126
|
pendingPromptRequest: PiUsereqPromptRequest | undefined;
|
|
126
127
|
activePromptRequest: PiUsereqPromptRequest | undefined;
|
|
128
|
+
pendingFinalizationOutcome: PiNotifyOutcome | undefined;
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
/**
|
|
@@ -680,6 +682,7 @@ export function createPiUsereqStatusController(): PiUsereqStatusController {
|
|
|
680
682
|
runtimeSoundLevel: undefined,
|
|
681
683
|
pendingPromptRequest: undefined,
|
|
682
684
|
activePromptRequest: undefined,
|
|
685
|
+
pendingFinalizationOutcome: undefined,
|
|
683
686
|
},
|
|
684
687
|
tickHandle: undefined,
|
|
685
688
|
};
|
|
@@ -802,7 +805,7 @@ export function setPiUsereqWorkflowState(
|
|
|
802
805
|
|
|
803
806
|
/**
|
|
804
807
|
* @brief Updates mutable status state for one intercepted lifecycle hook.
|
|
805
|
-
* @details Refreshes stored context usage on every hook, resets or restores persisted elapsed counters during `session_start`, loads the active runtime sound level from persisted config during `session_start`, restores persisted prompt-command metadata when the active session matches a forked execution session, resynchronizes that metadata on later lifecycle hooks so post-switch workflow transitions performed by the initiating command handler become visible to the replacement-session runtime, resets workflow state to `idle` for documented session-start reasons, starts run timing on `agent_start`, promotes pending prompt-request metadata into the active run, captures non-aborted run duration on `agent_end`, accumulates successful runtime into `Σ`, preserves in-memory prompt-command state plus process-scoped persistence across switch-triggered `session_shutdown`, tolerates stale post-replacement render contexts, synchronizes the live ticker, and re-renders the status bar with the runtime extension identity prefix when configuration is available. Runtime is O(n) in `agent_end` message count and otherwise O(1). Side effects include in-memory state mutation, interval scheduling, process-scoped persistence mutation, and footer-status updates.
|
|
808
|
+
* @details Refreshes stored context usage on every hook, resets or restores persisted elapsed counters during `session_start`, loads the active runtime sound level from persisted config during `session_start`, restores persisted prompt-command metadata when the active session matches a forked execution session, resynchronizes that metadata on later lifecycle hooks so post-switch workflow transitions performed by the initiating command handler become visible to the replacement-session runtime, resets workflow state to `idle` for documented session-start reasons, starts run timing on `agent_start`, promotes pending prompt-request metadata into the active run, captures non-aborted run duration on `agent_end` or `agent_settled`, accumulates successful runtime into `Σ`, preserves in-memory prompt-command state plus process-scoped persistence across switch-triggered `session_shutdown`, tolerates stale post-replacement render contexts, synchronizes the live ticker, and re-renders the status bar with the runtime extension identity prefix when configuration is available. Runtime is O(n) in `agent_end` message count and otherwise O(1). Side effects include in-memory state mutation, interval scheduling, process-scoped persistence mutation, and footer-status updates.
|
|
806
809
|
* @param[in,out] controller {PiUsereqStatusController} Mutable status controller.
|
|
807
810
|
* @param[in] hookName {PiUsereqStatusHookName} Intercepted hook name.
|
|
808
811
|
* @param[in] event {unknown} Hook payload forwarded from the wrapper.
|
|
@@ -851,9 +854,15 @@ export function updateExtensionStatus(
|
|
|
851
854
|
controller.state.pendingPromptRequest = undefined;
|
|
852
855
|
}
|
|
853
856
|
|
|
854
|
-
if (
|
|
857
|
+
if (
|
|
858
|
+
(hookName === "agent_end" || hookName === "agent_settled")
|
|
859
|
+
&& controller.state.runStartTimeMs !== undefined
|
|
860
|
+
) {
|
|
855
861
|
const durationMs = nowMs - controller.state.runStartTimeMs;
|
|
856
|
-
|
|
862
|
+
const agentEndMessages = hookName === "agent_end"
|
|
863
|
+
? (event as AgentEndEvent).messages ?? []
|
|
864
|
+
: [];
|
|
865
|
+
if (!didAgentEndAbort(agentEndMessages)) {
|
|
857
866
|
controller.state.lastRunDurationMs = durationMs;
|
|
858
867
|
controller.state.totalRunDurationMs = controller.state.totalRunDurationMs === undefined
|
|
859
868
|
? durationMs
|
package/src/index.ts
CHANGED
|
@@ -1249,7 +1249,7 @@ function applyConfiguredPiUsereqTools(pi: ExtensionAPI, config: UseReqConfig): v
|
|
|
1249
1249
|
|
|
1250
1250
|
/**
|
|
1251
1251
|
* @brief Handles one intercepted pi lifecycle hook for pi-usereq status updates.
|
|
1252
|
-
* @details Applies session-start-specific resource validation, project-config refresh, startup-tool enablement, and selected debug-tool logging before forwarding the originating hook name and payload into the shared `updateExtensionStatus(...)` pipeline. Before `agent_start`, re-verifies any prepared prompt execution session switch. On `agent_end`, dispatches configured command-notify, sound, and prompt-specific Pushover effects, logs dedicated workflow-closure diagnostics,
|
|
1252
|
+
* @details Applies session-start-specific resource validation, project-config refresh, startup-tool enablement, and selected debug-tool logging before forwarding the originating hook name and payload into the shared `updateExtensionStatus(...)` pipeline. Before `agent_start`, re-verifies any prepared prompt execution session switch. On `agent_end`, dispatches configured command-notify, sound, and prompt-specific Pushover effects, logs dedicated workflow-closure diagnostics, classifies the prompt outcome, and for every matched successful worktree-backed completion defers the restore switch, stash-assisted merge, and worktree deletion to `agent_settled` because the pi 0.67.1+ `switchSession` implementation awaits the active agent run to become idle and would deadlock inside `agent_end`. On `agent_settled`, reuses persisted replacement-session command contexts when event contexts omit `switchSession()`, executes the deferred stash-assisted merge-and-delete finalization path, emits a warning-only notification when restored `base-path` changes are reapplied after merge, tolerates stale replacement-session notification contexts after session replacement, retains the worktree plus notifies closure failure for interrupted or failed outcomes, logs selected prompt workflow transitions, and transitions workflow state through `merging`, `error`, and `idle` as required. On `session_shutdown`, captures pre-update prompt snapshots so workflow-shutdown diagnostics and same-runtime command continuation preserve the active prompt workflow state across switch-triggered rebinding, then disposes the shared controller. Runtime is dominated by configuration loading during `session_start` and git finalization during matched successful `agent_settled` handling; all other hooks are O(1). Side effects include resource checks, active-tool mutation, active-session replacement, status updates, live-ticker disposal on shutdown, optional child-process spawning, outbound HTTPS requests, branch merges, worktree deletion, and optional debug-log writes.
|
|
1253
1253
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
1254
1254
|
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
1255
1255
|
* @param[in] hookName {PiUsereqStatusHookName} Intercepted hook name.
|
|
@@ -1373,6 +1373,13 @@ async function handleExtensionStatusEvent(
|
|
|
1373
1373
|
);
|
|
1374
1374
|
}
|
|
1375
1375
|
if (shouldFinalizeMatchedSuccess) {
|
|
1376
|
+
// Defer the restore switch, merge, and worktree deletion to
|
|
1377
|
+
// `agent_settled`. The pi 0.67.1+ `switchSession` implementation
|
|
1378
|
+
// awaits the active agent run to become idle before replacing the
|
|
1379
|
+
// session, and that idle transition only happens at `agent_settled`.
|
|
1380
|
+
// Calling `switchSession` here would deadlock the `agent_end` handler
|
|
1381
|
+
// and leave the workflow parked in `merging` forever.
|
|
1382
|
+
statusController.state.pendingFinalizationOutcome = outcome;
|
|
1376
1383
|
if (debugConfig) {
|
|
1377
1384
|
transitionPromptWorkflowState(
|
|
1378
1385
|
statusController,
|
|
@@ -1385,75 +1392,6 @@ async function handleExtensionStatusEvent(
|
|
|
1385
1392
|
} else {
|
|
1386
1393
|
setPiUsereqWorkflowState(statusController, "merging", promptContext);
|
|
1387
1394
|
}
|
|
1388
|
-
let finalization:
|
|
1389
|
-
| {
|
|
1390
|
-
mergeAttempted: boolean;
|
|
1391
|
-
mergeSucceeded: boolean;
|
|
1392
|
-
cleanupSucceeded: boolean;
|
|
1393
|
-
errorMessage?: string;
|
|
1394
|
-
warningMessage?: string;
|
|
1395
|
-
activeContext?: unknown;
|
|
1396
|
-
}
|
|
1397
|
-
| undefined;
|
|
1398
|
-
try {
|
|
1399
|
-
finalization = await finalizePromptCommandExecution(
|
|
1400
|
-
activePromptRequest,
|
|
1401
|
-
promptContext,
|
|
1402
|
-
debugConfig
|
|
1403
|
-
? { config: debugConfig, workflowState: statusController.state.workflowState }
|
|
1404
|
-
: undefined,
|
|
1405
|
-
);
|
|
1406
|
-
promptContext = (finalization.activeContext ?? promptContext) as typeof ctx;
|
|
1407
|
-
} catch (error) {
|
|
1408
|
-
promptContext = (getPromptCommandErrorContext(error) ?? promptContext) as typeof ctx;
|
|
1409
|
-
let errorMessage = error instanceof Error ? error.message : String(error);
|
|
1410
|
-
let cleanupSucceeded = false;
|
|
1411
|
-
try {
|
|
1412
|
-
promptContext = (await restorePromptCommandExecution(
|
|
1413
|
-
activePromptRequest,
|
|
1414
|
-
promptContext,
|
|
1415
|
-
debugConfig
|
|
1416
|
-
? { config: debugConfig, workflowState: statusController.state.workflowState }
|
|
1417
|
-
: undefined,
|
|
1418
|
-
) ?? promptContext) as typeof ctx;
|
|
1419
|
-
cleanupSucceeded = true;
|
|
1420
|
-
} catch (restoreError) {
|
|
1421
|
-
promptContext = (getPromptCommandErrorContext(restoreError) ?? promptContext) as typeof ctx;
|
|
1422
|
-
errorMessage = restoreError instanceof Error ? restoreError.message : String(restoreError);
|
|
1423
|
-
}
|
|
1424
|
-
finalization = {
|
|
1425
|
-
mergeAttempted: false,
|
|
1426
|
-
mergeSucceeded: false,
|
|
1427
|
-
cleanupSucceeded,
|
|
1428
|
-
errorMessage,
|
|
1429
|
-
};
|
|
1430
|
-
}
|
|
1431
|
-
if (
|
|
1432
|
-
finalization.errorMessage
|
|
1433
|
-
&& (!finalization.cleanupSucceeded || !finalization.mergeSucceeded)
|
|
1434
|
-
) {
|
|
1435
|
-
if (debugConfig) {
|
|
1436
|
-
transitionPromptWorkflowState(
|
|
1437
|
-
statusController,
|
|
1438
|
-
promptContext,
|
|
1439
|
-
activePromptRequest.basePath,
|
|
1440
|
-
debugConfig,
|
|
1441
|
-
activePromptRequest.promptName,
|
|
1442
|
-
"error",
|
|
1443
|
-
);
|
|
1444
|
-
} else {
|
|
1445
|
-
setPiUsereqWorkflowState(statusController, "error", promptContext);
|
|
1446
|
-
}
|
|
1447
|
-
notifyContextSafely(promptContext, finalization.errorMessage, "error");
|
|
1448
|
-
}
|
|
1449
|
-
if (
|
|
1450
|
-
finalization.warningMessage
|
|
1451
|
-
&& finalization.cleanupSucceeded
|
|
1452
|
-
&& finalization.mergeSucceeded
|
|
1453
|
-
&& !finalization.errorMessage
|
|
1454
|
-
) {
|
|
1455
|
-
notifyContextSafely(promptContext, finalization.warningMessage, "info");
|
|
1456
|
-
}
|
|
1457
1395
|
} else if (closureFailureMessage !== undefined) {
|
|
1458
1396
|
// Worktree-backed run that ended interrupted, failed, aborted, or
|
|
1459
1397
|
// incomplete (REQ-209): keep the worktree execution session visible,
|
|
@@ -1500,7 +1438,7 @@ async function handleExtensionStatusEvent(
|
|
|
1500
1438
|
notifyContextSafely(promptContext, error instanceof Error ? error.message : String(error), "error");
|
|
1501
1439
|
}
|
|
1502
1440
|
}
|
|
1503
|
-
if (closureFailureMessage === undefined) {
|
|
1441
|
+
if (closureFailureMessage === undefined && !shouldFinalizeMatchedSuccess) {
|
|
1504
1442
|
statusController.state.pendingPromptRequest = undefined;
|
|
1505
1443
|
statusController.state.activePromptRequest = undefined;
|
|
1506
1444
|
if (debugConfig) {
|
|
@@ -1518,6 +1456,102 @@ async function handleExtensionStatusEvent(
|
|
|
1518
1456
|
}
|
|
1519
1457
|
}
|
|
1520
1458
|
}
|
|
1459
|
+
if (hookName === "agent_settled") {
|
|
1460
|
+
const settledPromptRequest = statusController.state.activePromptRequest;
|
|
1461
|
+
const pendingOutcome = statusController.state.pendingFinalizationOutcome;
|
|
1462
|
+
if (
|
|
1463
|
+
settledPromptRequest !== undefined
|
|
1464
|
+
&& settledPromptRequest.worktreeDir !== undefined
|
|
1465
|
+
&& pendingOutcome === "completed"
|
|
1466
|
+
) {
|
|
1467
|
+
const debugConfig = statusController.config;
|
|
1468
|
+
let promptContext = ctx;
|
|
1469
|
+
let finalization:
|
|
1470
|
+
| {
|
|
1471
|
+
mergeAttempted: boolean;
|
|
1472
|
+
mergeSucceeded: boolean;
|
|
1473
|
+
cleanupSucceeded: boolean;
|
|
1474
|
+
errorMessage?: string;
|
|
1475
|
+
warningMessage?: string;
|
|
1476
|
+
activeContext?: unknown;
|
|
1477
|
+
}
|
|
1478
|
+
| undefined;
|
|
1479
|
+
try {
|
|
1480
|
+
finalization = await finalizePromptCommandExecution(
|
|
1481
|
+
settledPromptRequest,
|
|
1482
|
+
promptContext,
|
|
1483
|
+
debugConfig
|
|
1484
|
+
? { config: debugConfig, workflowState: statusController.state.workflowState }
|
|
1485
|
+
: undefined,
|
|
1486
|
+
);
|
|
1487
|
+
promptContext = (finalization.activeContext ?? promptContext) as typeof ctx;
|
|
1488
|
+
} catch (error) {
|
|
1489
|
+
promptContext = (getPromptCommandErrorContext(error) ?? promptContext) as typeof ctx;
|
|
1490
|
+
let errorMessage = error instanceof Error ? error.message : String(error);
|
|
1491
|
+
let cleanupSucceeded = false;
|
|
1492
|
+
try {
|
|
1493
|
+
promptContext = (await restorePromptCommandExecution(
|
|
1494
|
+
settledPromptRequest,
|
|
1495
|
+
promptContext,
|
|
1496
|
+
debugConfig
|
|
1497
|
+
? { config: debugConfig, workflowState: statusController.state.workflowState }
|
|
1498
|
+
: undefined,
|
|
1499
|
+
) ?? promptContext) as typeof ctx;
|
|
1500
|
+
cleanupSucceeded = true;
|
|
1501
|
+
} catch (restoreError) {
|
|
1502
|
+
promptContext = (getPromptCommandErrorContext(restoreError) ?? promptContext) as typeof ctx;
|
|
1503
|
+
errorMessage = restoreError instanceof Error ? restoreError.message : String(restoreError);
|
|
1504
|
+
}
|
|
1505
|
+
finalization = {
|
|
1506
|
+
mergeAttempted: false,
|
|
1507
|
+
mergeSucceeded: false,
|
|
1508
|
+
cleanupSucceeded,
|
|
1509
|
+
errorMessage,
|
|
1510
|
+
};
|
|
1511
|
+
}
|
|
1512
|
+
if (
|
|
1513
|
+
finalization.errorMessage
|
|
1514
|
+
&& (!finalization.cleanupSucceeded || !finalization.mergeSucceeded)
|
|
1515
|
+
) {
|
|
1516
|
+
if (debugConfig) {
|
|
1517
|
+
transitionPromptWorkflowState(
|
|
1518
|
+
statusController,
|
|
1519
|
+
promptContext,
|
|
1520
|
+
settledPromptRequest.basePath,
|
|
1521
|
+
debugConfig,
|
|
1522
|
+
settledPromptRequest.promptName,
|
|
1523
|
+
"error",
|
|
1524
|
+
);
|
|
1525
|
+
} else {
|
|
1526
|
+
setPiUsereqWorkflowState(statusController, "error", promptContext);
|
|
1527
|
+
}
|
|
1528
|
+
notifyContextSafely(promptContext, finalization.errorMessage, "error");
|
|
1529
|
+
}
|
|
1530
|
+
if (
|
|
1531
|
+
finalization.warningMessage
|
|
1532
|
+
&& finalization.cleanupSucceeded
|
|
1533
|
+
&& finalization.mergeSucceeded
|
|
1534
|
+
&& !finalization.errorMessage
|
|
1535
|
+
) {
|
|
1536
|
+
notifyContextSafely(promptContext, finalization.warningMessage, "info");
|
|
1537
|
+
}
|
|
1538
|
+
statusController.state.pendingFinalizationOutcome = undefined;
|
|
1539
|
+
statusController.state.pendingPromptRequest = undefined;
|
|
1540
|
+
statusController.state.activePromptRequest = undefined;
|
|
1541
|
+
if (debugConfig) {
|
|
1542
|
+
transitionPromptWorkflowState(
|
|
1543
|
+
statusController,
|
|
1544
|
+
promptContext,
|
|
1545
|
+
settledPromptRequest.basePath,
|
|
1546
|
+
debugConfig,
|
|
1547
|
+
settledPromptRequest.promptName,
|
|
1548
|
+
"idle",
|
|
1549
|
+
);
|
|
1550
|
+
} else {
|
|
1551
|
+
setPiUsereqWorkflowState(statusController, "idle", promptContext);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1521
1555
|
if (hookName === "session_shutdown") {
|
|
1522
1556
|
if (shutdownPromptRequest !== undefined && statusController.config) {
|
|
1523
1557
|
logPromptWorkflowEvent(
|