@skrr-ai/cli 0.1.129 → 0.1.131

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.
Files changed (46) hide show
  1. package/dist/base-command.js +30 -0
  2. package/dist/commands/agenda/trigger/create.js +9 -1
  3. package/dist/commands/agent/message/send.js +3 -0
  4. package/dist/commands/agents/reflect.d.ts +6 -0
  5. package/dist/commands/agents/reflect.js +45 -6
  6. package/dist/commands/agents/schedule-overview.d.ts +17 -0
  7. package/dist/commands/agents/schedule-overview.js +33 -9
  8. package/dist/commands/agents/schedule-trace.js +14 -5
  9. package/dist/commands/agents/triggers/policy.js +3 -1
  10. package/dist/commands/harnesses/prune.d.ts +1 -0
  11. package/dist/commands/harnesses/prune.js +24 -4
  12. package/dist/commands/northstars/create.js +6 -1
  13. package/dist/commands/northstars/list.js +1 -1
  14. package/dist/commands/northstars/show.js +1 -1
  15. package/dist/commands/spaces/heartbeats/status.js +10 -0
  16. package/dist/commands/tasks/archive.js +6 -0
  17. package/dist/commands/tasks/pause-schedule.js +1 -1
  18. package/dist/commands/tasks/resume-schedule.js +4 -1
  19. package/dist/commands/tasks/schedule.js +6 -1
  20. package/dist/commands/tasks/self-schedule.d.ts +56 -0
  21. package/dist/commands/tasks/self-schedule.js +179 -21
  22. package/dist/lib/exec-runtime-binary.js +3 -2
  23. package/dist/lib/harnesses.d.ts +4 -1
  24. package/dist/lib/harnesses.js +3 -0
  25. package/dist/lib/northstars.d.ts +44 -4
  26. package/dist/lib/northstars.js +108 -92
  27. package/dist/lib/reflection.d.ts +58 -0
  28. package/dist/lib/reflection.js +115 -6
  29. package/dist/lib/schedule.d.ts +2 -0
  30. package/dist/lib/schedule.js +12 -0
  31. package/dist/lib/task-operations.js +2 -0
  32. package/dist/lib/task-schedule-notes.d.ts +46 -0
  33. package/dist/lib/task-schedule-notes.js +71 -0
  34. package/dist/lib/triggers.d.ts +18 -0
  35. package/dist/lib/triggers.js +16 -6
  36. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +9 -0
  37. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +22 -0
  38. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  39. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +2 -0
  40. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +9 -0
  41. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +28 -0
  42. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  43. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
  44. package/dist/node_modules/@skrr-ai/data-provider/index.js +1602 -1578
  45. package/oclif.manifest.json +44 -28
  46. package/package.json +1 -1
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ /**
3
+ * What a schedule command did to the schedule, as sentences.
4
+ *
5
+ * The server already knows these things and reports them as response-only fields
6
+ * (`schedulePaused`, `scheduleChange`, `scheduleResume`, `scheduleElapsedWhileArchived`).
7
+ * The commands printed "Scheduled ..." / "Resumed ..." / "unarchived" regardless, so a
8
+ * person could not tell that a time was recorded but nothing was armed (OSK-21521), that
9
+ * a slot which fell inside their pause was skipped (OSK-21513), or that a time which
10
+ * passed while a Task was archived was dropped rather than run (OSK-21494).
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.scheduleResultLines = scheduleResultLines;
14
+ exports.resumeResultLines = resumeResultLines;
15
+ exports.unarchiveResultLines = unarchiveResultLines;
16
+ exports.archiveResultLines = archiveResultLines;
17
+ function iso(value) {
18
+ if (!value)
19
+ return null;
20
+ const at = value instanceof Date ? value : new Date(value);
21
+ return Number.isNaN(at.getTime()) ? String(value) : at.toISOString();
22
+ }
23
+ /**
24
+ * Lines to print under `Scheduled task ...`. A paused series is told as such, with the one
25
+ * command that un-pauses it, because `schedule` never does.
26
+ */
27
+ function scheduleResultLines(result, taskRef, bin = 'skrr') {
28
+ const lines = [];
29
+ if (result?.scheduleChange?.changed === false) {
30
+ lines.push('Already scheduled exactly like this; nothing changed.');
31
+ }
32
+ if (result?.schedulePaused) {
33
+ lines.push('The recurring series is PAUSED: this time is recorded, but nothing is armed and nothing will run.', `Resume it with \`${bin} tasks resume-schedule ${taskRef}\` (\`schedule\` never resumes a paused series).`);
34
+ }
35
+ return lines;
36
+ }
37
+ /** What resuming did to the slot that fell inside the pause, or nothing when none did. */
38
+ function resumeResultLines(result) {
39
+ const resume = result?.scheduleResume;
40
+ if (!resume)
41
+ return [];
42
+ const skipped = iso(resume.skippedScheduledFor);
43
+ const next = iso(resume.nextRunAt);
44
+ return [
45
+ `Skipped the run that came due while paused (${skipped}); it is not replayed.`,
46
+ next ? `Next run: ${next}` : 'The series has no further run.',
47
+ ];
48
+ }
49
+ /** What unarchiving did to a schedule that came due while the Task was archived. */
50
+ function unarchiveResultLines(result, taskRef, bin = 'skrr') {
51
+ const elapsed = result?.scheduleElapsedWhileArchived;
52
+ if (!elapsed)
53
+ return [];
54
+ const skipped = iso(elapsed.skippedScheduledFor);
55
+ const next = iso(elapsed.nextRunAt);
56
+ return [
57
+ `Its scheduled time (${skipped}) passed while it was archived, so it was skipped, not run.`,
58
+ next
59
+ ? `Next run: ${next}`
60
+ : `It is no longer scheduled; set a new time with \`${bin} tasks schedule ${taskRef} --at <time>\`.`,
61
+ ];
62
+ }
63
+ /** At archive time: a kept time that will not fire while archived. */
64
+ function archiveResultLines(result) {
65
+ const kept = iso(result?.scheduledFor);
66
+ return kept
67
+ ? [
68
+ `Its scheduled time (${kept}) is kept but will not fire while archived; a time that passes meanwhile is skipped on unarchive.`,
69
+ ]
70
+ : [];
71
+ }
@@ -143,6 +143,16 @@ export interface TriggerDefinition {
143
143
  recordedAt?: string;
144
144
  retiredAt?: string;
145
145
  };
