@monotykamary/pi-ledger 0.2.0 → 0.3.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
CHANGED
|
@@ -191,7 +191,11 @@ Idle with no output is wasted time.
|
|
|
191
191
|
falls back to a `select` dialog; with **auto-extend** on, it skips the prompt
|
|
192
192
|
and provisions a block silently. `agent_settled` fires once the run is fully settled (no
|
|
193
193
|
auto-retry, compaction, or queued follow-up left), so the wizard never pops
|
|
194
|
-
mid-retry or mid-continuation.
|
|
194
|
+
mid-retry or mid-continuation. When [@monotykamary/pi-retry](https://github.com/monotykamary/pi-retry) is installed, its
|
|
195
|
+
backoff sleep can make `agent_settled` fire mid-retry; pi-ledger captures
|
|
196
|
+
pi-retry's `started`/`completed`/`cancelled` events and defers the prompt
|
|
197
|
+
until the retry genuinely settles — it pops on `completed`, never on
|
|
198
|
+
`cancelled`, never while one is in flight. With credit, it stays silent and arms to fire
|
|
195
199
|
when the engaged window's credit is exhausted; the exhaustion pop offers the
|
|
196
200
|
next extension (the `extend + extend + extend` chain).
|
|
197
201
|
- `/ledger-extend [m]` opens the wizard manually — with or without an open
|
|
@@ -396,7 +400,7 @@ current moment, including the in-progress open human window, from the sidecar
|
|
|
396
400
|
|
|
397
401
|
```bash
|
|
398
402
|
pnpm install
|
|
399
|
-
pnpm test # vitest run (
|
|
403
|
+
pnpm test # vitest run (140 tests)
|
|
400
404
|
pnpm run typecheck # tsc --noEmit
|
|
401
405
|
pnpm run lint:dead # knip
|
|
402
406
|
```
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
fmtMoney,
|
|
24
24
|
rehydrateFromSidecar,
|
|
25
25
|
resolveExtensionBudget,
|
|
26
|
+
retryEventId,
|
|
26
27
|
sidecarPathFor,
|
|
27
28
|
type LedgerSettings,
|
|
28
29
|
type ReceiptData,
|
|
@@ -2526,3 +2527,127 @@ describe('rehydrateFromSidecar — skip-billing guard', () => {
|
|
|
2526
2527
|
expect(rehydrateFromSidecar([]).billingPaused).toBe(false);
|
|
2527
2528
|
});
|
|
2528
2529
|
});
|
|
2530
|
+
|
|
2531
|
+
// ─── pi-retry capture (wizard gating) ────────────────────────────────────────
|
|
2532
|
+
// The engagement prompt (no rolling credit) lives at agent_settled. When
|
|
2533
|
+
// @monotykamary/pi-retry is installed, agent_settled can fire during a retry's
|
|
2534
|
+
// backoff sleep — before pi-retry has decided whether to re-prompt. pi-ledger
|
|
2535
|
+
// captures pi-retry's started/completed/cancelled events and DEFERS the prompt
|
|
2536
|
+
// until the retry settles, so the backoff is never billed as human time.
|
|
2537
|
+
// Mirrors localterm's agent-notify contract.
|
|
2538
|
+
|
|
2539
|
+
describe('pi-retry capture (wizard gating)', () => {
|
|
2540
|
+
let fixture: TestFixture;
|
|
2541
|
+
let cacheDir: string;
|
|
2542
|
+
|
|
2543
|
+
beforeEach(async () => {
|
|
2544
|
+
vi.useFakeTimers();
|
|
2545
|
+
cacheDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pi-ledger-test-'));
|
|
2546
|
+
process.env.XDG_CACHE_HOME = cacheDir;
|
|
2547
|
+
fixture = createTestFixture();
|
|
2548
|
+
await activateExtension(fixture);
|
|
2549
|
+
});
|
|
2550
|
+
|
|
2551
|
+
afterEach(() => {
|
|
2552
|
+
vi.useRealTimers();
|
|
2553
|
+
delete process.env.XDG_CACHE_HOME;
|
|
2554
|
+
fs.rmSync(cacheDir, { recursive: true, force: true });
|
|
2555
|
+
});
|
|
2556
|
+
|
|
2557
|
+
const STARTED = 'pi-retry:started';
|
|
2558
|
+
const COMPLETED = 'pi-retry:completed';
|
|
2559
|
+
const CANCELLED = 'pi-retry:cancelled';
|
|
2560
|
+
|
|
2561
|
+
it('pops the wizard at agent_settled when no pi-retry is active', () => {
|
|
2562
|
+
fixture.run('session_start', { type: 'session_start', reason: 'startup' });
|
|
2563
|
+
fixture.run('agent_end', { type: 'agent_end', messages: [] });
|
|
2564
|
+
expect(fixture.customSpy).not.toHaveBeenCalled();
|
|
2565
|
+
fixture.run('agent_settled', { type: 'agent_settled' });
|
|
2566
|
+
expect(fixture.customSpy).toHaveBeenCalledTimes(1); // no credit → pop
|
|
2567
|
+
});
|
|
2568
|
+
|
|
2569
|
+
it('defers the wizard while a pi-retry is in flight; pops on completed', () => {
|
|
2570
|
+
fixture.run('session_start', { type: 'session_start', reason: 'startup' });
|
|
2571
|
+
fixture.run('agent_start', { type: 'agent_start' });
|
|
2572
|
+
fixture.run('agent_end', { type: 'agent_end', messages: [] });
|
|
2573
|
+
fixture.emitEvent(STARTED, { retryId: 1 });
|
|
2574
|
+
fixture.run('agent_settled', { type: 'agent_settled' }); // retry in flight → defer
|
|
2575
|
+
expect(fixture.customSpy).not.toHaveBeenCalled();
|
|
2576
|
+
fixture.emitEvent(COMPLETED, { retryId: 1 }); // retry settled → pop the deferred prompt
|
|
2577
|
+
expect(fixture.customSpy).toHaveBeenCalledTimes(1);
|
|
2578
|
+
expect(fixture.lastSidecarEvent('human-open')).toBeUndefined(); // still engagement-gated
|
|
2579
|
+
});
|
|
2580
|
+
|
|
2581
|
+
it('waits for the final agent_settled when retry completion arrives first', () => {
|
|
2582
|
+
fixture.run('session_start', { type: 'session_start', reason: 'startup' });
|
|
2583
|
+
fixture.run('agent_start', { type: 'agent_start' });
|
|
2584
|
+
fixture.run('agent_end', { type: 'agent_end', messages: [] });
|
|
2585
|
+
fixture.emitEvent(STARTED, { retryId: 2 });
|
|
2586
|
+
fixture.run('agent_settled', { type: 'agent_settled' }); // retry in flight → defer
|
|
2587
|
+
// the retry turn fires (a new run supersedes the stale deferred prompt)
|
|
2588
|
+
fixture.run('agent_start', { type: 'agent_start' });
|
|
2589
|
+
fixture.run('agent_end', { type: 'agent_end', messages: [] });
|
|
2590
|
+
// retry completes BEFORE the run settles → pending was cleared by agent_start → no pop
|
|
2591
|
+
fixture.emitEvent(COMPLETED, { retryId: 2 });
|
|
2592
|
+
expect(fixture.customSpy).not.toHaveBeenCalled();
|
|
2593
|
+
fixture.run('agent_settled', { type: 'agent_settled' }); // now settled, no retry active → pop
|
|
2594
|
+
expect(fixture.customSpy).toHaveBeenCalledTimes(1);
|
|
2595
|
+
});
|
|
2596
|
+
|
|
2597
|
+
it('never pops when a pi-retry is cancelled', () => {
|
|
2598
|
+
fixture.run('session_start', { type: 'session_start', reason: 'startup' });
|
|
2599
|
+
fixture.run('agent_start', { type: 'agent_start' });
|
|
2600
|
+
fixture.run('agent_end', { type: 'agent_end', messages: [] });
|
|
2601
|
+
fixture.emitEvent(STARTED, { retryId: 3 });
|
|
2602
|
+
fixture.run('agent_settled', { type: 'agent_settled' }); // retry in flight → defer
|
|
2603
|
+
expect(fixture.customSpy).not.toHaveBeenCalled();
|
|
2604
|
+
fixture.emitEvent(CANCELLED, { retryId: 3 }); // abort/session-change → drop the prompt
|
|
2605
|
+
expect(fixture.customSpy).not.toHaveBeenCalled();
|
|
2606
|
+
});
|
|
2607
|
+
|
|
2608
|
+
it('ignores a completed event that does not match the active retry', () => {
|
|
2609
|
+
fixture.run('session_start', { type: 'session_start', reason: 'startup' });
|
|
2610
|
+
fixture.run('agent_start', { type: 'agent_start' });
|
|
2611
|
+
fixture.run('agent_end', { type: 'agent_end', messages: [] });
|
|
2612
|
+
fixture.emitEvent(STARTED, { retryId: 4 });
|
|
2613
|
+
fixture.run('agent_settled', { type: 'agent_settled' }); // defer (active id 4)
|
|
2614
|
+
fixture.emitEvent(COMPLETED, { retryId: 999 }); // stray → ignored
|
|
2615
|
+
expect(fixture.customSpy).not.toHaveBeenCalled();
|
|
2616
|
+
fixture.emitEvent(COMPLETED, { retryId: 4 }); // matching → pop
|
|
2617
|
+
expect(fixture.customSpy).toHaveBeenCalledTimes(1);
|
|
2618
|
+
});
|
|
2619
|
+
|
|
2620
|
+
it('treats a completed with no prior started as a no-op', () => {
|
|
2621
|
+
fixture.run('session_start', { type: 'session_start', reason: 'startup' });
|
|
2622
|
+
fixture.emitEvent(COMPLETED, { retryId: 7 }); // no retry active → stray event
|
|
2623
|
+
expect(fixture.customSpy).not.toHaveBeenCalled();
|
|
2624
|
+
});
|
|
2625
|
+
|
|
2626
|
+
it('defers auto-extend too while a pi-retry is in flight; extends on completed', () => {
|
|
2627
|
+
fixture.seedSidecar([
|
|
2628
|
+
{ kind: 'settings', settings: { ...DEFAULTS, autoExtend: true }, timestamp: 0 },
|
|
2629
|
+
]);
|
|
2630
|
+
fixture.run('session_start', { type: 'session_start', reason: 'startup' }); // rehydrate → autoExtend on
|
|
2631
|
+
fixture.run('agent_start', { type: 'agent_start' });
|
|
2632
|
+
fixture.run('agent_end', { type: 'agent_end', messages: [] });
|
|
2633
|
+
fixture.emitEvent(STARTED, { retryId: 5 });
|
|
2634
|
+
fixture.run('agent_settled', { type: 'agent_settled' }); // retry in flight → defer auto-extend
|
|
2635
|
+
expect(fixture.readSidecarEvents().filter((e) => e.kind === 'human-open')).toHaveLength(0);
|
|
2636
|
+
fixture.emitEvent(COMPLETED, { retryId: 5 }); // settled → auto-extend provisions a block + engages
|
|
2637
|
+
const open = fixture.lastSidecarEvent('human-open');
|
|
2638
|
+
expect(open).toBeDefined();
|
|
2639
|
+
expect(open!.engagedVia).toBe('extension'); // auto-extend engages via extension
|
|
2640
|
+
});
|
|
2641
|
+
});
|
|
2642
|
+
|
|
2643
|
+
describe('retryEventId', () => {
|
|
2644
|
+
it('returns the retryId of a pi-retry event', () => {
|
|
2645
|
+
expect(retryEventId({ retryId: 42 })).toBe(42);
|
|
2646
|
+
});
|
|
2647
|
+
it('returns undefined when retryId is absent or malformed', () => {
|
|
2648
|
+
expect(retryEventId(undefined)).toBeUndefined();
|
|
2649
|
+
expect(retryEventId(null)).toBeUndefined();
|
|
2650
|
+
expect(retryEventId({})).toBeUndefined();
|
|
2651
|
+
expect(retryEventId({ retryId: 'oops' })).toBeUndefined();
|
|
2652
|
+
});
|
|
2653
|
+
});
|
|
@@ -72,6 +72,19 @@ const TPS_TELEMETRY_EVENT = 'tps:telemetry';
|
|
|
72
72
|
/** Custom entry type written by @monotykamary/pi-tps into the session JSONL. */
|
|
73
73
|
const TPS_CUSTOM_TYPE = 'tps';
|
|
74
74
|
|
|
75
|
+
/** Custom events emitted by @monotykamary/pi-retry around its retry loop:
|
|
76
|
+
* 'started' (with a retryId) when the loop spins up, 'completed' when it
|
|
77
|
+
* exits on success, 'cancelled' when it exits on abort/session-change.
|
|
78
|
+
* pi-ledger subscribes so the engagement wizard does not pop mid-retry —
|
|
79
|
+
* agent_settled can fire during a pi-retry backoff sleep (before pi-retry has
|
|
80
|
+
* re-prompted), and popping then would bill the backoff as human time
|
|
81
|
+
* (violating scale-to-zero: a slow/queued provider is a retry, not billable).
|
|
82
|
+
* The prompt is deferred until the retry settles. Mirrors localterm's
|
|
83
|
+
* agent-notify handshake. */
|
|
84
|
+
const PI_RETRY_STARTED_EVENT = 'pi-retry:started';
|
|
85
|
+
const PI_RETRY_COMPLETED_EVENT = 'pi-retry:completed';
|
|
86
|
+
const PI_RETRY_CANCELLED_EVENT = 'pi-retry:cancelled';
|
|
87
|
+
|
|
75
88
|
/** Minimum gap between streaming updates to count as an inference stall (ms). */
|
|
76
89
|
const STALL_THRESHOLD_MS = 500;
|
|
77
90
|
|
|
@@ -847,6 +860,17 @@ function reveal(text: string): string {
|
|
|
847
860
|
return ` data-reveal="${esc(text)}">`;
|
|
848
861
|
}
|
|
849
862
|
|
|
863
|
+
/** Pull the 'retryId' out of a @monotykamary/pi-retry event payload, or
|
|
864
|
+
* 'undefined' if it is absent/malformed. Used to match a 'completed'/
|
|
865
|
+
* 'cancelled' event back to its 'started' so pi-ledger only acts on its own
|
|
866
|
+
* retry. Mirrors localterm's retry-event-id util. Pure.
|
|
867
|
+
* @internal Exported for testing only. */
|
|
868
|
+
export function retryEventId(event: unknown): number | undefined {
|
|
869
|
+
if (typeof event !== 'object' || event === null || !('retryId' in event)) return undefined;
|
|
870
|
+
const id = (event as { retryId?: unknown }).retryId;
|
|
871
|
+
return typeof id === 'number' ? id : undefined;
|
|
872
|
+
}
|
|
873
|
+
|
|
850
874
|
/**
|
|
851
875
|
* Build a self-contained HTML receipt. White-on-white, Geist Mono, with
|
|
852
876
|
* values that stream in autoregressively (char-by-char) on load.
|
|
@@ -1204,6 +1228,25 @@ export default function ledgerExtension(pi: ExtensionAPI) {
|
|
|
1204
1228
|
|
|
1205
1229
|
let wizardTimer: ReturnType<typeof setTimeout> | null = null;
|
|
1206
1230
|
|
|
1231
|
+
// pi-retry awareness: when @monotykamary/pi-retry is installed it emits
|
|
1232
|
+
// pi-retry:started/completed/cancelled around its (possibly multi-attempt)
|
|
1233
|
+
// retry loop. agent_settled can fire during a retry's backoff sleep —
|
|
1234
|
+
// before pi-retry has decided whether to re-prompt — so without gating the
|
|
1235
|
+
// wizard would pop mid-retry (billing the backoff as human time, violating
|
|
1236
|
+
// scale-to-zero). We capture the active retryId and DEFER the settled
|
|
1237
|
+
// prompt until the retry settles: pop only on 'completed', never on
|
|
1238
|
+
// 'cancelled', and never while a retry is in flight. Mirrors localterm's
|
|
1239
|
+
// agent-notify handshake.
|
|
1240
|
+
let retryActiveId: number | undefined;
|
|
1241
|
+
// The run settled (agent_settled fired, no rolling credit) but a retry was
|
|
1242
|
+
// in flight, so the engagement prompt is pending until the retry completes.
|
|
1243
|
+
// Cleared on pop, on agent_start (a new run supersedes the stale settled
|
|
1244
|
+
// state), on retry cancelled, and at session boundaries.
|
|
1245
|
+
let pendingSettledWizard: { ctx: ExtensionContext } | null = null;
|
|
1246
|
+
// Unsubscribers for the pi-retry event capture (a session-scoped resource);
|
|
1247
|
+
// torn down at session_shutdown. The factory re-binds on the next session.
|
|
1248
|
+
let retryUnsubs: Array<() => void> = [];
|
|
1249
|
+
|
|
1207
1250
|
// Latest ctx (event-bus listeners for tps:telemetry don't receive one).
|
|
1208
1251
|
let lastCtx: ExtensionContext | null = null;
|
|
1209
1252
|
|
|
@@ -1903,6 +1946,11 @@ export default function ledgerExtension(pi: ExtensionAPI) {
|
|
|
1903
1946
|
// rule as an unclosed idle window: uncommitted, so not restored.
|
|
1904
1947
|
pendingSteers = [];
|
|
1905
1948
|
dequeuedBuffer = null;
|
|
1949
|
+
// Reset pi-retry capture state — a fresh session has no in-flight retry and
|
|
1950
|
+
// no deferred prompt (an unclosed prior session's retry could otherwise
|
|
1951
|
+
// leak across the session boundary on a per-process extension instance).
|
|
1952
|
+
retryActiveId = undefined;
|
|
1953
|
+
pendingSettledWizard = null;
|
|
1906
1954
|
// Wrap the input editor so keystrokes stage for billing: during a run they
|
|
1907
1955
|
// feed a steer/followUp burst (committed on submit via `input`); between
|
|
1908
1956
|
// turns the FIRST keystroke engages an idle window at its onset. Extends
|
|
@@ -1944,6 +1992,20 @@ export default function ledgerExtension(pi: ExtensionAPI) {
|
|
|
1944
1992
|
// at shutdown was never committed, so it's abandoned and bills 0 (idle
|
|
1945
1993
|
// with no output is wasted). Persisted as a close for replay cleanliness.
|
|
1946
1994
|
closeHumanWindow(lastCtx, false);
|
|
1995
|
+
// Tear down the pi-retry event capture (a session-scoped resource): the
|
|
1996
|
+
// factory re-binds subscriptions on the next session, so unbind here to
|
|
1997
|
+
// avoid leaking listeners across the session boundary. Reset the capture
|
|
1998
|
+
// state too — no in-flight retry survives a session teardown.
|
|
1999
|
+
for (const unsub of retryUnsubs) {
|
|
2000
|
+
try {
|
|
2001
|
+
unsub();
|
|
2002
|
+
} catch {
|
|
2003
|
+
// best-effort — ignore a stale bus
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
retryUnsubs = [];
|
|
2007
|
+
retryActiveId = undefined;
|
|
2008
|
+
pendingSettledWizard = null;
|
|
1947
2009
|
});
|
|
1948
2010
|
|
|
1949
2011
|
// ── Agent timing (tool execution) ─────────────────────────────────────
|
|
@@ -2044,6 +2106,41 @@ export default function ledgerExtension(pi: ExtensionAPI) {
|
|
|
2044
2106
|
// extension load order — a 'fallback' may be corrected by a later 'tps'
|
|
2045
2107
|
// entry for the same turnIndex (rehydrate keeps the last per turnIndex).
|
|
2046
2108
|
|
|
2109
|
+
// ── pi-retry capture ─────────────────────────────────────────────────
|
|
2110
|
+
// @monotykamary/pi-retry emits started/completed/cancelled around its
|
|
2111
|
+
// (possibly multi-attempt) retry loop. agent_settled can land during a
|
|
2112
|
+
// retry's backoff sleep — before pi-retry has re-prompted — so without this
|
|
2113
|
+
// capture the engagement wizard would pop mid-retry, billing the backoff as
|
|
2114
|
+
// human time (violating scale-to-zero: a slow/queued provider is a retry,
|
|
2115
|
+
// not billable). Defer the settled prompt until the retry settles: pop on
|
|
2116
|
+
// 'completed' (if still no credit), never on 'cancelled', never while one is
|
|
2117
|
+
// in flight. Unsubscribed at session_shutdown (a session-scoped resource);
|
|
2118
|
+
// the factory re-binds on the next session. Mirrors localterm's agent-notify.
|
|
2119
|
+
retryUnsubs.push(
|
|
2120
|
+
pi.events.on(PI_RETRY_STARTED_EVENT, (event: unknown) => {
|
|
2121
|
+
retryActiveId = retryEventId(event);
|
|
2122
|
+
})
|
|
2123
|
+
);
|
|
2124
|
+
retryUnsubs.push(
|
|
2125
|
+
pi.events.on(PI_RETRY_COMPLETED_EVENT, (event: unknown) => {
|
|
2126
|
+
if (retryEventId(event) !== retryActiveId) return;
|
|
2127
|
+
retryActiveId = undefined;
|
|
2128
|
+
const pending = pendingSettledWizard;
|
|
2129
|
+
if (!pending) return;
|
|
2130
|
+
pendingSettledWizard = null;
|
|
2131
|
+
// Credit may have changed since settle (e.g. a mid-backoff /ledger-extend);
|
|
2132
|
+
// with none, pop now that the retry has settled. With credit, stay quiet.
|
|
2133
|
+
if (extensionBudgetMs <= 0) armWizardNow(pending.ctx);
|
|
2134
|
+
})
|
|
2135
|
+
);
|
|
2136
|
+
retryUnsubs.push(
|
|
2137
|
+
pi.events.on(PI_RETRY_CANCELLED_EVENT, (event: unknown) => {
|
|
2138
|
+
if (retryEventId(event) !== retryActiveId) return;
|
|
2139
|
+
retryActiveId = undefined;
|
|
2140
|
+
pendingSettledWizard = null; // abort/session-change → no prompt
|
|
2141
|
+
})
|
|
2142
|
+
);
|
|
2143
|
+
|
|
2047
2144
|
pi.events.on(TPS_TELEMETRY_EVENT, (payload: unknown) => {
|
|
2048
2145
|
const t = payload as TpsTelemetry | null;
|
|
2049
2146
|
if (!t || !t.timing || !t.tokens || !t.model) return;
|
|
@@ -2159,6 +2256,11 @@ export default function ledgerExtension(pi: ExtensionAPI) {
|
|
|
2159
2256
|
// If the human never engaged (no keystroke, no extension), there's no
|
|
2160
2257
|
// window to close and the turn handoff bills nothing.
|
|
2161
2258
|
closeHumanWindow(ctx, true);
|
|
2259
|
+
// A new run supersedes any deferred settled prompt (a retry turn, a queued
|
|
2260
|
+
// follow-up, or a fresh prompt all re-settle later — agent_settled
|
|
2261
|
+
// re-evaluates then). Keeps a pi-retry that completes after a retry
|
|
2262
|
+
// turn's agent_end from popping on a stale 'settled' until that run settles.
|
|
2263
|
+
pendingSettledWizard = null;
|
|
2162
2264
|
});
|
|
2163
2265
|
|
|
2164
2266
|
pi.on('agent_end', (_event, ctx) => {
|
|
@@ -2208,7 +2310,22 @@ export default function ledgerExtension(pi: ExtensionAPI) {
|
|
|
2208
2310
|
// after a retry storm exhausts — the last errored agent_end no longer
|
|
2209
2311
|
// suppresses it, since the run has now settled and the human must take
|
|
2210
2312
|
// over.
|
|
2211
|
-
if (extensionBudgetMs <= 0)
|
|
2313
|
+
if (extensionBudgetMs <= 0) {
|
|
2314
|
+
// @monotykamary/pi-retry (if installed) drives its own retry loop with
|
|
2315
|
+
// backoff sleeps outside processEvents; pi-core's settlement detection
|
|
2316
|
+
// can fire this agent_settled during that backoff — before pi-retry has
|
|
2317
|
+
// decided whether to re-prompt. Popping then would bill the backoff as
|
|
2318
|
+
// human time (violating scale-to-zero: a slow/queued provider is a
|
|
2319
|
+
// retry, not billable). Defer: if a pi-retry is in flight, stage the
|
|
2320
|
+
// prompt and pop when the retry settles (on pi-retry:completed); a
|
|
2321
|
+
// cancelled retry never pops. With no pi-retry (or none active), pop now
|
|
2322
|
+
// as before.
|
|
2323
|
+
if (retryActiveId !== undefined) {
|
|
2324
|
+
pendingSettledWizard = { ctx };
|
|
2325
|
+
} else {
|
|
2326
|
+
armWizardNow(ctx);
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2212
2329
|
});
|
|
2213
2330
|
|
|
2214
2331
|
// The `input` event fires for every interactive submit that isn't a slash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monotykamary/pi-ledger",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Billing engine for the serverless agency — a pi extension that meters agentic dev work like cloud compute and invoices it like a timesheet.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|