pi-simple-background-tasks 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/FORK-CHANGES.md CHANGED
@@ -12,11 +12,12 @@ This package is a **KISS fork** of
12
12
 
13
13
  ## Divergence summary
14
14
 
15
- Relative to the 2.6.9 source, the fork changes **63 files (31,260 lines removed, 3,704 added)**, ending with **13 source files**:
15
+ Relative to the 2.6.9 source, the fork changes **69 files (34,235 lines removed, 5,275 added)**, ending with **14 source files** plus a 3-file `node:test` platform suite:
16
16
 
17
17
  | Subsystem (as in 2.6.9) | Fork |
18
18
  | --- | --- |
19
- | Background-task core: `registry`, `common`, `extension-api`, `durable-fs`, `task-durable`, `shell-policy`, `windows-taskkill`, `reload-shell-owner`, `ui/background-tasks-manager` | **Kept, simplified** |
19
+ | Background-task core: `registry`, `common`, `extension-api`, `durable-fs`, `task-durable`, `shell-policy`, `windows-taskkill`, `ui/background-tasks-manager` | **Kept, simplified** |
20
+ | `reload-shell-owner` (lease/nonce handoff protocol for reload survival) | **Removed** — replaced by the detached-spawn design below |
20
21
  | Fusion workflows (`src/core/fusion/*` + `fusion-extension.ts` + `fusion-child-extension.ts` + `ui/fusion-model-selector.ts`) | **Removed** (~15k lines) |
21
22
  | Delegate (`src/core/delegate/*` + `delegate-extension.ts` + `delegate-child-extension.ts`) | **Removed** (~5k lines) |
22
23
  | Claude/Anthropic impersonation (`anthropic-attribution*.ts`) | **Removed** (~3.7k lines) |