146
+ /**
147
+ * Server-DERIVED and only present when the list was asked for it (`?include=autonomy`), and
148
+ * only on a self-reflection loop: whether its scheduled fires RUN (`runs`) or stop short of
149
+ * running because the agent's initiative mode applies to them (`suggests_only`, with
150
+ * `autonomyNote` saying why). It is the verdict `triggers preflight` prints at its initiative
151
+ * gate. A server that predates it, or a verdict it could not reach, sends neither field, which
152
+ * means UNKNOWN: a reader must never take an absent `autonomy` for `runs` (OSK-21606).
153
+ */
154
+ autonomy?: 'runs' | 'suggests_only';
155
+ autonomyNote?: string;
146
156
  createdAt?: string;
147
157
  updatedAt?: string;
148
158
  }
@@ -457,6 +467,14 @@ export declare function scopeLabel(trigger: TriggerDefinition): string;
457
467
  * `triggers show` said `Armed: yes` with no time (OSK-15416).
458
468
  */
459
469
  export declare function onceDueAt(trigger: TriggerDefinition): string | null;
470
+ /**
471
+ * When a trigger WILL fire next: its scheduled time, and only while it is enabled. A disabled
472
+ * trigger keeps the `schedule.nextRunAt` it had when it was paused, and nothing will ever run
473
+ * at that time — `agents reflect` and `agents schedule-overview` printed it as the next run of
474
+ * a paused loop while `agents upcoming` correctly listed nothing (OSK-21605). Null when
475
+ * nothing is scheduled; never the last fire.
476
+ */
477
+ export declare function scheduledRunAt(trigger: TriggerDefinition): string | null;
460
478
  export declare function nextRunLabel(trigger: TriggerDefinition): string;
461
479
  /**
462
480
  * Whether a trigger will actually fire — not merely whether someone left it on.
@@ -39,6 +39,7 @@ exports.agentRefEntity = agentRefEntity;
39
39
  exports.fetchAgentScopedTrigger = fetchAgentScopedTrigger;
40
40
  exports.scopeLabel = scopeLabel;
41
41
  exports.onceDueAt = onceDueAt;
42
+ exports.scheduledRunAt = scheduledRunAt;
42
43
  exports.nextRunLabel = nextRunLabel;
43
44
  exports.armedLabel = armedLabel;
44
45
  exports.degradedMarker = degradedMarker;
@@ -1078,12 +1079,21 @@ function onceDueAt(trigger) {
1078
1079
  const due = once.runAt ?? once.at;
1079
1080
  return due ? String(due) : null;
1080
1081
  }
1082
+ /**
1083
+ * When a trigger WILL fire next: its scheduled time, and only while it is enabled. A disabled
1084
+ * trigger keeps the `schedule.nextRunAt` it had when it was paused, and nothing will ever run
1085
+ * at that time — `agents reflect` and `agents schedule-overview` printed it as the next run of
1086
+ * a paused loop while `agents upcoming` correctly listed nothing (OSK-21605). Null when
1087
+ * nothing is scheduled; never the last fire.
1088
+ */
1089
+ function scheduledRunAt(trigger) {
1090
+ if (trigger.enabled === false)
1091
+ return null;
1092
+ const next = trigger.schedule?.nextRunAt ?? trigger.selfScheduled?.nextRunAt ?? onceDueAt(trigger);
1093
+ return next ? String(next) : null;
1094
+ }
1081
1095
  function nextRunLabel(trigger) {
1082
- const next = trigger.schedule?.nextRunAt ??
1083
- trigger.selfScheduled?.nextRunAt ??
1084
- onceDueAt(trigger) ??
1085
- trigger.audit?.lastFiredAt ??
1086
- '';
1096
+ const next = scheduledRunAt(trigger) ?? trigger.audit?.lastFiredAt ?? '';
1087
1097
  return next ? String(next) : '-';
1088
1098
  }
1089
1099
  /**
@@ -1438,7 +1448,7 @@ function renderTriggerSummary(trigger, log, options = {}) {
1438
1448
  const next = nextRunLabel(trigger);
1439
1449
  if (next !== '-') {
1440
1450
  // `nextRunLabel` falls back to the last fire when nothing is scheduled; say which it is.
1441
- const upcoming = Boolean(trigger.schedule?.nextRunAt ?? trigger.selfScheduled?.nextRunAt ?? onceDueAt(trigger));
1451
+ const upcoming = Boolean(scheduledRunAt(trigger));
1442
1452
  log(`${upcoming ? 'Next run:' : 'Last run:'}${next.length > 0 ? ` ${next}` : ''}`);
1443
1453
  }
1444
1454
  /*
@@ -1,5 +1,6 @@
1
1
  import { type DeviceKeyAlg, type DevicePublicJwk } from './deviceKey.js';
2
2
  import { type DeviceKeyCustody, type DeviceKeyHandle } from './deviceKeyHandle.js';
3
+ import { type ExecutorStopReceipt, type ExecutorStopReceiptBody } from './executorStopReceipt.js';
3
4
  import { type HardwareKeyCapability, type HardwareKeyRecord } from './deviceKeyHardware.js';
4
5
  /**
5
6
  * The `cred_envelope.read.decrypt_failed` hint for the device PRIVATE key
@@ -235,6 +236,14 @@ export declare function signServerRequest(opts: {
235
236
  refreshToken?: string;
236
237
  accessToken?: string;
237
238
  }): Promise<string | null>;
239
+ /**
240
+ * Sign an executor stop receipt (OSK-20777 Gate 1) with the device key: the signed proof that a
241
+ * scheduled session stopped, which the server verifies against the key pinned on this machine's row.
242
+ * Returns null when device identity is not active or the key cannot sign right now. NEVER throws: a
243
+ * failed sign emits a telemetry event and the caller sends the response without a receipt, which the
244
+ * server treats as an unproven stop rather than as a failure of the session.
245
+ */
246
+ export declare function signExecutorStopReceiptWithDeviceKey(body: ExecutorStopReceiptBody): Promise<ExecutorStopReceipt | null>;
238
247
  /** @internal Test seams — file path resolvers. */
239
248
  export declare function __publicKeyPathForTest(profile: string): string;
240
249
  export declare function __privateKeyPathForTest(profile: string): string;
@@ -51,6 +51,7 @@ __export(deviceIdentityBridge_exports, {
51
51
  persistPendingHardwareKey: () => persistPendingHardwareKey,
52
52
  prepareHardwareKeyCandidate: () => prepareHardwareKeyCandidate,
53
53
  resetDeviceIdentity: () => resetDeviceIdentity,
54
+ signExecutorStopReceiptWithDeviceKey: () => signExecutorStopReceiptWithDeviceKey,
54
55
  signServerRequest: () => signServerRequest,
55
56
  signWithCandidate: () => signWithCandidate
56
57
  });
@@ -60,6 +61,7 @@ var import_node_path = __toESM(require("node:path"), 1);
60
61
  var import_proper_lockfile = __toESM(require("proper-lockfile"), 1);
61
62
  var import_deviceKey = require("./deviceKey.js");
62
63
  var import_deviceKeyHandle = require("./deviceKeyHandle.js");
64
+ var import_executorStopReceipt = require("./executorStopReceipt.js");
63
65
  var import_deviceKeyHardware = require("./deviceKeyHardware.js");
64
66
  var import_credentialEnvelopeBridge = require("./credentialEnvelopeBridge.js");
65
67
  var import_profileStateDir = require("./profileStateDir.js");
@@ -827,6 +829,25 @@ async function signServerRequest(opts) {
827
829
  return null;
828
830
  }
829
831
  }
832
+ async function signExecutorStopReceiptWithDeviceKey(body) {
833
+ reloadIfRecovered();
834
+ const state = _state;
835
+ if (state.kind !== "active")
836
+ return null;
837
+ try {
838
+ return await (0, import_executorStopReceipt.signExecutorStopReceipt)(state.key, body);
839
+ } catch (err) {
840
+ emit("sign_failed", false, {
841
+ profile: state.profile,
842
+ thumbprint: state.key.thumbprint,
843
+ custody: state.key.custody,
844
+ method: "executor_stop_receipt",
845
+ ...err instanceof import_deviceKeyHardware.HardwareDeviceKeyError ? { hardwareFailure: err.code } : {},
846
+ message: err?.message ?? "unknown"
847
+ });
848
+ return null;
849
+ }
850
+ }
830
851
  function __publicKeyPathForTest(profile) {
831
852
  return publicKeyPath(profile);
832
853
  }
@@ -1040,6 +1061,7 @@ async function enrollWithServer(opts) {
1040
1061
  persistPendingHardwareKey,
1041
1062
  prepareHardwareKeyCandidate,
1042
1063
  resetDeviceIdentity,
1064
+ signExecutorStopReceiptWithDeviceKey,
1043
1065
  signServerRequest,
1044
1066
  signWithCandidate
1045
1067
  });
