fullcourtdefense-cli 1.27.0 → 1.27.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.
@@ -114,6 +114,14 @@ export interface HookIo {
114
114
  write(text: string): void;
115
115
  writeErr(text: string): void;
116
116
  exit(code: number): never;
117
+ /**
118
+ * True when `exit` really ends the process (standalone hook: CI runners,
119
+ * containers, a workstation whose daemon is down). Fire-and-forget network
120
+ * work started before `respond()` dies with the process, so audit reports
121
+ * must go through the spool + detached flusher instead. False in daemon
122
+ * mode, where `exit` only unwinds the evaluation and the process lives on.
123
+ */
124
+ exitsProcess: boolean;
117
125
  }
118
126
  /**
119
127
  * Daemon-side entry: evaluate one hook request in-process and return the
@@ -785,6 +785,7 @@ function processIo(stdin, stdinErr) {
785
785
  write: (text) => { process.stdout.write(text); },
786
786
  writeErr: (text) => { process.stderr.write(text); },
787
787
  exit: (code) => process.exit(code),
788
+ exitsProcess: true,
788
789
  };
789
790
  }
790
791
  /**
@@ -804,6 +805,7 @@ async function evaluateHookRequest(args, stdinRaw, config) {
804
805
  write: (text) => { out += text; },
805
806
  writeErr: (text) => { errOut += text; },
806
807
  exit: (code) => { throw new HookExitSignal(code); },
808
+ exitsProcess: false,
807
809
  };
808
810
  try {
809
811
  await hookCommandInner(args, config, io);
@@ -1195,7 +1197,7 @@ async function hookCommandInner(args, config, io) {
1195
1197
  await enforceActionPolicy({
1196
1198
  event, payload, apiUrl: apiUrl, shieldId: shieldId, shieldKey,
1197
1199
  shadow, failClosed, timeoutMs, approvalMode, approvalTimeoutMs, approvalPollMs, respond, deferLocalWarnings,
1198
- effectivePolicyHash, localPolicies, client,
1200
+ effectivePolicyHash, localPolicies, client, exitsProcess: io.exitsProcess,
1199
1201
  });
1200
1202
  return;
1201
1203
  }
@@ -1450,8 +1452,21 @@ async function enforceActionPolicy(ctx) {
1450
1452
  const localVerdict = (0, actionPolicyEngine_1.evaluateActionPolicies)(ctx.localPolicies, call.toolName, localOp, localCtx, (0, actionPolicyEngine_1.deriveToolOperations)(call.toolName));
1451
1453
  (0, hookIo_1.dbg)({ phase: 'policy_local_first', event, tool: call.toolName, operation: localOp, verdict: localVerdict.verdict, policyName: localVerdict.policyName });
1452
1454
  if (localVerdict.verdict === 'allow') {
1453
- // Audit rides async: server records the action + its own verdict; if it
1454
- // is unreachable the spool keeps the trail (idempotent on eventId).
1455
+ if (ctx.exitsProcess) {
1456
+ // Standalone hook (CI runner, container, daemon down): respond() ends
1457
+ // the process, so a fire-and-forget gate report would die in flight and
1458
+ // the allowed action would never reach the fleet — the machine would
1459
+ // read "N blocked of N recent" with every allow missing. Same delivery
1460
+ // as a block: spool (sync file append) + detached flusher that outlives
1461
+ // this process. The IDE is not kept waiting on the network.
1462
+ spoolCallEvent(call, { decision: 'allow', reason: 'local policy allow (block mode, local-first)' });
1463
+ (0, telemetry_1.triggerFlush)(true);
1464
+ respond(false);
1465
+ return;
1466
+ }
1467
+ // Daemon-evaluated: the process lives on, so the audit rides async — the
1468
+ // server records the action + its own verdict; if it is unreachable the
1469
+ // spool keeps the trail (idempotent on eventId).
1455
1470
  void fetch(gateUrl, { method: 'POST', headers, body: gateBody, signal: AbortSignal.timeout(4000) })
1456
1471
  .then(() => (0, policyGateHealth_1.recordGateSuccess)())
1457
1472
  .catch(() => {
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.27.0"
2
+ "version": "1.27.1"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.27.0",
3
+ "version": "1.27.1",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {