@gajae-code/coding-agent 0.15.5 → 0.15.6
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/CHANGELOG.md +26 -1
- package/dist/types/async/job-manager.d.ts +83 -0
- package/dist/types/cli/auth-gateway-cli.d.ts +17 -0
- package/dist/types/cli/model-presets-cli.d.ts +9 -0
- package/dist/types/cli/update-cli.d.ts +2 -0
- package/dist/types/commands/auth-gateway.d.ts +3 -0
- package/dist/types/commands/gc.d.ts +13 -0
- package/dist/types/commands/model-presets.d.ts +21 -0
- package/dist/types/config/autorouting-tier-map.d.ts +96 -0
- package/dist/types/config/file-lock.d.ts +49 -2
- package/dist/types/config/internal/model-preset-registry-test-state.d.ts +4 -0
- package/dist/types/config/internal/model-preset-registry-test-support.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +3 -2
- package/dist/types/config/model-preset-registry.d.ts +368 -0
- package/dist/types/config/model-profile-activation.d.ts +39 -0
- package/dist/types/config/model-profiles.d.ts +5 -3
- package/dist/types/config/model-registry.d.ts +11 -5
- package/dist/types/config/settings-schema.d.ts +9 -0
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -5
- package/dist/types/coordinator-mcp/projection-scan.d.ts +46 -0
- package/dist/types/extensibility/extensions/types.d.ts +2 -5
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +58 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +35 -6
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/main.d.ts +3 -1
- package/dist/types/modes/components/jobs-overlay-model.d.ts +4 -2
- package/dist/types/modes/jobs-observer.d.ts +36 -0
- package/dist/types/sdk/broker/broker.d.ts +3 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +3 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +4 -0
- package/dist/types/sdk/client/discovery.d.ts +1 -0
- package/dist/types/sdk/host/model-pin.d.ts +7 -2
- package/dist/types/sdk/host/sdk-run-capability.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +15 -1
- package/dist/types/sdk/prompt-deadline-manager.d.ts +11 -1
- package/dist/types/sdk/protocol/adapter-validation.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +9 -0
- package/dist/types/session/account-inventory.d.ts +1 -0
- package/dist/types/session/agent-session.d.ts +19 -7
- package/dist/types/session/fold-coordinator.d.ts +201 -0
- package/dist/types/session/sdk-run-capability-internal.d.ts +7 -0
- package/dist/types/session/sdk-run-capability.d.ts +2 -0
- package/dist/types/telemetry/control.d.ts +11 -0
- package/dist/types/telemetry/events.d.ts +20 -0
- package/dist/types/telemetry/index.d.ts +3 -0
- package/dist/types/telemetry/transport.d.ts +22 -0
- package/dist/types/tools/bash-interactive.d.ts +23 -0
- package/dist/types/tools/index.d.ts +6 -5
- package/dist/types/web/search/providers/base.d.ts +9 -4
- package/dist/types/web/search/providers/xai.d.ts +2 -1
- package/dist/types/web/search/types.d.ts +14 -0
- package/package.json +10 -7
- package/scripts/generate-hotkeys-docs.ts +4 -0
- package/scripts/generate-sdk-operation-inventory.ts +3 -3
- package/src/async/job-manager.ts +671 -39
- package/src/cli/auth-broker-cli.ts +40 -12
- package/src/cli/auth-gateway-cli.ts +273 -97
- package/src/cli/config-cli.ts +2 -2
- package/src/cli/fast-help.ts +3 -0
- package/src/cli/model-presets-cli.ts +100 -0
- package/src/cli/update-cli.ts +40 -5
- package/src/cli.ts +6 -2
- package/src/commands/auth-gateway.ts +12 -8
- package/src/commands/gc.ts +19 -0
- package/src/commands/model-presets.ts +24 -0
- package/src/config/autorouting-tier-map.ts +32 -0
- package/src/config/file-lock-gc.ts +9 -4
- package/src/config/file-lock.ts +775 -59
- package/src/config/internal/model-preset-registry-test-state.ts +28 -0
- package/src/config/internal/model-preset-registry-test-support.ts +11 -0
- package/src/config/keybindings.ts +6 -2
- package/src/config/model-preset-registry.ts +2799 -0
- package/src/config/model-profile-activation.ts +115 -28
- package/src/config/model-profile-contract.ts +4 -1
- package/src/config/model-profiles.ts +51 -9
- package/src/config/model-registry.ts +1021 -122
- package/src/config/settings-schema.ts +11 -0
- package/src/config/settings.ts +6 -1
- package/src/coordinator-mcp/model-preset.ts +32 -17
- package/src/coordinator-mcp/projection-scan.ts +571 -0
- package/src/coordinator-mcp/server.ts +19 -24
- package/src/coordinator-mcp/session-reaper.ts +9 -3
- package/src/extensibility/extensions/loader.ts +0 -1
- package/src/extensibility/extensions/types.ts +2 -3
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +1 -0
- package/src/gjc-runtime/empty-delete-gc.ts +301 -0
- package/src/gjc-runtime/gc-render.ts +29 -0
- package/src/gjc-runtime/gc-runtime.ts +174 -11
- package/src/gjc-runtime/session-state-lock.ts +1242 -141
- package/src/gjc-runtime/session-state-sidecar.ts +76 -3
- package/src/index.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +9 -8
- package/src/lsp/config.ts +4 -3
- package/src/lsp/path-trust.ts +12 -6
- package/src/main.ts +9 -2
- package/src/modes/acp/acp-agent.ts +11 -5
- package/src/modes/components/jobs-overlay-model.ts +99 -16
- package/src/modes/components/model-selector.ts +158 -18
- package/src/modes/components/status-line/segments.ts +6 -1
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +23 -6
- package/src/modes/interactive-mode.ts +14 -1
- package/src/modes/jobs-observer.ts +149 -16
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/utils/injected-user-submission.ts +15 -1
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/fold-receipt.md +3 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +17 -2
- package/src/sdk/broker/broker.ts +48 -21
- package/src/sdk/broker/lifecycle.ts +32 -6
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +11 -1
- package/src/sdk/bus/kind-aware-reconciliation.ts +36 -6
- package/src/sdk/bus/prompt-reconciliation.ts +28 -4
- package/src/sdk/bus/slack-conversation.ts +5 -1
- package/src/sdk/cli/session-cli.ts +12 -1
- package/src/sdk/client/discovery.ts +3 -1
- package/src/sdk/host/control/dispatch.ts +3 -0
- package/src/sdk/host/model-pin.ts +185 -25
- package/src/sdk/host/sdk-run-capability.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +371 -37
- package/src/sdk/host/session-runtime.ts +364 -52
- package/src/sdk/prompt-deadline-manager.ts +16 -1
- package/src/sdk/protocol/adapter-validation.ts +27 -0
- package/src/sdk/protocol/operation-inventory.generated.json +2 -2
- package/src/sdk/protocol/operation-registry.ts +4 -1
- package/src/sdk/router/session-router.ts +228 -96
- package/src/sdk/session.ts +83 -12
- package/src/session/account-inventory.ts +27 -11
- package/src/session/agent-session.ts +678 -103
- package/src/session/fold-coordinator.ts +433 -0
- package/src/session/sdk-run-capability-internal.ts +15 -0
- package/src/session/sdk-run-capability.ts +2 -0
- package/src/session/session-manager.ts +5 -4
- package/src/setup/provider-presets.json +1 -1
- package/src/slash-commands/helpers/credential-switch.ts +6 -1
- package/src/task/executor.ts +19 -3
- package/src/telemetry/control.ts +26 -0
- package/src/telemetry/events.ts +142 -0
- package/src/telemetry/index.ts +3 -0
- package/src/telemetry/transport.ts +87 -0
- package/src/tools/bash-interactive.ts +182 -85
- package/src/tools/bash.ts +772 -67
- package/src/tools/index.ts +7 -5
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/web/search/provider.ts +120 -11
- package/src/web/search/providers/anthropic.ts +89 -17
- package/src/web/search/providers/base.ts +9 -4
- package/src/web/search/providers/gemini.ts +87 -15
- package/src/web/search/providers/openai-compatible.ts +23 -8
- package/src/web/search/providers/xai.ts +98 -12
- package/src/web/search/types.ts +16 -0
|
@@ -31,7 +31,12 @@ import {
|
|
|
31
31
|
removeCanonicalBlob,
|
|
32
32
|
} from "../session/blob-store";
|
|
33
33
|
import { FileSessionStorage, probeSessionRetirement, retireSessionTranscript } from "../session/session-storage";
|
|
34
|
-
|
|
34
|
+
import {
|
|
35
|
+
collectEmptyDeleteReceipts,
|
|
36
|
+
type EmptyDeleteGcRecord,
|
|
37
|
+
type EmptyDeleteGcReport,
|
|
38
|
+
runEmptyDeleteGc,
|
|
39
|
+
} from "./empty-delete-gc";
|
|
35
40
|
import { buildGcReportText } from "./gc-render";
|
|
36
41
|
import { collectSessionScopeUsage, type GcSessionScopeUsage, shouldReportSessionScope } from "./gc-session-scope";
|
|
37
42
|
|
|
@@ -168,6 +173,7 @@ export interface GcReport {
|
|
|
168
173
|
* PID-liveness axis above is unchanged by its absence or presence.
|
|
169
174
|
*/
|
|
170
175
|
disk?: GcDiskReport;
|
|
176
|
+
empty_delete_receipts?: EmptyDeleteGcReport;
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
export interface GcRunResult {
|
|
@@ -274,9 +280,11 @@ interface ParsedGcArgs {
|
|
|
274
280
|
json: boolean;
|
|
275
281
|
prune: boolean;
|
|
276
282
|
repairSessionIndex: boolean;
|
|
277
|
-
/** Opt-in second axis: report (and with `--prune`, reclaim) on-disk retention. */
|
|
278
283
|
disk: boolean;
|
|
279
284
|
help: boolean;
|
|
285
|
+
emptyDeleteReceipts: boolean;
|
|
286
|
+
emptyDeleteRoots: string[];
|
|
287
|
+
emptyDeleteManifests: string[];
|
|
280
288
|
}
|
|
281
289
|
|
|
282
290
|
class GcUsageError extends Error {}
|
|
@@ -286,10 +294,13 @@ function parseGcArgs(argv: string[]): ParsedGcArgs {
|
|
|
286
294
|
let prune = false;
|
|
287
295
|
let repairSessionIndex = false;
|
|
288
296
|
let disk = false;
|
|
289
|
-
|
|
290
297
|
let dryRun = false;
|
|
291
298
|
let help = false;
|
|
292
|
-
|
|
299
|
+
let emptyDeleteReceipts = false;
|
|
300
|
+
const emptyDeleteRoots: string[] = [];
|
|
301
|
+
const emptyDeleteManifests: string[] = [];
|
|
302
|
+
for (let i = 0; i < argv.length; i++) {
|
|
303
|
+
const arg = argv[i]!;
|
|
293
304
|
switch (arg) {
|
|
294
305
|
case "--json":
|
|
295
306
|
case "-j":
|
|
@@ -305,7 +316,6 @@ function parseGcArgs(argv: string[]): ParsedGcArgs {
|
|
|
305
316
|
case "--disk":
|
|
306
317
|
disk = true;
|
|
307
318
|
break;
|
|
308
|
-
|
|
309
319
|
case "--dry-run":
|
|
310
320
|
dryRun = true;
|
|
311
321
|
break;
|
|
@@ -313,15 +323,34 @@ function parseGcArgs(argv: string[]): ParsedGcArgs {
|
|
|
313
323
|
case "-h":
|
|
314
324
|
help = true;
|
|
315
325
|
break;
|
|
326
|
+
case "--empty-delete-receipts":
|
|
327
|
+
emptyDeleteReceipts = true;
|
|
328
|
+
break;
|
|
329
|
+
case "--root": {
|
|
330
|
+
const value = argv[++i];
|
|
331
|
+
// A following option token is a missing operand, not a root path. Any
|
|
332
|
+
// dash-prefixed value is rejected (short flags like -j included); spell a
|
|
333
|
+
// genuinely dash-prefixed path as ./-name so it is unambiguous.
|
|
334
|
+
if (!value || value.startsWith("-")) throw new GcUsageError("missing_root");
|
|
335
|
+
emptyDeleteRoots.push(value);
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
case "--manifest": {
|
|
339
|
+
const value = argv[++i];
|
|
340
|
+
if (!value || value.startsWith("-")) throw new GcUsageError("missing_manifest");
|
|
341
|
+
emptyDeleteManifests.push(value);
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
316
344
|
default:
|
|
317
345
|
throw new GcUsageError(`unknown_flag:${arg}`);
|
|
318
346
|
}
|
|
319
347
|
}
|
|
320
348
|
if (repairSessionIndex && prune) throw new GcUsageError("repair_session_index_cannot_combine_with_prune");
|
|
349
|
+
if (!emptyDeleteReceipts && (emptyDeleteRoots.length > 0 || emptyDeleteManifests.length > 0))
|
|
350
|
+
throw new GcUsageError("empty_delete_operands_require_feature_flag");
|
|
321
351
|
if (repairSessionIndex && dryRun) throw new GcUsageError("repair_session_index_cannot_combine_with_dry_run");
|
|
322
|
-
// Explicit --dry-run always wins over --prune/--force.
|
|
323
352
|
if (dryRun) prune = false;
|
|
324
|
-
return { json, prune, repairSessionIndex, disk, help };
|
|
353
|
+
return { json, prune, repairSessionIndex, disk, help, emptyDeleteReceipts, emptyDeleteRoots, emptyDeleteManifests };
|
|
325
354
|
}
|
|
326
355
|
|
|
327
356
|
/**
|
|
@@ -404,6 +433,7 @@ export async function collectGcReport(adapters: GcStoreAdapter[], ctx: GcContext
|
|
|
404
433
|
export function computeExitCode(report: GcReport): number {
|
|
405
434
|
if (report.errors.length > 0) return 1;
|
|
406
435
|
if (!report.dry_run && report.counts.failed > 0) return 1;
|
|
436
|
+
if (report.empty_delete_receipts?.errors.length) return 1;
|
|
407
437
|
if (report.disk) {
|
|
408
438
|
if (report.disk.errors.length > 0) return 1;
|
|
409
439
|
if (!report.disk.dry_run && report.disk.totals.failed > 0) return 1;
|
|
@@ -415,6 +445,63 @@ function resolveGcAgentDir(env: NodeJS.ProcessEnv): string {
|
|
|
415
445
|
return env.GJC_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim() || getAgentDir();
|
|
416
446
|
}
|
|
417
447
|
|
|
448
|
+
/**
|
|
449
|
+
* Validate operator-supplied empty-delete roots before any ordinary GC mutation.
|
|
450
|
+
*
|
|
451
|
+
* These roots are explicit operands, so a missing, symlink, non-directory, or
|
|
452
|
+
* unreadable root is a usage failure rather than a partial GC report. The
|
|
453
|
+
* collector repeats its own race-safe checks after this preflight.
|
|
454
|
+
*/
|
|
455
|
+
async function preflightEmptyDeleteRoot(root: string): Promise<void> {
|
|
456
|
+
const resolvedRoot = path.resolve(root);
|
|
457
|
+
let stat: Stats;
|
|
458
|
+
try {
|
|
459
|
+
stat = await fsp.lstat(resolvedRoot);
|
|
460
|
+
} catch (error) {
|
|
461
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
462
|
+
throw new GcUsageError(`${root}: missing_root`);
|
|
463
|
+
}
|
|
464
|
+
throw new GcUsageError(`${root}: ${error instanceof Error ? error.message : String(error)}`);
|
|
465
|
+
}
|
|
466
|
+
if (stat.isSymbolicLink()) throw new GcUsageError(`${root}: symlink_root`);
|
|
467
|
+
if (!stat.isDirectory()) throw new GcUsageError(`${root}: not_directory`);
|
|
468
|
+
try {
|
|
469
|
+
await fsp.readdir(resolvedRoot);
|
|
470
|
+
} catch (error) {
|
|
471
|
+
throw new GcUsageError(`${root}: ${error instanceof Error ? error.message : String(error)}`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Collect every explicit empty-delete root once before starting any destructive
|
|
477
|
+
* operation. The second pass reuses those identities so a later root race cannot
|
|
478
|
+
* leave an earlier root pruned before the race is discovered.
|
|
479
|
+
*/
|
|
480
|
+
async function collectEmptyDeleteReceiptsForGc(
|
|
481
|
+
roots: string[],
|
|
482
|
+
): Promise<{ report: EmptyDeleteGcReport; records: Map<string, EmptyDeleteGcRecord[]> }> {
|
|
483
|
+
const collected = new Map<string, EmptyDeleteGcRecord[]>();
|
|
484
|
+
const collect = async (root: string): Promise<EmptyDeleteGcRecord[]> => {
|
|
485
|
+
const records = await collectEmptyDeleteReceipts(root);
|
|
486
|
+
collected.set(root, records);
|
|
487
|
+
return records.map(record => ({
|
|
488
|
+
...record,
|
|
489
|
+
identity: record.identity ? { ...record.identity } : undefined,
|
|
490
|
+
retainedPaths: record.retainedPaths ? { ...record.retainedPaths } : undefined,
|
|
491
|
+
}));
|
|
492
|
+
};
|
|
493
|
+
const report = await runEmptyDeleteGc({ roots, prune: false }, { collect });
|
|
494
|
+
return { report, records: collected };
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function cloneEmptyDeleteRecords(records: EmptyDeleteGcRecord[]): EmptyDeleteGcRecord[] {
|
|
498
|
+
return records.map(record => ({
|
|
499
|
+
...record,
|
|
500
|
+
identity: record.identity ? { ...record.identity } : undefined,
|
|
501
|
+
retainedPaths: record.retainedPaths ? { ...record.retainedPaths } : undefined,
|
|
502
|
+
}));
|
|
503
|
+
}
|
|
504
|
+
|
|
418
505
|
/**
|
|
419
506
|
* Locate and measure the managed scope for `cwd`.
|
|
420
507
|
*
|
|
@@ -482,9 +569,77 @@ export async function runGjcGcCommand(
|
|
|
482
569
|
return { stdout: gcHelpText(), stderr: "", status: 0 };
|
|
483
570
|
}
|
|
484
571
|
|
|
572
|
+
// Resolve and validate every empty-delete operand BEFORE any store collection or
|
|
573
|
+
// prune can mutate: a malformed manifest or missing operand must fail with status 2
|
|
574
|
+
// while every candidate is still untouched.
|
|
575
|
+
let emptyDeleteRoots: string[] = [];
|
|
576
|
+
if (parsed.emptyDeleteReceipts) {
|
|
577
|
+
emptyDeleteRoots = [...parsed.emptyDeleteRoots];
|
|
578
|
+
if (emptyDeleteRoots.some(root => root.includes("\0"))) {
|
|
579
|
+
return { stdout: "", stderr: "gjc gc: root_invalid\n", status: 2 };
|
|
580
|
+
}
|
|
581
|
+
// Every supplied manifest is validated, not just the last: a malformed earlier
|
|
582
|
+
// manifest must fail the run before any store collection or prune can mutate.
|
|
583
|
+
for (const manifestPath of parsed.emptyDeleteManifests) {
|
|
584
|
+
let parsedManifest: unknown;
|
|
585
|
+
try {
|
|
586
|
+
const raw = await fsp.readFile(manifestPath, "utf8");
|
|
587
|
+
parsedManifest = JSON.parse(raw);
|
|
588
|
+
} catch (error) {
|
|
589
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
590
|
+
return { stdout: "", stderr: `gjc gc: manifest_invalid: ${message}\n`, status: 2 };
|
|
591
|
+
}
|
|
592
|
+
if (typeof parsedManifest !== "object" || parsedManifest === null || Array.isArray(parsedManifest)) {
|
|
593
|
+
return { stdout: "", stderr: "gjc gc: manifest_shape_invalid\n", status: 2 };
|
|
594
|
+
}
|
|
595
|
+
const manifestRoots = (parsedManifest as { roots?: unknown }).roots;
|
|
596
|
+
if (!Array.isArray(manifestRoots)) {
|
|
597
|
+
return { stdout: "", stderr: "gjc gc: manifest_roots_required\n", status: 2 };
|
|
598
|
+
}
|
|
599
|
+
for (const root of manifestRoots) {
|
|
600
|
+
if (typeof root !== "string" || root.length === 0 || root.includes("\0")) {
|
|
601
|
+
return { stdout: "", stderr: "gjc gc: manifest_root_invalid\n", status: 2 };
|
|
602
|
+
}
|
|
603
|
+
emptyDeleteRoots.push(root);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (emptyDeleteRoots.length === 0) {
|
|
607
|
+
return { stdout: "", stderr: "gjc gc: empty_delete_receipts_requires_root_or_manifest\n", status: 2 };
|
|
608
|
+
}
|
|
609
|
+
try {
|
|
610
|
+
for (const root of emptyDeleteRoots) await preflightEmptyDeleteRoot(root);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
const message = error instanceof GcUsageError ? error.message : String(error);
|
|
613
|
+
return { stdout: "", stderr: `gjc gc: ${message}\n`, status: 2 };
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
let emptyDeleteReceipts: EmptyDeleteGcReport | undefined;
|
|
617
|
+
let emptyDeleteRecords: Map<string, EmptyDeleteGcRecord[]> | undefined;
|
|
618
|
+
if (parsed.emptyDeleteReceipts) {
|
|
619
|
+
// Discovery is deliberately read-only and covers every root before either the
|
|
620
|
+
// empty-delete unlink or an ordinary --prune adapter can mutate anything.
|
|
621
|
+
const collected = await collectEmptyDeleteReceiptsForGc(emptyDeleteRoots);
|
|
622
|
+
emptyDeleteReceipts = collected.report;
|
|
623
|
+
emptyDeleteRecords = collected.records;
|
|
624
|
+
if (parsed.prune && emptyDeleteReceipts.errors.length === 0) {
|
|
625
|
+
emptyDeleteReceipts = await runEmptyDeleteGc(
|
|
626
|
+
{ roots: emptyDeleteRoots, prune: true },
|
|
627
|
+
{
|
|
628
|
+
collect: async root => {
|
|
629
|
+
const records = emptyDeleteRecords?.get(root);
|
|
630
|
+
if (!records) throw new Error(`${root}: empty_delete_collection_missing`);
|
|
631
|
+
return cloneEmptyDeleteRecords(records);
|
|
632
|
+
},
|
|
633
|
+
},
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
485
637
|
const resolvedAdapters = adapters ?? (await defaultGcAdapters());
|
|
486
638
|
const ctx: GcContext = { probe: gcPidProbe, force: parsed.prune, env, cwd };
|
|
487
|
-
|
|
639
|
+
// A failed explicit empty-delete discovery or cleanup is a safety gate: report
|
|
640
|
+
// ordinary candidates, but never perform their destructive prune in that run.
|
|
641
|
+
const ordinaryPrune = parsed.prune && (emptyDeleteReceipts?.errors.length ?? 0) === 0;
|
|
642
|
+
const report = await collectGcReport(resolvedAdapters, ctx, ordinaryPrune);
|
|
488
643
|
report.operation = parsed.repairSessionIndex ? "repair_session_index" : parsed.prune ? "prune" : "dry_run";
|
|
489
644
|
report.session_index = await collectSessionIndexHealth(parsed.repairSessionIndex, resolveGcAgentDir(env));
|
|
490
645
|
if (parsed.disk) {
|
|
@@ -492,9 +647,10 @@ export async function runGjcGcCommand(
|
|
|
492
647
|
agentDir: resolveGcAgentDir(env),
|
|
493
648
|
env,
|
|
494
649
|
policy: resolveGcDiskPolicy(diskPolicy),
|
|
495
|
-
prune:
|
|
650
|
+
prune: ordinaryPrune,
|
|
496
651
|
});
|
|
497
652
|
}
|
|
653
|
+
if (emptyDeleteReceipts) report.empty_delete_receipts = emptyDeleteReceipts;
|
|
498
654
|
const scopeUsage = await collectGcSessionScope(cwd, resolveGcAgentDir(env));
|
|
499
655
|
if (scopeUsage && shouldReportSessionScope(scopeUsage)) report.session_scope = scopeUsage;
|
|
500
656
|
const sessionIndexFailed =
|
|
@@ -503,7 +659,7 @@ export async function runGjcGcCommand(
|
|
|
503
659
|
report.session_index?.status === "repair_failed";
|
|
504
660
|
const status = sessionIndexFailed ? 1 : computeExitCode(report);
|
|
505
661
|
const stdout = parsed.json
|
|
506
|
-
? `${JSON.stringify(report,
|
|
662
|
+
? `${JSON.stringify(report, (_key, value) => (typeof value === "bigint" ? value.toString() : value), 2)}\n`
|
|
507
663
|
: `${buildGcReportText(report)}${report.disk ? buildGcDiskReportText(report.disk) : ""}`;
|
|
508
664
|
return { stdout, stderr: "", status };
|
|
509
665
|
}
|
|
@@ -513,7 +669,7 @@ export function gcHelpText(): string {
|
|
|
513
669
|
"gjc gc - garbage-collect stale GJC session/PID records",
|
|
514
670
|
"",
|
|
515
671
|
"USAGE",
|
|
516
|
-
" $ gjc gc [--prune|--force] [--disk] [--repair-session-index] [--json]",
|
|
672
|
+
" $ gjc gc [--prune|--force] [--disk] [--repair-session-index] [--empty-delete-receipts --root <dir>] [--json]",
|
|
517
673
|
|
|
518
674
|
"",
|
|
519
675
|
"FLAGS",
|
|
@@ -522,6 +678,13 @@ export function gcHelpText(): string {
|
|
|
522
678
|
" -j, --json Emit machine-readable JSON",
|
|
523
679
|
" --repair-session-index Explicitly quarantine a corrupt session-index suffix and retain its valid prefix",
|
|
524
680
|
" --disk Also report on-disk retention (sessions, blobs, artifacts, natives, backups)",
|
|
681
|
+
" --empty-delete-receipts Report/prune empty .gjc-delete-* under --root / --manifest",
|
|
682
|
+
" --root <dir> Operand root for --empty-delete-receipts (repeatable)",
|
|
683
|
+
' --manifest <file> JSON {"roots":[...]} for --empty-delete-receipts (repeatable)',
|
|
684
|
+
"",
|
|
685
|
+
" Operand values starting with '-' are rejected as missing operands; spell a",
|
|
686
|
+
" dash-prefixed path as ./-name. --root/--manifest without --empty-delete-receipts",
|
|
687
|
+
" is a usage error, and every supplied manifest is validated before any prune.",
|
|
525
688
|
"",
|
|
526
689
|
"Liveness-only: a record is removed only when its owning process is dead",
|
|
527
690
|
"(ESRCH). Live / permission-denied / unknown processes are always kept.",
|