@@ -34,7 +34,7 @@ export { AUTH_SERVER_METADATA_PATH, readServerDeviceKeyAlgs, SERVER_DEVICE_KEY_A
34
34
  export { configureHardwareDeviceKeyHelper, HARDWARE_KEY_HELPER_NAME, HardwareDeviceKeyError, probeHardwareDeviceKey, resolveHardwareDeviceKeyHelper, type HardwareDeviceKeyErrorCode, type HardwareKeyCapability, } from './deviceKeyHardware.js';
35
35
  export { DPOP_HEADER, DPOP_MAX_AGE_SECONDS, DPOP_PROOF_INVALID, DPOP_PROOF_REQUIRED, DPOP_PROOF_TYP, DPOP_PROOF_VERIFY_UNAVAILABLE, accessTokenHash, createDpopKey, isAth, isJwkThumbprint, normalizeDpopHtu, verifyDpopProof, type DpopKey, type DpopProofClaims, type DpopProofRequest, type DpopVerifyFailureReason, type DpopVerifyOptions, type DpopVerifyResult, } from './dpop.js';
36
36
  export { initCredEnvelope, initExistingCredEnvelope, initNewIndependentCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, splitKeyConvergencePolicy, maybeEncryptForWrite, maybeDecryptOnRead, DEFAULT_UNREADABLE_CREDENTIAL_HINT, type DecryptOnReadOptions, __wrappedDekFilePathForTest, __setStateForTest as __setCredEnvelopeStateForTest, __getStateForTest as __getCredEnvelopeStateForTest, __resetShutdownGuardForTest, type ReadTransformResult, } from './credentialEnvelopeBridge.js';
37
- export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, prepareHardwareKeyCandidate, commitDeviceKeyCandidate, signWithCandidate, persistPendingHardwareKey, commitPendingHardwareKey, discardPendingHardwareKey, PENDING_HARDWARE_KEY_MAX_AGE_MS, type DeviceKeyCandidate, type DeviceKeyCandidateResult, type PendingHardwareKeyOutcome, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, describeDeviceKeySplit, describeStoredDeviceKey, DEVICE_KEY_UNREADABLE_CREDENTIAL_HINT, type StoredDeviceKeyDescription, __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setUnreadableReloadIntervalForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, type EnrollmentResult, } from './deviceIdentityBridge.js';
37
+ export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, signExecutorStopReceiptWithDeviceKey, prepareHardwareKeyCandidate, commitDeviceKeyCandidate, signWithCandidate, persistPendingHardwareKey, commitPendingHardwareKey, discardPendingHardwareKey, PENDING_HARDWARE_KEY_MAX_AGE_MS, type DeviceKeyCandidate, type DeviceKeyCandidateResult, type PendingHardwareKeyOutcome, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, describeDeviceKeySplit, describeStoredDeviceKey, DEVICE_KEY_UNREADABLE_CREDENTIAL_HINT, type StoredDeviceKeyDescription, __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setUnreadableReloadIntervalForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, type EnrollmentResult, } from './deviceIdentityBridge.js';
38
38
  export { getMachineUuid, getMachineUuidForProfile, describeMachineIdentity, type MachineIdentityDescription, type MachineIdentityReason, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
39
39
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
40
40
  export { PAIR_AAD_PREFIX, PAIR_AAD_MAX_LEN, PAIR_BUNDLE_MAX_BASE64_LEN, PAIR_NONCE_LEN, PAIR_PUBKEY_LEN, PAIR_SECRET_LEN, PairBundleError, assemblePairBundleBase64, base64ToUint8Array as pairBase64ToUint8Array, base64UrlToUint8Array as pairBase64UrlToUint8Array, buildPairAad, buildPairUrl, generatePairKeyPair, openPairBundleBase64, parsePairBundle, parsePairPlaintext, parsePairUrl, uint8ArrayToBase64 as pairUint8ArrayToBase64, uint8ArrayToBase64Url as pairUint8ArrayToBase64Url, type AssembleBundleOptions as PairAssembleBundleOptions, type OpenBundleOptions as PairOpenBundleOptions, type PairBundle, type PairBundleErrorCode, type PairKeyPair, type PairPlaintextV1, type PairUrlFields, } from './daemonPair.js';
@@ -316,6 +316,7 @@ __export(index_exports, {
316
316
  setMachineUuidProfile: () => import_machineUuid.setMachineUuidProfile,
317
317
  shutdownCredEnvelope: () => import_credentialEnvelopeBridge.shutdownCredEnvelope,
318
318
  signExecutorStopReceipt: () => import_executorStopReceipt.signExecutorStopReceipt,
319
+ signExecutorStopReceiptWithDeviceKey: () => import_deviceIdentityBridge.signExecutorStopReceiptWithDeviceKey,
319
320
  signProofWith: () => import_deviceKey.signProofWith,
320
321
  signServerRequest: () => import_deviceIdentityBridge.signServerRequest,
321
322
  signWithCandidate: () => import_deviceIdentityBridge.signWithCandidate,
@@ -686,6 +687,7 @@ var import_independentMachineReservation = require("./independentMachineReservat
686
687
  setMachineUuidProfile,
687
688
  shutdownCredEnvelope,
688
689
  signExecutorStopReceipt,
690
+ signExecutorStopReceiptWithDeviceKey,
689
691
  signProofWith,
690
692
  signServerRequest,
691
693
  signWithCandidate,
@@ -1,5 +1,6 @@
1
1
  import { type DeviceKeyAlg, type DevicePublicJwk } from './deviceKey.js';
2
2
  import { type DeviceKeyCustody, type DeviceKeyHandle } from './deviceKeyHandle.js';
3
+ import { type ExecutorStopReceipt, type ExecutorStopReceiptBody } from './executorStopReceipt.js';
3
4
  import { type HardwareKeyCapability, type HardwareKeyRecord } from './deviceKeyHardware.js';
4
5
  /**
5
6
  * The `cred_envelope.read.decrypt_failed` hint for the device PRIVATE key
@@ -235,6 +236,14 @@ export declare function signServerRequest(opts: {
235
236
  refreshToken?: string;
236
237
  accessToken?: string;
237
238
  }): Promise<string | null>;
239
+ /**
240
+ * Sign an executor stop receipt (OSK-20777 Gate 1) with the device key: the signed proof that a
241
+ * scheduled session stopped, which the server verifies against the key pinned on this machine's row.
242
+ * Returns null when device identity is not active or the key cannot sign right now. NEVER throws: a
243
+ * failed sign emits a telemetry event and the caller sends the response without a receipt, which the
244
+ * server treats as an unproven stop rather than as a failure of the session.
245
+ */
246
+ export declare function signExecutorStopReceiptWithDeviceKey(body: ExecutorStopReceiptBody): Promise<ExecutorStopReceipt | null>;
238
247
  /** @internal Test seams — file path resolvers. */
239
248
  export declare function __publicKeyPathForTest(profile: string): string;
240
249
  export declare function __privateKeyPathForTest(profile: string): string;
@@ -34,6 +34,7 @@ import path from 'node:path';
34
34
  import lockfile from 'proper-lockfile';
35
35
  import { generateDeviceKeyPair, isDevicePublicJwk, jwkThumbprint, signProofWith, } from './deviceKey.js';
36
36
  import { softwareEd25519Handle } from './deviceKeyHandle.js';
37
+ import { signExecutorStopReceipt, } from './executorStopReceipt.js';
37
38
  import { createHardwareDeviceKey, hardwareDeviceKeyHandle, parseHardwareKeyRecord, probeHardwareDeviceKey, resolveHardwareDeviceKeyHelper, serializeHardwareKeyRecord, HardwareDeviceKeyError, } from './deviceKeyHardware.js';
38
39
  import { hasCredEnvelopePathConflict, isCredEnvelopeActive, maybeDecryptOnRead, splitKeyConvergencePolicy, wrapIfActiveOrPassthrough, } from './credentialEnvelopeBridge.js';
39
40
  import { consolidateProfileState, profileStateFilePathsToClear, profileStateDir, resolveProfileStateDir, RETIRED_COPY_SUFFIX, } from './profileStateDir.js';
@@ -1133,6 +1134,33 @@ export async function signServerRequest(opts) {
1133
1134
  return null;
1134
1135
  }
1135
1136
  }
1137
+ /**
1138
+ * Sign an executor stop receipt (OSK-20777 Gate 1) with the device key: the signed proof that a
1139
+ * scheduled session stopped, which the server verifies against the key pinned on this machine's row.
1140
+ * Returns null when device identity is not active or the key cannot sign right now. NEVER throws: a
1141
+ * failed sign emits a telemetry event and the caller sends the response without a receipt, which the
1142
+ * server treats as an unproven stop rather than as a failure of the session.
1143
+ */
1144
+ export async function signExecutorStopReceiptWithDeviceKey(body) {
1145
+ reloadIfRecovered();
1146
+ const state = _state;
1147
+ if (state.kind !== 'active')
1148
+ return null;
1149
+ try {
1150
+ return await signExecutorStopReceipt(state.key, body);
1151
+ }
1152
+ catch (err) {
1153
+ emit('sign_failed', false, {
1154
+ profile: state.profile,
1155
+ thumbprint: state.key.thumbprint,
1156
+ custody: state.key.custody,
1157
+ method: 'executor_stop_receipt',
1158
+ ...(err instanceof HardwareDeviceKeyError ? { hardwareFailure: err.code } : {}),
1159
+ message: err?.message ?? 'unknown',
1160
+ });
1161
+ return null;
1162
+ }
1163
+ }
1136
1164
  /** @internal Test seams — file path resolvers. */
1137
1165
  export function __publicKeyPathForTest(profile) {
1138
1166
  return publicKeyPath(profile);
@@ -34,7 +34,7 @@ export { AUTH_SERVER_METADATA_PATH, readServerDeviceKeyAlgs, SERVER_DEVICE_KEY_A
34
34
  export { configureHardwareDeviceKeyHelper, HARDWARE_KEY_HELPER_NAME, HardwareDeviceKeyError, probeHardwareDeviceKey, resolveHardwareDeviceKeyHelper, type HardwareDeviceKeyErrorCode, type HardwareKeyCapability, } from './deviceKeyHardware.js';
35
35
  export { DPOP_HEADER, DPOP_MAX_AGE_SECONDS, DPOP_PROOF_INVALID, DPOP_PROOF_REQUIRED, DPOP_PROOF_TYP, DPOP_PROOF_VERIFY_UNAVAILABLE, accessTokenHash, createDpopKey, isAth, isJwkThumbprint, normalizeDpopHtu, verifyDpopProof, type DpopKey, type DpopProofClaims, type DpopProofRequest, type DpopVerifyFailureReason, type DpopVerifyOptions, type DpopVerifyResult, } from './dpop.js';
36
36
  export { initCredEnvelope, initExistingCredEnvelope, initNewIndependentCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, splitKeyConvergencePolicy, maybeEncryptForWrite, maybeDecryptOnRead, DEFAULT_UNREADABLE_CREDENTIAL_HINT, type DecryptOnReadOptions, __wrappedDekFilePathForTest, __setStateForTest as __setCredEnvelopeStateForTest, __getStateForTest as __getCredEnvelopeStateForTest, __resetShutdownGuardForTest, type ReadTransformResult, } from './credentialEnvelopeBridge.js';
37
- export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, prepareHardwareKeyCandidate, commitDeviceKeyCandidate, signWithCandidate, persistPendingHardwareKey, commitPendingHardwareKey, discardPendingHardwareKey, PENDING_HARDWARE_KEY_MAX_AGE_MS, type DeviceKeyCandidate, type DeviceKeyCandidateResult, type PendingHardwareKeyOutcome, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, describeDeviceKeySplit, describeStoredDeviceKey, DEVICE_KEY_UNREADABLE_CREDENTIAL_HINT, type StoredDeviceKeyDescription, __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setUnreadableReloadIntervalForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, type EnrollmentResult, } from './deviceIdentityBridge.js';
37
+ export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, signExecutorStopReceiptWithDeviceKey, prepareHardwareKeyCandidate, commitDeviceKeyCandidate, signWithCandidate, persistPendingHardwareKey, commitPendingHardwareKey, discardPendingHardwareKey, PENDING_HARDWARE_KEY_MAX_AGE_MS, type DeviceKeyCandidate, type DeviceKeyCandidateResult, type PendingHardwareKeyOutcome, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, describeDeviceKeySplit, describeStoredDeviceKey, DEVICE_KEY_UNREADABLE_CREDENTIAL_HINT, type StoredDeviceKeyDescription, __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setUnreadableReloadIntervalForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, type EnrollmentResult, } from './deviceIdentityBridge.js';
38
38
  export { getMachineUuid, getMachineUuidForProfile, describeMachineIdentity, type MachineIdentityDescription, type MachineIdentityReason, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
39
39
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
40
40
  export { PAIR_AAD_PREFIX, PAIR_AAD_MAX_LEN, PAIR_BUNDLE_MAX_BASE64_LEN, PAIR_NONCE_LEN, PAIR_PUBKEY_LEN, PAIR_SECRET_LEN, PairBundleError, assemblePairBundleBase64, base64ToUint8Array as pairBase64ToUint8Array, base64UrlToUint8Array as pairBase64UrlToUint8Array, buildPairAad, buildPairUrl, generatePairKeyPair, openPairBundleBase64, parsePairBundle, parsePairPlaintext, parsePairUrl, uint8ArrayToBase64 as pairUint8ArrayToBase64, uint8ArrayToBase64Url as pairUint8ArrayToBase64Url, type AssembleBundleOptions as PairAssembleBundleOptions, type OpenBundleOptions as PairOpenBundleOptions, type PairBundle, type PairBundleErrorCode, type PairKeyPair, type PairPlaintextV1, type PairUrlFields, } from './daemonPair.js';
@@ -131,7 +131,7 @@ initCredEnvelope, initExistingCredEnvelope, initNewIndependentCredEnvelope, rese
131
131
  __wrappedDekFilePathForTest, __setStateForTest as __setCredEnvelopeStateForTest, __getStateForTest as __getCredEnvelopeStateForTest, __resetShutdownGuardForTest, } from './credentialEnvelopeBridge.js';
132
132
  export {
133
133
  // deviceIdentity bridge
134
- initDeviceIdentity, resetDeviceIdentity, signServerRequest, prepareHardwareKeyCandidate, commitDeviceKeyCandidate, signWithCandidate, persistPendingHardwareKey, commitPendingHardwareKey, discardPendingHardwareKey, PENDING_HARDWARE_KEY_MAX_AGE_MS, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, describeDeviceKeySplit, describeStoredDeviceKey, DEVICE_KEY_UNREADABLE_CREDENTIAL_HINT,
134
+ initDeviceIdentity, resetDeviceIdentity, signServerRequest, signExecutorStopReceiptWithDeviceKey, prepareHardwareKeyCandidate, commitDeviceKeyCandidate, signWithCandidate, persistPendingHardwareKey, commitPendingHardwareKey, discardPendingHardwareKey, PENDING_HARDWARE_KEY_MAX_AGE_MS, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, describeDeviceKeySplit, describeStoredDeviceKey, DEVICE_KEY_UNREADABLE_CREDENTIAL_HINT,
135
135
  // Test seams — file path resolvers + state setters for spec files.
136
136
  __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setUnreadableReloadIntervalForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, } from './deviceIdentityBridge.js';
137
137
  // Hardware machine identity sent as `machineUuid` (IOPlatformUUID /