@@ -31,9 +32,15 @@ Relative to the 2.6.9 source, the fork changes **63 files (31,260 lines removed,
31
32
 
32
33
  ## What the fork adds / changes
33
34
 
35
+ - **Single detached task path, survival without flags** — every task spawns fully detached: on POSIX in its own process group (no pipes, no in-process ownership); on Windows as an outer process created **by the WMI service** (`Win32_Process.Create`), because direct children of the Pi process — no matter how detached — die together with it through job/console-session teardown (reproduced on a real Windows 11 VM with `taskkill /F` on the Pi process; WMI-created processes survive it). Ownership facts (pid, pgid, wrapper-written `<id>.exit` marker) live in the on-disk metadata and the extension holds no output file descriptors: the spawned command itself redirects output and the wrapper records the exit code. Each activation adopts still-running records from disk; a same-process `/reload` or a full Pi restart never kills a running task, and a Pi session shutdown kills it deliberately. Upstream's `reload-shell-owner` lease/handoff protocol (`--survive-reload`) was removed along with its ~1.5k lines: there is exactly one launch path and nothing to negotiate per task.
36
+
37
+ - **One Windows kill: `taskkill /T /F`** — the old two-phase ladder (graceful `taskkill /T` window-close termination, then `/F` escalation after a grace timer) is gone: console applications do not honor window-close termination, so the graceful phase could neither stop anything on Windows nor leave time to escalate during a session shutdown (the Pi process would exit first and the un-ref'd escalation timer would die with it). One forced tree kill, outcome checked once. Cap/timeout notices are appended after the tree is terminated because the child's own `1>` redirection holds the output file locked until then.
38
+
39
+ - **Platform test suite** (`npm test`) — `tests/registry.test.ts` runs the registry against real processes on the platform it is executed on, with the node:test runner through tsx (no new dependencies): completion/exit markers, quoting, timeout and cap kills, detachment across a genuine parent exit, adoption of a dead activation's records, kill of adopted tasks with notification, and no-adoption of terminal records. The two launch races it found — reaping a task while its WMI spawn is still in flight, and flood bytes overwriting cap/timeout notices — had product fixes (see the git history). All tests pass on Linux (8 run, 1 windows-only skip) and on a real Windows 11 VM (8 run, 1 posix-only skip).
40
+
34
41
  - **`/clean` command + `bg_clean` tool** — delete finished tasks (`session` / `all` / `<id>`). New file `src/core/task-cleanup.ts`; empty run directories are removed after cleanup (tasks live under `~/.pi/agent/tasks`).
35
42
  - **Task runtime moved to `~/.pi/agent/tasks`** with the PI extension-config pattern (`pi-background-tasks.json`, `tasksDir`).
36
- - **KISS packaging** — single entry point `src/extension.ts`, no `dist/`, no tests, no docs tree; fork identity in `package.json`.
43
+ - **KISS packaging** — single entry point `src/extension.ts`, no `dist/`, no docs tree; one `node:test` platform suite under `tests/` (`npm test`, not shipped to npm); fork identity in `package.json`.
37
44
  - **Task manager UI** — opens top-right as a fixed 96-column modal (96 % width left the box stuck left on wide terminals); `d` deletes the selected task; both shortcuts registered (Shift↓ / Ctrl+Alt+B); footer options contextual to the selected task; empty-state footer shows only sensible actions; task detail adapts to terminal height (modal title dropped); **f** toggles fullscreen (gated on terminal size), **w** toggles output wrap.
38
45
  - **`--agent` flag** removed from `/bg` (stale).
39
46
  - **Lazy module loader simplified.**
package/README.md CHANGED
@@ -7,19 +7,22 @@ A KISS fork of [pi-background-tasks](https://github.com/ismailsaleekh/pi-backgro
7
7
  ## Features
8
8
 
9
9
  - **Run long jobs without blocking**: start a named shell command in the background, keep talking to Pi, and get a completion notification in chat when it finishes.
10
- - **Durable tasks**: task state is persisted under `~/.pi/agent/tasks`, so it survives session restarts.
10
+ - **Durable tasks**: task state is persisted under `~/.pi/agent/tasks`, and every task spawns detached, so a same-process `/reload` or a full Pi restart never kills a running task — the next activation adopts it from disk.
11
+ - **Deliberate lifecycle**: a Pi session shutdown kills running tasks by design; `/reload`, crashes and restarts never do.
11
12
  - **Task manager overlay**: a TUI panel lists running and recent tasks with status and bounded output (default **Shift↓**); **f** toggles fullscreen, **w** wraps long output lines.
12
- - **Survive Pi reloads**: opt in to keeping a shell task alive across a same-process `/reload`.
13
- - **Cross-platform**: POSIX shell and Windows (process-group kill).
13
+ - **Cross-platform**: POSIX (own process group, `-pgid` kill authority) and Windows (detached via a WMI-created outer process, `taskkill /T /F` tree ownership, `cmd`-runner exit markers).
14
14
 
15
15
  ## Install
16
16
 
17
17
  ```bash
18
- # Global install from the git main branch
19
- pi install git:github.com/jlsalvador/pi-simple-background-tasks@main
18
+ # Default install (npm)
19
+ pi install npm:pi-simple-background-tasks
20
+
21
+ # Project-local install (npm)
22
+ pi install -l npm:pi-simple-background-tasks
20
23
 
21
- # Project-local install from git
22
- pi install git:github.com/jlsalvador/pi-simple-background-tasks@main -l
24
+ # Or from the git main branch
25
+ pi install git:github.com/jlsalvador/pi-simple-background-tasks@main
23
26
  ```
24
27
 
25
28
  ## Quick start
@@ -30,7 +33,7 @@ pi install git:github.com/jlsalvador/pi-simple-background-tasks@main -l
30
33
  /bg --name "Typecheck watch" npm run typecheck -- --watch
31
34
  ```
32
35
 
33
- `/bg` starts a tracked shell task and returns the task id plus output path. User-launched `/bg` tasks notify in the UI but do not automatically wake a follow-up model turn. Add one leading `--survive-reload` only when the shell process must keep running across a same-process `/reload`.
36
+ `/bg` starts a tracked shell command and returns the task id plus output path. User-launched `/bg` tasks notify in the UI but do not automatically wake a follow-up model turn. Tasks survive `/reload` and Pi restarts unconditionally; quitting the agent kills them.
34
37
 
35
38
  2. List tasks or open the task manager:
36
39
 
@@ -58,7 +61,7 @@ pi install git:github.com/jlsalvador/pi-simple-background-tasks@main -l
58
61
 
59
62
  | Command | Purpose |
60
63
  | --- | --- |
61
- | `/bg [--survive-reload] [--name "name"] <command>` | Start a tracked shell command. |
64
+ | `/bg [--name "name"] <command>` | Start a tracked shell command. |
62
65
  | `/jobs` | List running and recent tasks. |
63
66
  | `/tasks [id]` | Open the task manager overlay. |
64
67
  | `/logs <id> [maxBytes]` | Show bounded output from a task. |
@@ -94,8 +97,15 @@ Example:
94
97
  npm install
95
98
  npm run check # tsc --noEmit && biome check .
96
99
  npm run fix # biome check --write .
100
+ npm test # registry platform suite (tests/), run per-platform
97
101
  ```
98
102
 
103
+ `npm test` verifies the detached-task promises (survival across a real parent
104
+ exit, adoption, kills) with `node:test` against processes it genuinely spawns.
105
+ Run it on the platform whose behavior you want to verify: on POSIX it exercises
106
+ process groups and the group kill; on Windows it exercises WMI-created tasks,
107
+ the cmd runner and `taskkill /T /F`. Only the platform-specific pieces skip.
108
+
99
109
  ## License
100
110
 
101
111
  ISC — see [LICENSE](LICENSE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-simple-background-tasks",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Durable background shell tasks for Pi. KISS fork of pi-background-tasks.",
5
5
  "type": "module",
6
6
  "license": "ISC",
@@ -32,7 +32,8 @@
32
32
  ],
33
33
  "scripts": {
34
34
  "check": "tsc --noEmit && biome check .",
35
- "fix": "biome check --write ."
35
+ "fix": "biome check --write .",
36
+ "test": "tsx --test \"tests/*.test.ts\""
36
37
  },
37
38
  "pi": {
38
39
  "extensions": [
@@ -1,8 +1,7 @@
1
- import { accessSync, constants, statSync, type WriteStream } from 'node:fs';
1
+ import { accessSync, constants, statSync } from 'node:fs';
2
2
  import { open } from 'node:fs/promises';
3
3
  import { basename, delimiter, extname, isAbsolute, join, resolve, win32 } from 'node:path';
4
4
  import { DEFAULT_MAX_BYTES } from '@earendil-works/pi-coding-agent';
5
- import type { BackgroundTaskChildProcess } from './registry.js';
6
5
 
7
6
  export const TASK_STATUS_VALUES = ['running', 'completed', 'failed', 'killed'] as const;
8
7
  export const TERMINAL_TASK_STATUS_VALUES = ['completed', 'failed', 'killed'] as const;
@@ -10,7 +9,6 @@ export const TERMINAL_TASK_STATUS_VALUES = ['completed', 'failed', 'killed'] as
10
9
  export type TaskStatus = (typeof TASK_STATUS_VALUES)[number];
11
10
  export type TerminalTaskStatus = (typeof TERMINAL_TASK_STATUS_VALUES)[number];
12
11
  export type KillKind = 'user' | 'timeout' | 'output_cap' | 'shutdown';
13
- export type ReloadShellStopKind = KillKind | 'handoff_expired';
14
12
 
15
13
  export type TerminalPublicationState = 'pending' | 'delivered' | 'abandoned';
16
14
  export type TerminalPublicationAbandonReason =
@@ -18,178 +16,7 @@ export type TerminalPublicationAbandonReason =
18
16
  | 'publisher_closed'
19
17
  | 'gate_rejected'
20
18
  | 'retry_exhausted'
21
- | 'retention_limit'
22
- | 'reload_handoff_expired';
23
-
24
- export type ReloadSurvivalErrorCode =
25
- | 'pi_bg_survive_reload_invalid'
26
- | 'pi_bg_survive_reload_requires_non_agent'
27
- | 'pi_bg_survive_reload_unsupported_task_kind'
28
- | 'pi_bg_reload_owner_unavailable'
29
- | 'pi_bg_reload_owner_protocol_incompatible'
30
- | 'pi_bg_reload_owner_activation_conflict'
31
- | 'pi_bg_reload_owner_stale_claim'
32
- | 'pi_bg_reload_handoff_expired';
33
-
34
- export class ReloadSurvivalError extends Error {
35
- constructor(
36
- readonly code: ReloadSurvivalErrorCode,
37
- message: string,
38
- ) {
39
- super(`${code}: ${message}`);
40
- this.name = 'ReloadSurvivalError';
41
- }
42
- }
43
-
44
- export interface ReloadSurvivalSnapshotV1 {
45
- schemaVersion: 'pi-background-tasks.reload-shell.v1';
46
- authority: 'same-process-live-owner';
47
- hostPid: number;
48
- sessionId: string;
49
- cwdRealpath: string;
50
- launchNonce: string;
51
- completionId: string;
52
- spawnedAt: number;
53
- childPid: number;
54
- timeoutDeadlineAt?: number | undefined;
55
- outputCapBytes: number;
56
- posixProcessGroupId?: number | undefined;
57
- windowsTreeRootPid?: number | undefined;
58
- leaseGeneration: number;
59
- handoffCount: number;
60
- }
61
-
62
- export interface ReloadShellIdentityV1 {
63
- readonly hostPid: number;
64
- readonly sessionId: string;
65
- readonly cwdRealpath: string;
66
- }
67
-
68
- export interface ReloadShellActivationLeaseV1 {
69
- readonly protocol: 'pi-background-tasks.reload-shell-owner.v1';
70
- readonly hubNonce: string;
71
- readonly identityKey: string;
72
- readonly generation: number;
73
- readonly activationNonce: string;
74
- }
75
-
76
- export interface ReloadShellActivationClaimV1 {
77
- readonly protocol: 'pi-background-tasks.reload-shell-owner.v1';
78
- readonly hubNonce: string;
79
- readonly claimNonce: string;
80
- readonly identity: ReloadShellIdentityV1;
81
- readonly identityKey: string;
82
- readonly generation: number;
83
- readonly activationNonce: string;
84
- readonly expiresAt?: number | undefined;
85
- readonly executions: readonly ReloadableShellExecutionV1[];
86
- }
87
-
88
- export interface ReloadShellProcessV1 {
89
- readonly pid?: number | undefined;
90
- stdout?:
91
- | {
92
- on(event: 'data', listener: (data: Buffer | string) => void): unknown;
93
- off?(event: 'data', listener: (data: Buffer | string) => void): unknown;
94
- }
95
- | null
96
- | undefined;
97
- stderr?:
98
- | {
99
- on(event: 'data', listener: (data: Buffer | string) => void): unknown;
100
- off?(event: 'data', listener: (data: Buffer | string) => void): unknown;
101
- }
102
- | null
103
- | undefined;
104
- kill(signal?: NodeJS.Signals): boolean;
105
- on(event: 'error', listener: (error: Error) => void): unknown;
106
- on(
107
- event: 'close',
108
- listener: (code: number | null, signal: NodeJS.Signals | null) => void,
109
- ): unknown;
110
- off?(event: 'error', listener: (error: Error) => void): unknown;
111
- off?(
112
- event: 'close',
113
- listener: (code: number | null, signal: NodeJS.Signals | null) => void,
114
- ): unknown;
115
- }
116
-
117
- export interface ReloadShellOwnerEventSinkV1 {
118
- readonly onChanged: (execution: ReloadableShellExecutionV1) => void;
119
- readonly onTerminal: (execution: ReloadableShellExecutionV1) => void;
120
- }
121
-
122
- export type ReloadShellNotificationState = 'disabled' | 'pending' | 'sending' | 'delivered';
123
-
124
- export interface ReloadableShellExecutionV1 {
125
- readonly protocol: 'pi-background-tasks.reload-shell-owner.v1';
126
- readonly launchNonce: string;
127
- readonly completionId: string;
128
- readonly task: BgTask;
129
- child: ReloadShellProcessV1 | undefined;
130
- outputStream: WriteStream | undefined;
131
- readonly spawnedAt: number;
132
- readonly timeoutDeadlineAt?: number | undefined;
133
- readonly outputCapBytes: number;
134
- readonly terminal: Promise<BgTask>;
135
- readonly requestStop: (kind: ReloadShellStopKind, reason?: string) => Promise<BgTask>;
136
- phase: 'starting' | 'running' | 'stop_requested' | 'finalizing' | 'terminal' | 'released';
137
- closeObservation?:
138
- | {
139
- code: number | null;
140
- signal: NodeJS.Signals | null;
141
- observedAt: number;
142
- }
143
- | undefined;
144
- admissionCommitted: boolean;
145
- notificationState: ReloadShellNotificationState;
146
- readonly commitInitialMetadata: (signal?: AbortSignal) => Promise<void>;
147
- readonly failAdmission: (error: Error) => void;
148
- readonly setOwnerEventSink: (sink: ReloadShellOwnerEventSinkV1 | undefined) => void;
149
- readonly markAdmissionCommitted: (generation: number, handoffCount: number) => void;
150
- readonly updateLeaseAudit: (generation: number, handoffCount: number) => void;
151
- readonly abandonReloadHandoff: () => void;
152
- readonly beginNotification: (lease: ReloadShellActivationLeaseV1) => string | undefined;
153
- readonly finishNotification: (token: string, delivered: boolean) => void;
154
- readonly releaseResources: () => void;
155
- }
156
-
157
- export interface ReloadShellHostAdapterV1 {
158
- readonly activationNonce: string;
159
- readonly onBound: (lease: ReloadShellActivationLeaseV1) => void;
160
- readonly onChanged: (execution: ReloadableShellExecutionV1) => void;
161
- readonly onTerminal: (execution: ReloadableShellExecutionV1) => void;
162
- }
163
-
164
- export interface ReloadShellOwnerHubV1 {
165
- readonly protocol: 'pi-background-tasks.reload-shell-owner.v1';
166
- readonly hubNonce: string;
167
- beginActivation(
168
- identity: ReloadShellIdentityV1,
169
- startReason: string,
170
- activationNonce: string,
171
- ): ReloadShellActivationClaimV1;
172
- commitActivation(
173
- claim: ReloadShellActivationClaimV1,
174
- adapter: ReloadShellHostAdapterV1,
175
- ): ReloadShellActivationLeaseV1;
176
- abortActivation(claim: ReloadShellActivationClaimV1, error: unknown): void;
177
- beginReloadHandoff(lease: ReloadShellActivationLeaseV1): readonly ReloadableShellExecutionV1[];
178
- releaseActivation(lease: ReloadShellActivationLeaseV1): void;
179
- registerExecution(
180
- lease: ReloadShellActivationLeaseV1,
181
- execution: ReloadableShellExecutionV1,
182
- ): void;
183
- markAdmissionCommitted(
184
- lease: ReloadShellActivationLeaseV1,
185
- execution: ReloadableShellExecutionV1,
186
- ): void;
187
- releaseExecution(
188
- leaseOrClaim: ReloadShellActivationLeaseV1 | ReloadShellActivationClaimV1,
189
- execution: ReloadableShellExecutionV1,
190
- ): void;
191
- isCurrentLease(lease: ReloadShellActivationLeaseV1): boolean;
192
- }
19
+ | 'retention_limit';
193
20
 
194
21
  export interface BgTaskSnapshot {
195
22
  id: string;
@@ -204,9 +31,8 @@ export interface BgTaskSnapshot {
204
31
  exitCode?: number | null | undefined;
205
32
  signal?: string | null | undefined;
206
33
  pid?: number | undefined;
34
+ posixProcessGroupId?: number | undefined;
207
35
  bytesWritten: number;
208
- surviveReload: boolean;
209
- reloadSurvival?: ReloadSurvivalSnapshotV1 | undefined;
210
36
  error?: string | undefined;
211
37
  notified: boolean;
212
38
  notifyOnCompletion: boolean;
@@ -222,13 +48,18 @@ export interface BgTask extends Omit<BgTaskSnapshot, 'name'> {
222
48
  metadataAbsPath: string;
223
49
  eventsAbsPath?: string | undefined;
224
50
  stderrAbsPath?: string | undefined;
225
- child?: BackgroundTaskChildProcess | undefined;
226
- /** Immutable in-memory ownership captured from a detached POSIX spawn; never restored from metadata. */
51
+ /** POSIX group authority captured at spawn; never restored except by adoption. */
227
52
  ownedPosixProcessGroupId?: number | undefined;
228
53
  /** One-way latch preventing any later signal after group authority is released. */
229
54
  posixProcessGroupSignalAuthorityReleased?: boolean | undefined;
230
- stream?: WriteStream | undefined;
231
- timeoutHandle?: NodeJS.Timeout | undefined;
55
+ /** Absolute marker path; the wrapper writes the exit code here on settlement. */
56
+ exitMarkerAbsPath?: string | undefined;
57
+ /** False while the detached spawn is still in flight; the poller must not reap it then. */
58
+ launched?: boolean | undefined;
59
+ /** False only for an unclassified user shell launched without a wrapper. */
60
+ exitMarkerTrusted?: boolean | undefined;
61
+ runnerScriptFile?: string | undefined;
62
+ pollInFlight?: boolean | undefined;
232
63
  killKind?: KillKind | undefined;
233
64
  killSignalSent?: boolean | undefined;
234
65
  killEscalationTimer?: NodeJS.Timeout | undefined;
@@ -245,12 +76,6 @@ export interface BgTask extends Omit<BgTaskSnapshot, 'name'> {
245
76
  terminalPublishRetryHandle?: NodeJS.Timeout | undefined;
246
77
  /** Optional protocol barrier used by EventBus run requests so early child exits cannot publish before the run response is observable. */
247
78
  terminalPublicationGate?: Promise<void> | undefined;
248
- /** In-memory same-process authority for an opted ordinary shell task; never serialized. */
249
- reloadExecution?: ReloadableShellExecutionV1 | undefined;
250
- /** Fresh-registry terminal host delivery state for an imported owner execution. */
251
- reloadHostDeliveryInFlight?: boolean | undefined;
252
- reloadHostDeliverySettled?: boolean | undefined;
253
- reloadHostNotificationSettled?: boolean | undefined;
254
79
  metadataWriteChain?: Promise<void> | undefined;
255
80
  waiters: Array<() => void>;
256
81
  }
@@ -342,7 +167,6 @@ export interface StartTaskOptions {
342
167
  timeoutSeconds?: number | undefined;
343
168
  notifyOnCompletion?: boolean | undefined;
344
169
  triggerOnCompletion?: boolean | undefined;
345
- surviveReload?: boolean | undefined;
346
170
  /** @internal EventBus protocol barrier; callers should not set this outside the extension service. */
347
171
  terminalPublicationGate?: Promise<void> | undefined;
348
172
  }
@@ -452,11 +276,9 @@ function parseNameValueAndRest(valueAndRest: string): { value: string; rest: str
452
276
  export function parseBgCommandArgs(args: string): {
453
277
  name?: string;
454
278
  command: string;
455
- surviveReload: boolean;
456
279
  } {
457
280
  let input = args.trim();
458
281
  let name: string | undefined;
459
- let surviveReload = false;
460
282
 
461
283
  while (input) {
462
284
  let consumed = false;
@@ -484,27 +306,25 @@ export function parseBgCommandArgs(args: string): {
484
306
  }
485
307
  if (consumed) continue;
486
308
 
309
+ // Legacy flag: recognized and silently dropped. Task survival across /reload
310
+ // is now the only behavior, so the flag has nothing left to toggle.
487
311
  if (
488
312
  input === '--survive-reload' ||
489
313
  input.startsWith('--survive-reload ') ||
490
- input.startsWith('--survive-reload\t')
314
+ input.startsWith('--survive-reload\t') ||
315
+ input.startsWith('--survive-reload=')
491
316
  ) {
492
- if (surviveReload) {
493
- throw new ReloadSurvivalError(
494
- 'pi_bg_survive_reload_invalid',
495
- '/bg accepts --survive-reload at most once',
496
- );
317
+ if (input.startsWith('--survive-reload=')) {
318
+ // Legacy bare-flag-only policy: a `=` value was never honored, so the
319
+ // ignored flag consumes its whole value up to the next token.
320
+ const afterEquals = input.slice('--survive-reload='.length);
321
+ const valueEnd = afterEquals.search(/\s/u);
322
+ input = valueEnd === -1 ? '' : afterEquals.slice(valueEnd).trimStart();
323
+ } else {
324
+ input = input.slice('--survive-reload'.length).trimStart();
497
325
  }
498
- surviveReload = true;
499
- input = input.slice('--survive-reload'.length).trimStart();
500
326
  continue;
501
327
  }
502
- if (input.startsWith('--survive-reload=')) {
503
- throw new ReloadSurvivalError(
504
- 'pi_bg_survive_reload_invalid',
505
- '/bg accepts only the bare --survive-reload flag',
506
- );
507
- }
508
328
 
509
329
  if (input === '--') {
510
330
  input = '';
@@ -517,7 +337,7 @@ export function parseBgCommandArgs(args: string): {
517
337
  break;
518
338
  }
519
339
 
520
- return name ? { name, command: input, surviveReload } : { command: input, surviveReload };
340
+ return name ? { name, command: input } : { command: input };
521
341
  }
522
342
 
523
343
  export function formatDuration(ms: number): string {
@@ -850,9 +670,8 @@ export function snapshot(task: BgTask): BgTaskSnapshot {
850
670
  exitCode: task.exitCode,
851
671
  signal: task.signal,
852
672
  pid: task.pid,
673
+ posixProcessGroupId: task.ownedPosixProcessGroupId,
853
674
  bytesWritten: task.bytesWritten,
854
- surviveReload: task.surviveReload === true,
855
- reloadSurvival: task.reloadSurvival,
856
675
  error: task.error,
857
676
  notified: task.notified,
858
677
  notifyOnCompletion: task.notifyOnCompletion,