@skrr-ai/cli 0.1.108 → 0.1.110

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 (202) hide show
  1. package/README.md +10 -0
  2. package/dist/base-command.d.ts +74 -0
  3. package/dist/base-command.js +182 -24
  4. package/dist/commands/agents/triggers/create.js +1 -1
  5. package/dist/commands/agents/triggers/update.js +1 -1
  6. package/dist/commands/agents/update.d.ts +25 -0
  7. package/dist/commands/agents/update.js +98 -3
  8. package/dist/commands/commitments/accounting.js +7 -2
  9. package/dist/commands/commitments/cost.d.ts +11 -0
  10. package/dist/commands/commitments/cost.js +26 -3
  11. package/dist/commands/commitments/create.js +2 -1
  12. package/dist/commands/commitments/operating-review.d.ts +15 -0
  13. package/dist/commands/commitments/operating-review.js +103 -50
  14. package/dist/commands/commitments/update.js +12 -5
  15. package/dist/commands/daemon/install.js +17 -1
  16. package/dist/commands/daemon/login.js +14 -0
  17. package/dist/commands/doctor.js +9 -0
  18. package/dist/commands/goals/list.js +6 -1
  19. package/dist/commands/grants/rotate.js +4 -0
  20. package/dist/commands/harnesses/prune.d.ts +26 -0
  21. package/dist/commands/harnesses/prune.js +172 -0
  22. package/dist/commands/login.js +53 -14
  23. package/dist/commands/machines/list.js +28 -6
  24. package/dist/commands/projects/show.js +1 -1
  25. package/dist/commands/revoke-daemon.js +2 -1
  26. package/dist/commands/spaces/workflows/archive.d.ts +1 -0
  27. package/dist/commands/spaces/workflows/archive.js +1 -0
  28. package/dist/commands/spaces/workflows/artifact.d.ts +1 -0
  29. package/dist/commands/spaces/workflows/artifact.js +1 -0
  30. package/dist/commands/spaces/workflows/cancel-node.d.ts +1 -0
  31. package/dist/commands/spaces/workflows/cancel-node.js +1 -0
  32. package/dist/commands/spaces/workflows/cancel.d.ts +1 -0
  33. package/dist/commands/spaces/workflows/cancel.js +6 -2
  34. package/dist/commands/spaces/workflows/create.d.ts +1 -0
  35. package/dist/commands/spaces/workflows/create.js +7 -1
  36. package/dist/commands/spaces/workflows/events.d.ts +1 -0
  37. package/dist/commands/spaces/workflows/events.js +1 -0
  38. package/dist/commands/spaces/workflows/fixtures/create.d.ts +1 -0
  39. package/dist/commands/spaces/workflows/fixtures/create.js +1 -0
  40. package/dist/commands/spaces/workflows/fixtures/list.d.ts +1 -0
  41. package/dist/commands/spaces/workflows/fixtures/list.js +1 -0
  42. package/dist/commands/spaces/workflows/lifecycle-diagnostics.d.ts +1 -0
  43. package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +1 -0
  44. package/dist/commands/spaces/workflows/list.d.ts +1 -0
  45. package/dist/commands/spaces/workflows/list.js +1 -0
  46. package/dist/commands/spaces/workflows/node-types.d.ts +2 -0
  47. package/dist/commands/spaces/workflows/node-types.js +24 -6
  48. package/dist/commands/spaces/workflows/pause.d.ts +1 -0
  49. package/dist/commands/spaces/workflows/pause.js +1 -0
  50. package/dist/commands/spaces/workflows/plan/show.d.ts +1 -0
  51. package/dist/commands/spaces/workflows/plan/show.js +1 -0
  52. package/dist/commands/spaces/workflows/plan/update.d.ts +1 -0
  53. package/dist/commands/spaces/workflows/plan/update.js +1 -0
  54. package/dist/commands/spaces/workflows/proposals/apply.d.ts +1 -0
  55. package/dist/commands/spaces/workflows/proposals/apply.js +1 -0
  56. package/dist/commands/spaces/workflows/proposals/confirm.d.ts +1 -0
  57. package/dist/commands/spaces/workflows/proposals/confirm.js +1 -0
  58. package/dist/commands/spaces/workflows/proposals/create.d.ts +1 -0
  59. package/dist/commands/spaces/workflows/proposals/create.js +1 -0
  60. package/dist/commands/spaces/workflows/proposals/dismiss.d.ts +1 -0
  61. package/dist/commands/spaces/workflows/proposals/dismiss.js +1 -0
  62. package/dist/commands/spaces/workflows/proposals/list.d.ts +1 -0
  63. package/dist/commands/spaces/workflows/proposals/list.js +1 -0
  64. package/dist/commands/spaces/workflows/publish.d.ts +1 -0
  65. package/dist/commands/spaces/workflows/publish.js +9 -0
  66. package/dist/commands/spaces/workflows/reconcile.d.ts +1 -0
  67. package/dist/commands/spaces/workflows/reconcile.js +1 -0
  68. package/dist/commands/spaces/workflows/resume.d.ts +1 -0
  69. package/dist/commands/spaces/workflows/resume.js +1 -0
  70. package/dist/commands/spaces/workflows/retry.d.ts +1 -0
  71. package/dist/commands/spaces/workflows/retry.js +1 -0
  72. package/dist/commands/spaces/workflows/run.d.ts +1 -0
  73. package/dist/commands/spaces/workflows/run.js +14 -7
  74. package/dist/commands/spaces/workflows/runs.d.ts +1 -0
  75. package/dist/commands/spaces/workflows/runs.js +1 -0
  76. package/dist/commands/spaces/workflows/show.d.ts +25 -0
  77. package/dist/commands/spaces/workflows/show.js +42 -4
  78. package/dist/commands/spaces/workflows/signal.d.ts +12 -0
  79. package/dist/commands/spaces/workflows/signal.js +40 -2
  80. package/dist/commands/spaces/workflows/start.d.ts +1 -0
  81. package/dist/commands/spaces/workflows/start.js +4 -1
  82. package/dist/commands/spaces/workflows/task-execution/create.d.ts +1 -0
  83. package/dist/commands/spaces/workflows/task-execution/create.js +1 -0
  84. package/dist/commands/spaces/workflows/test.d.ts +1 -0
  85. package/dist/commands/spaces/workflows/test.js +1 -0
  86. package/dist/commands/spaces/workflows/triggers/create.d.ts +1 -0
  87. package/dist/commands/spaces/workflows/triggers/create.js +1 -0
  88. package/dist/commands/spaces/workflows/triggers/fire.d.ts +1 -0
  89. package/dist/commands/spaces/workflows/triggers/fire.js +1 -0
  90. package/dist/commands/spaces/workflows/triggers/list.d.ts +1 -0
  91. package/dist/commands/spaces/workflows/triggers/list.js +1 -0
  92. package/dist/commands/spaces/workflows/triggers/update.d.ts +1 -0
  93. package/dist/commands/spaces/workflows/triggers/update.js +1 -0
  94. package/dist/commands/spaces/workflows/update.d.ts +1 -0
  95. package/dist/commands/spaces/workflows/update.js +4 -0
  96. package/dist/commands/spaces/workflows/validate.d.ts +1 -0
  97. package/dist/commands/spaces/workflows/validate.js +27 -2
  98. package/dist/commands/tasks/activity.js +17 -3
  99. package/dist/commands/tasks/bulk-update.d.ts +1 -0
  100. package/dist/commands/tasks/bulk-update.js +7 -0
  101. package/dist/commands/tasks/complete.js +2 -1
  102. package/dist/commands/tasks/create-tree.d.ts +69 -0
  103. package/dist/commands/tasks/create-tree.js +413 -47
  104. package/dist/commands/tasks/list.d.ts +2 -0
  105. package/dist/commands/tasks/list.js +25 -3
  106. package/dist/commands/tasks/rearm-recurrence.d.ts +22 -0
  107. package/dist/commands/tasks/rearm-recurrence.js +87 -0
  108. package/dist/commands/tasks/show.d.ts +9 -0
  109. package/dist/commands/tasks/show.js +79 -33
  110. package/dist/commands/tasks/timeline.js +4 -0
  111. package/dist/commands/tasks/unschedule.js +5 -1
  112. package/dist/commands/tasks/update.d.ts +1 -0
  113. package/dist/commands/tasks/update.js +17 -2
  114. package/dist/commands/triggers/audit.js +12 -3
  115. package/dist/commands/triggers/create.js +9 -3
  116. package/dist/commands/triggers/fire-detail.d.ts +7 -0
  117. package/dist/commands/triggers/fire-detail.js +30 -12
  118. package/dist/commands/triggers/fire.js +13 -3
  119. package/dist/commands/triggers/parse-intent.d.ts +7 -0
  120. package/dist/commands/triggers/parse-intent.js +31 -9
  121. package/dist/commands/triggers/update.js +1 -1
  122. package/dist/commands/workspaces/grants.js +20 -3
  123. package/dist/commands/workspaces/members.d.ts +20 -0
  124. package/dist/commands/workspaces/members.js +64 -0
  125. package/dist/commands/workspaces/stewardship.js +14 -2
  126. package/dist/help.js +12 -0
  127. package/dist/hooks/command-not-found.js +27 -1
  128. package/dist/lib/command-miss.d.ts +52 -2
  129. package/dist/lib/command-miss.js +187 -18
  130. package/dist/lib/commitment-loop-status.d.ts +8 -0
  131. package/dist/lib/commitment-loop-status.js +45 -0
  132. package/dist/lib/commitment-product.js +5 -2
  133. package/dist/lib/commitments.d.ts +18 -3
  134. package/dist/lib/commitments.js +85 -45
  135. package/dist/lib/daemon-command-hint.d.ts +15 -0
  136. package/dist/lib/daemon-command-hint.js +21 -0
  137. package/dist/lib/daemon-machine-identity-copy.d.ts +14 -0
  138. package/dist/lib/daemon-machine-identity-copy.js +27 -0
  139. package/dist/lib/daemon-machine-identity.d.ts +27 -0
  140. package/dist/lib/daemon-machine-identity.js +61 -0
  141. package/dist/lib/daemon-setup.js +10 -1
  142. package/dist/lib/daemonBrokerRefusal.js +3 -2
  143. package/dist/lib/daemonHandoff.d.ts +19 -1
  144. package/dist/lib/daemonHandoff.js +61 -1
  145. package/dist/lib/deployed-build.d.ts +43 -0
  146. package/dist/lib/deployed-build.js +66 -0
  147. package/dist/lib/grants.d.ts +27 -1
  148. package/dist/lib/grants.js +39 -21
  149. package/dist/lib/harnesses.d.ts +71 -0
  150. package/dist/lib/harnesses.js +70 -0
  151. package/dist/lib/json-mode.d.ts +11 -0
  152. package/dist/lib/json-mode.js +19 -0
  153. package/dist/lib/json-output.d.ts +25 -0
  154. package/dist/lib/json-output.js +30 -0
  155. package/dist/lib/list-output.d.ts +10 -1
  156. package/dist/lib/list-output.js +22 -1
  157. package/dist/lib/local-daemon-readiness.d.ts +90 -0
  158. package/dist/lib/local-daemon-readiness.js +143 -0
  159. package/dist/lib/machines.d.ts +117 -0
  160. package/dist/lib/machines.js +139 -0
  161. package/dist/lib/managed-instruction-local.d.ts +2 -0
  162. package/dist/lib/managed-instruction-local.js +2 -0
  163. package/dist/lib/profile-machine-identity-guard.d.ts +18 -0
  164. package/dist/lib/profile-machine-identity-guard.js +39 -0
  165. package/dist/lib/request-field-flags.d.ts +20 -0
  166. package/dist/lib/request-field-flags.js +34 -0
  167. package/dist/lib/stewardship-output.d.ts +1 -1
  168. package/dist/lib/stewardship-output.js +26 -30
  169. package/dist/lib/task-batch.d.ts +61 -4
  170. package/dist/lib/task-batch.js +316 -80
  171. package/dist/lib/task-economics.d.ts +15 -0
  172. package/dist/lib/task-economics.js +39 -2
  173. package/dist/lib/task-recurrence-outcome.d.ts +6 -6
  174. package/dist/lib/task-recurrence-outcome.js +27 -3
  175. package/dist/lib/tasks.d.ts +39 -0
  176. package/dist/lib/tasks.js +132 -0
  177. package/dist/lib/triggers.d.ts +42 -1
  178. package/dist/lib/triggers.js +98 -2
  179. package/dist/lib/workflow-issues.d.ts +14 -0
  180. package/dist/lib/workflow-issues.js +32 -0
  181. package/dist/lib/workflow-run-view.d.ts +59 -0
  182. package/dist/lib/workflow-run-view.js +110 -0
  183. package/dist/lib/workspaces.d.ts +48 -4
  184. package/dist/lib/workspaces.js +55 -0
  185. package/dist/lib/write-target.d.ts +29 -0
  186. package/dist/lib/write-target.js +72 -0
  187. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  188. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +5 -3
  189. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.d.ts +60 -0
  190. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +130 -6
  191. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/workspaceIsolationProbeWire.d.ts +51 -0
  192. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/workspaceIsolationProbeWire.js +50 -0
  193. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  194. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
  195. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.d.ts +60 -0
  196. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +128 -6
  197. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/workspaceIsolationProbeWire.d.ts +51 -0
  198. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/workspaceIsolationProbeWire.js +46 -0
  199. package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
  200. package/dist/node_modules/@skrr-ai/data-provider/index.js +7523 -7140
  201. package/oclif.manifest.json +473 -59
  202. package/package.json +28 -1
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ /**
3
+ * The workspace-isolation probe wire contract (OSK-15099).
4
+ *
5
+ * A Commitment Run is always isolated: the daemon cuts it a private git
6
+ * worktree from the directory the Run is rooted in, and refuses the Run with
7
+ * `EXECUTION_ISOLATION_UNAVAILABLE` before any tool when it cannot. Preflight
8
+ * had no way to read that machine's filesystem, so it reported `ready`,
9
+ * activation succeeded, and the first Run failed. The daemon now answers the
10
+ * same question BEFORE a Run, through the same predicate its executor refuses
11
+ * on, and this module is the one place both ends spell it.
12
+ *
13
+ * - The daemon announces {@link WORKSPACE_ISOLATION_PROBE_CAPABILITY}. A daemon
14
+ * that does not is never asked, and the server reports the prerequisite as
15
+ * UNVERIFIED — never as satisfied (unknown is never ok).
16
+ * - The server sends {@link WORKSPACE_ISOLATION_PROBE_METHOD} as a control RPC
17
+ * with `{ workingDirectory? }`: exactly the directory it would put on the
18
+ * Run's request (`agent.workingDirectory || daemon cwd`).
19
+ * - The daemon answers {@link WorkspaceIsolationProbeAnswer}.
20
+ *
21
+ * Pure: no I/O, safe for the browser, the CLI and the API.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.REFUSING_WORKTREE_BASE_VERDICTS = exports.WORKTREE_BASE_VERDICTS = exports.WORKSPACE_ISOLATION_PROBE_METHOD = exports.WORKSPACE_ISOLATION_PROBE_CAPABILITY = void 0;
25
+ exports.isWorktreeBaseVerdict = isWorktreeBaseVerdict;
26
+ exports.WORKSPACE_ISOLATION_PROBE_CAPABILITY = 'workspace_isolation_probe_v1';
27
+ exports.WORKSPACE_ISOLATION_PROBE_METHOD = 'daemon:workspace:isolation-probe';
28
+ /**
29
+ * What an explicit isolation request can be given from a base directory.
30
+ *
31
+ * - `worktree` — a git repository with a commit: a worktree is cut;
32
+ * - `sandbox` — the daemon's own bare scratch workspace: an empty
33
+ * private sandbox is used instead;
34
+ * - `not_a_repository` — refused: not inside a git repository;
35
+ * - `no_initial_commit` — refused: a repository with an unborn HEAD.
36
+ */
37
+ exports.WORKTREE_BASE_VERDICTS = Object.freeze([
38
+ 'worktree',
39
+ 'sandbox',
40
+ 'not_a_repository',
41
+ 'no_initial_commit',
42
+ ]);
43
+ /** The verdicts on which the daemon refuses an isolated Run before any tool. */
44
+ exports.REFUSING_WORKTREE_BASE_VERDICTS = new Set([
45
+ 'not_a_repository',
46
+ 'no_initial_commit',
47
+ ]);
48
+ function isWorktreeBaseVerdict(value) {
49
+ return typeof value === 'string' && exports.WORKTREE_BASE_VERDICTS.includes(value);
50
+ }
@@ -30,7 +30,7 @@ export { buildProof as buildDeviceProof, generateDeviceKeyPair, jwkThumbprint, n
30
30
  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';
31
31
  export { initCredEnvelope, 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';
32
32
  export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, describeDeviceKeySplit, __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, type EnrollmentResult, } from './deviceIdentityBridge.js';
33
- export { getMachineUuid, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
33
+ export { getMachineUuid, getMachineUuidForProfile, describeMachineIdentity, type MachineIdentityDescription, type MachineIdentityReason, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
34
34
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
35
35
  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';
36
36
  export { HARNESS_TIERS, getHarnessTier, credentialPolicyForTier, tierPermitsConfiguredAutoMode, type HarnessTrustTier, type CredentialDeliveryPolicy, type CredentialMode, } from './harnessTrust.js';
@@ -125,7 +125,7 @@ __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest,
125
125
  // Hardware machine identity sent as `machineUuid` (IOPlatformUUID /
126
126
  // machine-id / MachineGuid). ONE implementation for daemon and CLI —
127
127
  // OSK-12063. Also exposed dependency-free as `@skrr-ai/auth-core/machine-uuid`.
128
- export { getMachineUuid, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
128
+ export { getMachineUuid, getMachineUuidForProfile, describeMachineIdentity, getHardwareMachineUuid, setMachineUuidProfile, getMachineUuidProfile, qualifyMachineUuid, MACHINE_IDENTITY_FILENAME, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
129
129
  // Stable per-machine UUIDv7 identity persisted at <configDir>/daemon.id.
130
130
  // NOT a `machineUuid` — see machineUuid.ts.
131
131
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
@@ -56,5 +56,65 @@ export declare function getMachineUuidProfile(): string | null;
56
56
  * profile that already held credentials; see the block above.
57
57
  */
58
58
  export declare function getMachineUuid(): string;
59
+ /**
60
+ * The `machineUuid` a process running as `profile` sends, WITHOUT changing which
61
+ * profile this process runs as.
62
+ *
63
+ * The CLI needs the answer for the profile it is about to hand a credential to,
64
+ * and asks the daemon binary what IT will send (`accept-handoff --print-id`).
65
+ * Two programs that compute one identity can only be known to agree by
66
+ * comparing them, and a daemon that predates per-profile identities does not
67
+ * agree: it sends the hardware value whatever the profile's
68
+ * `machine-identity.json` says, so a credential minted for what it reports
69
+ * registers the profile on the default machine's row (the OSK-13845 failure, now
70
+ * reached through version skew rather than through the rule). The comparison
71
+ * needs this: the expected value for the TARGET profile, which is not
72
+ * necessarily the one this process is running as.
73
+ *
74
+ * Decides and persists the profile's identity if nothing has yet — the same
75
+ * side effect `getMachineUuid()` has, for the same reason.
76
+ */
77
+ export declare function getMachineUuidForProfile(profile: string | null | undefined): string;
78
+ export type MachineIdentityReason =
79
+ /** The default profile, which always uses this computer's own identity. */
80
+ 'default_profile'
81
+ /** A hosted runtime, whose identity the control plane issued. */
82
+ | 'hosted_runtime'
83
+ /** A profile that held no credentials when its identity was first decided. */
84
+ | 'new_profile'
85
+ /** A profile that already held credentials, so it keeps the shared identity. */
86
+ | 'pre_existing_profile'
87
+ /** Recovered from the copy kept outside the profile after product state was removed. */
88
+ | 'restored_profile'
89
+ /** The profile's record could not be read (the identity is still the one in use). */
90
+ | 'unrecorded';
91
+ export interface MachineIdentityDescription {
92
+ /** The profile this process runs as; null for the default profile. */
93
+ profile: string | null;
94
+ /** The value sent to the server as `machineUuid`. */
95
+ machineUuid: string;
96
+ /** This computer's own identity. */
97
+ hardware: string;
98
+ /**
99
+ * A non-default profile that registers on the SAME machine row as the default
100
+ * profile: they share one Daemon row, one device-key pin and one machine page.
101
+ */
102
+ sharesDefaultMachine: boolean;
103
+ reason: MachineIdentityReason;
104
+ /** The files that made a profile "already hold credentials", when recorded. */
105
+ evidence: string[];
106
+ decidedAt: string | null;
107
+ /** One sentence for a person. Written once, here, so no surface words it differently. */
108
+ sentence: string;
109
+ }
110
+ /**
111
+ * Which machine this process registers as, and WHY — the question a person has
112
+ * when two profiles turn up on one machine page and the logs say nothing.
113
+ *
114
+ * A read, not a decision: it reports the identity `getMachineUuid()` settled on
115
+ * (settling it if nothing has, exactly as any caller would) and the record that
116
+ * explains it. It never changes what a profile registers as.
117
+ */
118
+ export declare function describeMachineIdentity(): MachineIdentityDescription;
59
119
  /** @internal test seam — forget the cached value so the next call re-probes. */
60
120
  export declare function __resetMachineUuidCacheForTest(): void;
@@ -218,7 +218,7 @@ function readPersistedChoice(file, hardware, profile, requireProfile = false) {
218
218
  return null;
219
219
  }
220
220
  }
221
- function persistChoice(dir, file, hardware, machineUuid, reason, profile) {
221
+ function persistChoice(dir, file, hardware, machineUuid, reason, profile, evidence) {
222
222
  try {
223
223
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
224
224
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
@@ -228,6 +228,12 @@ function persistChoice(dir, file, hardware, machineUuid, reason, profile) {
228
228
  machineUuid,
229
229
  profile,
230
230
  reason,
231
+ // WHICH file made a profile "already hold credentials". The reason alone
232
+ // says a profile shares the default's machine row; this says what put it
233
+ // there, which is the difference between "an older Skrr set it up" and
234
+ // "its first credential was written before anything chose its identity".
235
+ // Additive: a reader that predates it ignores the field.
236
+ ...(evidence && evidence.length > 0 ? { evidence: [...evidence] } : {}),
231
237
  decidedAt: new Date().toISOString(),
232
238
  };
233
239
  fs.writeFileSync(tmp, `${JSON.stringify(body)}\n`, { mode: 0o600 });
@@ -267,7 +273,7 @@ function resolveProfileMachineUuid(profile, hardware) {
267
273
  persistChoice(dir, file, hardware, chosen, 'restored_profile', profile);
268
274
  }
269
275
  if (!chosen) {
270
- const preExisting = PRE_EXISTING_PROFILE_EVIDENCE.some((name) => {
276
+ const evidence = PRE_EXISTING_PROFILE_EVIDENCE.filter((name) => {
271
277
  try {
272
278
  return fs.existsSync(path.join(dir, name));
273
279
  }
@@ -275,8 +281,9 @@ function resolveProfileMachineUuid(profile, hardware) {
275
281
  return false;
276
282
  }
277
283
  });
284
+ const preExisting = evidence.length > 0;
278
285
  chosen = preExisting ? hardware : qualifyMachineUuid(hardware, profile);
279
- persistChoice(dir, file, hardware, chosen, preExisting ? 'pre_existing_profile' : 'new_profile', profile);
286
+ persistChoice(dir, file, hardware, chosen, preExisting ? 'pre_existing_profile' : 'new_profile', profile, evidence);
280
287
  }
281
288
  preserveRecoveryChoice(profile, hardware, chosen);
282
289
  profileChoiceCache.set(cacheKey, chosen);
@@ -296,14 +303,129 @@ function resolveProfileMachineUuid(profile, hardware) {
296
303
  * profile that already held credentials; see the block above.
297
304
  */
298
305
  export function getMachineUuid() {
306
+ return getMachineUuidForProfile(activeMachineProfile);
307
+ }
308
+ /**
309
+ * The `machineUuid` a process running as `profile` sends, WITHOUT changing which
310
+ * profile this process runs as.
311
+ *
312
+ * The CLI needs the answer for the profile it is about to hand a credential to,
313
+ * and asks the daemon binary what IT will send (`accept-handoff --print-id`).
314
+ * Two programs that compute one identity can only be known to agree by
315
+ * comparing them, and a daemon that predates per-profile identities does not
316
+ * agree: it sends the hardware value whatever the profile's
317
+ * `machine-identity.json` says, so a credential minted for what it reports
318
+ * registers the profile on the default machine's row (the OSK-13845 failure, now
319
+ * reached through version skew rather than through the rule). The comparison
320
+ * needs this: the expected value for the TARGET profile, which is not
321
+ * necessarily the one this process is running as.
322
+ *
323
+ * Decides and persists the profile's identity if nothing has yet — the same
324
+ * side effect `getMachineUuid()` has, for the same reason.
325
+ */
326
+ export function getMachineUuidForProfile(profile) {
299
327
  const hardware = getHardwareMachineUuid();
300
328
  // A hosted runtime's identity is ISSUED and fenced by the control plane.
301
329
  if (hostedRuntimeIdOverride('OVERSKY_MACHINE_UUID'))
302
330
  return hardware;
303
- const profile = activeMachineProfile;
304
- if (!profile || profile === DEFAULT_PROFILE_NAME)
331
+ const name = typeof profile === 'string' ? profile.trim() : '';
332
+ if (!name || name === DEFAULT_PROFILE_NAME)
305
333
  return hardware;
306
- return resolveProfileMachineUuid(profile, hardware);
334
+ return resolveProfileMachineUuid(name, hardware);
335
+ }
336
+ function readRecordedChoice(profile) {
337
+ try {
338
+ const file = path.join(resolveConfigRoot(), 'profiles', profile, MACHINE_IDENTITY_FILENAME);
339
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
340
+ const reason = parsed.reason === 'new_profile' ||
341
+ parsed.reason === 'pre_existing_profile' ||
342
+ parsed.reason === 'restored_profile'
343
+ ? parsed.reason
344
+ : null;
345
+ return {
346
+ reason,
347
+ evidence: Array.isArray(parsed.evidence)
348
+ ? parsed.evidence.filter((e) => typeof e === 'string')
349
+ : [],
350
+ decidedAt: typeof parsed.decidedAt === 'string' ? parsed.decidedAt : null,
351
+ };
352
+ }
353
+ catch {
354
+ return { reason: null, evidence: [], decidedAt: null };
355
+ }
356
+ }
357
+ /**
358
+ * Which machine this process registers as, and WHY — the question a person has
359
+ * when two profiles turn up on one machine page and the logs say nothing.
360
+ *
361
+ * A read, not a decision: it reports the identity `getMachineUuid()` settled on
362
+ * (settling it if nothing has, exactly as any caller would) and the record that
363
+ * explains it. It never changes what a profile registers as.
364
+ */
365
+ export function describeMachineIdentity() {
366
+ const hardware = getHardwareMachineUuid();
367
+ const machineUuid = getMachineUuid();
368
+ const active = activeMachineProfile;
369
+ const profile = active && active !== DEFAULT_PROFILE_NAME ? active : null;
370
+ const base = { profile, machineUuid, hardware };
371
+ if (hostedRuntimeIdOverride('OVERSKY_MACHINE_UUID')) {
372
+ return {
373
+ ...base,
374
+ sharesDefaultMachine: false,
375
+ reason: 'hosted_runtime',
376
+ evidence: [],
377
+ decidedAt: null,
378
+ sentence: 'This runtime is hosted; its machine identity was issued by the control plane.',
379
+ };
380
+ }
381
+ if (!profile) {
382
+ return {
383
+ ...base,
384
+ sharesDefaultMachine: false,
385
+ reason: 'default_profile',
386
+ evidence: [],
387
+ decidedAt: null,
388
+ sentence: "The default profile uses this computer's own machine identity.",
389
+ };
390
+ }
391
+ const recorded = readRecordedChoice(profile);
392
+ const sharesDefaultMachine = machineUuid === hardware;
393
+ const reason = recorded.reason ?? 'unrecorded';
394
+ const decided = recorded.decidedAt ? ` (decided ${recorded.decidedAt})` : '';
395
+ const held = recorded.evidence.length > 0 ? ` (${recorded.evidence.join(', ')})` : '';
396
+ let sentence;
397
+ if (reason === 'new_profile') {
398
+ sentence =
399
+ `Profile '${profile}' has its own machine identity: it held no credentials when that ` +
400
+ `identity was first chosen${decided}.`;
401
+ }
402
+ else if (reason === 'pre_existing_profile') {
403
+ sentence =
404
+ `Profile '${profile}' shares this computer's default machine identity: it already held ` +
405
+ `credentials${held} when its identity was first chosen${decided}, and the server ties ` +
406
+ 'those credentials to that identity, so changing it would sign the profile out.';
407
+ }
408
+ else if (reason === 'restored_profile') {
409
+ sentence = sharesDefaultMachine
410
+ ? `Profile '${profile}' shares this computer's default machine identity, restored from the ` +
411
+ `copy kept outside the profile after its state was removed${decided}.`
412
+ : `Profile '${profile}' has its own machine identity, restored from the copy kept outside ` +
413
+ `the profile after its state was removed${decided}.`;
414
+ }
415
+ else {
416
+ sentence = sharesDefaultMachine
417
+ ? `Profile '${profile}' uses this computer's default machine identity; the record of why ` +
418
+ 'could not be read.'
419
+ : `Profile '${profile}' has its own machine identity; the record of why could not be read.`;
420
+ }
421
+ return {
422
+ ...base,
423
+ sharesDefaultMachine,
424
+ reason,
425
+ evidence: recorded.evidence,
426
+ decidedAt: recorded.decidedAt,
427
+ sentence,
428
+ };
307
429
  }
308
430
  /** @internal test seam — forget the cached value so the next call re-probes. */
309
431
  export function __resetMachineUuidCacheForTest() {
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The workspace-isolation probe wire contract (OSK-15099).
3
+ *
4
+ * A Commitment Run is always isolated: the daemon cuts it a private git
5
+ * worktree from the directory the Run is rooted in, and refuses the Run with
6
+ * `EXECUTION_ISOLATION_UNAVAILABLE` before any tool when it cannot. Preflight
7
+ * had no way to read that machine's filesystem, so it reported `ready`,
8
+ * activation succeeded, and the first Run failed. The daemon now answers the
9
+ * same question BEFORE a Run, through the same predicate its executor refuses
10
+ * on, and this module is the one place both ends spell it.
11
+ *
12
+ * - The daemon announces {@link WORKSPACE_ISOLATION_PROBE_CAPABILITY}. A daemon
13
+ * that does not is never asked, and the server reports the prerequisite as
14
+ * UNVERIFIED — never as satisfied (unknown is never ok).
15
+ * - The server sends {@link WORKSPACE_ISOLATION_PROBE_METHOD} as a control RPC
16
+ * with `{ workingDirectory? }`: exactly the directory it would put on the
17
+ * Run's request (`agent.workingDirectory || daemon cwd`).
18
+ * - The daemon answers {@link WorkspaceIsolationProbeAnswer}.
19
+ *
20
+ * Pure: no I/O, safe for the browser, the CLI and the API.
21
+ */
22
+ export declare const WORKSPACE_ISOLATION_PROBE_CAPABILITY: "workspace_isolation_probe_v1";
23
+ export declare const WORKSPACE_ISOLATION_PROBE_METHOD: "daemon:workspace:isolation-probe";
24
+ /**
25
+ * What an explicit isolation request can be given from a base directory.
26
+ *
27
+ * - `worktree` — a git repository with a commit: a worktree is cut;
28
+ * - `sandbox` — the daemon's own bare scratch workspace: an empty
29
+ * private sandbox is used instead;
30
+ * - `not_a_repository` — refused: not inside a git repository;
31
+ * - `no_initial_commit` — refused: a repository with an unborn HEAD.
32
+ */
33
+ export declare const WORKTREE_BASE_VERDICTS: readonly ["worktree", "sandbox", "not_a_repository", "no_initial_commit"];
34
+ export type WorktreeBaseVerdict = (typeof WORKTREE_BASE_VERDICTS)[number];
35
+ /** The verdicts on which the daemon refuses an isolated Run before any tool. */
36
+ export declare const REFUSING_WORKTREE_BASE_VERDICTS: ReadonlySet<WorktreeBaseVerdict>;
37
+ export declare function isWorktreeBaseVerdict(value: unknown): value is WorktreeBaseVerdict;
38
+ export interface WorkspaceIsolationProbeRequest {
39
+ /** Omitted: the daemon resolves its own default, as it would for the Run. */
40
+ workingDirectory?: string;
41
+ }
42
+ export interface WorkspaceIsolationProbeAnswer {
43
+ /** The directory a Run asked with this `workingDirectory` would be rooted in. */
44
+ baseCwd: string;
45
+ /** Whether a person chose it (agent/task directory or configured default). */
46
+ chosenExplicitly: boolean;
47
+ verdict: WorktreeBaseVerdict;
48
+ /** The refusal sentence the Run would fail with; null when it would be admitted. */
49
+ remedy: string | null;
50
+ checkedAt: string;
51
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The workspace-isolation probe wire contract (OSK-15099).
3
+ *
4
+ * A Commitment Run is always isolated: the daemon cuts it a private git
5
+ * worktree from the directory the Run is rooted in, and refuses the Run with
6
+ * `EXECUTION_ISOLATION_UNAVAILABLE` before any tool when it cannot. Preflight
7
+ * had no way to read that machine's filesystem, so it reported `ready`,
8
+ * activation succeeded, and the first Run failed. The daemon now answers the
9
+ * same question BEFORE a Run, through the same predicate its executor refuses
10
+ * on, and this module is the one place both ends spell it.
11
+ *
12
+ * - The daemon announces {@link WORKSPACE_ISOLATION_PROBE_CAPABILITY}. A daemon
13
+ * that does not is never asked, and the server reports the prerequisite as
14
+ * UNVERIFIED — never as satisfied (unknown is never ok).
15
+ * - The server sends {@link WORKSPACE_ISOLATION_PROBE_METHOD} as a control RPC
16
+ * with `{ workingDirectory? }`: exactly the directory it would put on the
17
+ * Run's request (`agent.workingDirectory || daemon cwd`).
18
+ * - The daemon answers {@link WorkspaceIsolationProbeAnswer}.
19
+ *
20
+ * Pure: no I/O, safe for the browser, the CLI and the API.
21
+ */
22
+ export const WORKSPACE_ISOLATION_PROBE_CAPABILITY = 'workspace_isolation_probe_v1';
23
+ export const WORKSPACE_ISOLATION_PROBE_METHOD = 'daemon:workspace:isolation-probe';
24
+ /**
25
+ * What an explicit isolation request can be given from a base directory.
26
+ *
27
+ * - `worktree` — a git repository with a commit: a worktree is cut;
28
+ * - `sandbox` — the daemon's own bare scratch workspace: an empty
29
+ * private sandbox is used instead;
30
+ * - `not_a_repository` — refused: not inside a git repository;
31
+ * - `no_initial_commit` — refused: a repository with an unborn HEAD.
32
+ */
33
+ export const WORKTREE_BASE_VERDICTS = Object.freeze([
34
+ 'worktree',
35
+ 'sandbox',
36
+ 'not_a_repository',
37
+ 'no_initial_commit',
38
+ ]);
39
+ /** The verdicts on which the daemon refuses an isolated Run before any tool. */
40
+ export const REFUSING_WORKTREE_BASE_VERDICTS = new Set([
41
+ 'not_a_repository',
42
+ 'no_initial_commit',
43
+ ]);
44
+ export function isWorktreeBaseVerdict(value) {
45
+ return typeof value === 'string' && WORKTREE_BASE_VERDICTS.includes(value);
46
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skrr-ai/auth-core",
3
- "version": "0.4.25",
3
+ "version": "0.4.27",
4
4
  "main": "dist/cjs/index.js",
5
5
  "types": "dist/esm/index.d.ts",
6
6
  "exports": {
@@ -144,6 +144,16 @@
144
144
  "default": "./dist/cjs/dedicatedRuntimeWakeWire.js"
145
145
  }
146
146
  },
147
+ "./workspace-isolation-probe-wire": {
148
+ "import": {
149
+ "types": "./dist/esm/workspaceIsolationProbeWire.d.ts",
150
+ "default": "./dist/esm/workspaceIsolationProbeWire.js"
151
+ },
152
+ "require": {
153
+ "types": "./dist/cjs/workspaceIsolationProbeWire.d.ts",
154
+ "default": "./dist/cjs/workspaceIsolationProbeWire.js"
155
+ }
156
+ },
147
157
  "./commitment-execution-contract": {
148
158
  "import": {
149
159
  "types": "./dist/esm/commitmentExecutionContract.d.ts",