@skrr-ai/cli 0.1.93 → 0.1.95

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 (174) hide show
  1. package/README.md +153 -165
  2. package/bin/run.js +32 -0
  3. package/bin/update-npm-runner.cjs +33 -0
  4. package/bin/update-session.cjs +194 -0
  5. package/bin/update-worker.cjs +208 -0
  6. package/dist/base-command.js +0 -15
  7. package/dist/commands/agents/operating-plan.d.ts +11 -0
  8. package/dist/commands/agents/operating-plan.js +54 -0
  9. package/dist/commands/agents/roles/example.d.ts +11 -0
  10. package/dist/commands/agents/roles/example.js +30 -0
  11. package/dist/commands/{commitments/mode.d.ts → agents/roles/export.d.ts} +5 -5
  12. package/dist/commands/agents/roles/export.js +81 -0
  13. package/dist/commands/agents/roles/install.d.ts +12 -0
  14. package/dist/commands/agents/roles/install.js +48 -0
  15. package/dist/commands/agents/roles/list.d.ts +11 -0
  16. package/dist/commands/agents/roles/list.js +36 -0
  17. package/dist/commands/agents/roles/preview.d.ts +14 -0
  18. package/dist/commands/agents/roles/preview.js +49 -0
  19. package/dist/commands/agents/roles/withdraw.d.ts +13 -0
  20. package/dist/commands/agents/roles/withdraw.js +43 -0
  21. package/dist/commands/code/doctor.d.ts +1 -0
  22. package/dist/commands/code/doctor.js +6 -0
  23. package/dist/commands/code/index.js +4 -0
  24. package/dist/commands/code/install.d.ts +5 -62
  25. package/dist/commands/code/install.js +86 -279
  26. package/dist/commands/code/rollback.d.ts +8 -0
  27. package/dist/commands/code/rollback.js +20 -0
  28. package/dist/commands/code/update.d.ts +8 -37
  29. package/dist/commands/code/update.js +134 -213
  30. package/dist/commands/commitments/{autonomy-requests → cadence-requests}/decide.d.ts +5 -7
  31. package/dist/commands/commitments/{autonomy-requests → cadence-requests}/decide.js +13 -15
  32. package/dist/commands/commitments/cost.d.ts +1 -1
  33. package/dist/commands/commitments/cost.js +1 -1
  34. package/dist/commands/commitments/create.d.ts +0 -7
  35. package/dist/commands/commitments/create.js +9 -98
  36. package/dist/commands/commitments/delivery.js +3 -21
  37. package/dist/commands/commitments/doctor.js +28 -27
  38. package/dist/commands/commitments/effective-policy.js +5 -36
  39. package/dist/commands/commitments/evaluations.d.ts +15 -0
  40. package/dist/commands/commitments/evaluations.js +54 -0
  41. package/dist/commands/commitments/pack/activate.d.ts +11 -0
  42. package/dist/commands/commitments/pack/activate.js +22 -0
  43. package/dist/commands/commitments/pack/bind.js +1 -1
  44. package/dist/commands/commitments/remediate.js +2 -5
  45. package/dist/commands/commitments/show.js +1 -1
  46. package/dist/commands/commitments/update.js +9 -9
  47. package/dist/commands/commitments/wake-capabilities.d.ts +1 -3
  48. package/dist/commands/commitments/wake-capabilities.js +5 -21
  49. package/dist/commands/daemon/install.js +4 -0
  50. package/dist/commands/generate-recovery-code.d.ts +13 -1
  51. package/dist/commands/generate-recovery-code.js +146 -13
  52. package/dist/commands/instructions/guide.d.ts +20 -0
  53. package/dist/commands/instructions/guide.js +84 -0
  54. package/dist/commands/instructions/install.js +57 -33
  55. package/dist/commands/instructions/setup.d.ts +14 -0
  56. package/dist/commands/instructions/setup.js +72 -0
  57. package/dist/commands/integrations/execute.js +1 -1
  58. package/dist/commands/login.d.ts +5 -6
  59. package/dist/commands/login.js +27 -11
  60. package/dist/commands/logout.js +7 -7
  61. package/dist/commands/security/overview.d.ts +11 -0
  62. package/dist/commands/security/overview.js +158 -0
  63. package/dist/commands/spaces/claim.d.ts +12 -0
  64. package/dist/commands/spaces/claim.js +42 -0
  65. package/dist/commands/spaces/ownership.d.ts +12 -0
  66. package/dist/commands/spaces/ownership.js +46 -0
  67. package/dist/commands/tasks/cancel-cascade.js +1 -1
  68. package/dist/commands/tasks/create-tree.d.ts +5 -1
  69. package/dist/commands/tasks/create-tree.js +35 -7
  70. package/dist/commands/tasks/interrupt.js +2 -2
  71. package/dist/commands/tasks/pause-schedule.js +1 -1
  72. package/dist/commands/token/list.d.ts +3 -5
  73. package/dist/commands/token/list.js +99 -57
  74. package/dist/commands/update.d.ts +14 -0
  75. package/dist/commands/update.js +61 -0
  76. package/dist/generated/managed-instruction-guides.json +9 -0
  77. package/dist/hooks/init.d.ts +5 -0
  78. package/dist/hooks/init.js +27 -0
  79. package/dist/lib/commitment-product.d.ts +0 -6
  80. package/dist/lib/commitment-product.js +1 -30
  81. package/dist/lib/commitments.d.ts +9 -53
  82. package/dist/lib/commitments.js +23 -146
  83. package/dist/lib/daemon-installer.d.ts +2 -0
  84. package/dist/lib/daemon-installer.js +100 -24
  85. package/dist/lib/first-party-harness-doctor.js +31 -1
  86. package/dist/lib/first-party-harness-remote-install.d.ts +71 -0
  87. package/dist/lib/first-party-harness-remote-install.js +310 -0
  88. package/dist/lib/first-party-harness-remote-update.d.ts +46 -0
  89. package/dist/lib/first-party-harness-remote-update.js +242 -0
  90. package/dist/lib/first-party-harness-startup-update.d.ts +26 -0
  91. package/dist/lib/first-party-harness-startup-update.js +166 -0
  92. package/dist/lib/first-party-harness-update.d.ts +46 -0
  93. package/dist/lib/first-party-harness-update.js +289 -0
  94. package/dist/lib/first-party-harness.js +7 -4
  95. package/dist/lib/managed-instruction-local.d.ts +12 -0
  96. package/dist/lib/managed-instruction-local.js +58 -0
  97. package/dist/lib/managed-instruction-setup.d.ts +35 -0
  98. package/dist/lib/managed-instruction-setup.js +306 -0
  99. package/dist/lib/memory-sync.d.ts +1 -0
  100. package/dist/lib/memory-sync.js +279 -68
  101. package/dist/lib/northstars.js +1 -1
  102. package/dist/lib/platform-update-local.d.ts +2 -0
  103. package/dist/lib/platform-update-local.js +113 -0
  104. package/dist/lib/platform-update.d.ts +39 -0
  105. package/dist/lib/platform-update.js +84 -0
  106. package/dist/lib/profile-machine-identity-guard.d.ts +2 -0
  107. package/dist/lib/profile-machine-identity-guard.js +13 -0
  108. package/dist/lib/prompt.d.ts +1 -0
  109. package/dist/lib/prompt.js +4 -3
  110. package/dist/lib/roles.d.ts +98 -0
  111. package/dist/lib/roles.js +45 -0
  112. package/dist/lib/task-batch.d.ts +9 -0
  113. package/dist/lib/task-batch.js +94 -17
  114. package/dist/lib/task-instruction-offer.d.ts +1 -4
  115. package/dist/lib/task-instruction-offer.js +3 -129
  116. package/dist/lib/update-check.d.ts +61 -53
  117. package/dist/lib/update-check.js +453 -314
  118. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/commitmentExecutionContract.d.ts +45 -0
  119. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/commitmentExecutionContract.js +550 -0
  120. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +3 -4
  121. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +3 -4
  122. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.d.ts +82 -0
  123. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.js +105 -0
  124. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.d.ts +110 -0
  125. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.js +945 -0
  126. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.d.ts +32 -0
  127. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.js +62 -0
  128. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -0
  129. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +4 -1
  130. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +35 -5
  131. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.d.ts +63 -0
  132. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.js +169 -0
  133. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.d.ts +2 -0
  134. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.js +44 -0
  135. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/sessionPermissionAuthority.d.ts +7 -44
  136. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/sessionPermissionAuthority.js +8 -47
  137. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/commitmentExecutionContract.d.ts +45 -0
  138. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/commitmentExecutionContract.js +545 -0
  139. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +3 -4
  140. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +3 -4
  141. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.d.ts +82 -0
  142. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.js +102 -0
  143. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.d.ts +110 -0
  144. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.js +923 -0
  145. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.d.ts +32 -0
  146. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.js +59 -0
  147. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -0
  148. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -0
  149. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +35 -5
  150. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.d.ts +63 -0
  151. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.js +163 -0
  152. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.d.ts +2 -0
  153. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.js +41 -0
  154. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/sessionPermissionAuthority.d.ts +7 -44
  155. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/sessionPermissionAuthority.js +8 -47
  156. package/dist/node_modules/@skrr-ai/auth-core/package.json +41 -1
  157. package/dist/node_modules/@skrr-ai/data-provider/index.js +7362 -7232
  158. package/dist/node_modules/@skrr-ai/data-provider/package.json +2 -2
  159. package/docs/v1/README.md +28 -0
  160. package/docs/v1/api.md +55 -0
  161. package/docs/v1/changelog.md +16 -0
  162. package/docs/v1/check-examples.mjs +178 -0
  163. package/docs/v1/examples/api-readonly.mjs +144 -0
  164. package/docs/v1/operations.md +87 -0
  165. package/docs/v1/quickstart.md +71 -0
  166. package/oclif.manifest.json +46381 -45478
  167. package/package.json +16 -11
  168. package/dist/commands/commitments/autonomy.d.ts +0 -30
  169. package/dist/commands/commitments/autonomy.js +0 -67
  170. package/dist/commands/commitments/mode.js +0 -58
  171. package/dist/commands/commitments/pack/promote.d.ts +0 -19
  172. package/dist/commands/commitments/pack/promote.js +0 -46
  173. package/dist/commands/commitments/pack/shadow.d.ts +0 -20
  174. package/dist/commands/commitments/pack/shadow.js +0 -42
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Read-only capability wire for ordinary agent turns. This is NOT a session
3
+ * authorization request or an effective permission profile. In particular,
4
+ * an ACP approval callback is not a process sandbox, and access to a loopback
5
+ * inference broker is not a grant to make arbitrary outbound connections.
6
+ *
7
+ * Daemons advertise what their adapters implement; execution still validates
8
+ * the requested policy independently. Consumers never infer controls from a
9
+ * provider name, a missing report, or the unified permission-mode vocabulary.
10
+ */
11
+ export interface HarnessPermissionControls {
12
+ version: 1;
13
+ plan: 'harness' | 'reported-actions' | 'unsupported' | 'unknown';
14
+ approval: 'harness' | 'reported-actions' | 'none' | 'unknown';
15
+ filesystem: 'harness-sandbox' | 'tool-path-guards' | 'reported-actions' | 'not-restricted-by-daemon' | 'unknown';
16
+ strictToolPolicy: {
17
+ modes: readonly ('none' | 'allowlist')[];
18
+ enforcement: 'harness' | 'negotiated' | 'unsupported' | 'unknown';
19
+ };
20
+ organizationToolDenylist: 'unsupported' | 'unknown';
21
+ networkEgress: 'not-restricted-by-daemon' | 'unknown';
22
+ }
23
+ export interface HarnessPermissionControlReport {
24
+ controls: HarnessPermissionControls | null;
25
+ source: 'daemon' | 'daemon_record' | 'unreported';
26
+ }
27
+ /**
28
+ * Presence/registration metadata is untrusted input. Copy only the v1 enum
29
+ * vocabulary: unknown future claims and malformed profiles yield no report,
30
+ * rather than a green badge. Additive fields are stripped for compatibility.
31
+ */
32
+ export declare function parseHarnessPermissionControls(value: unknown): HarnessPermissionControls | null;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseHarnessPermissionControls = parseHarnessPermissionControls;
4
+ const PLAN = ['harness', 'reported-actions', 'unsupported', 'unknown'];
5
+ const APPROVAL = ['harness', 'reported-actions', 'none', 'unknown'];
6
+ const FILESYSTEM = [
7
+ 'harness-sandbox',
8
+ 'tool-path-guards',
9
+ 'reported-actions',
10
+ 'not-restricted-by-daemon',
11
+ 'unknown',
12
+ ];
13
+ const TOOL_ENFORCEMENT = ['harness', 'negotiated', 'unsupported', 'unknown'];
14
+ /**
15
+ * Presence/registration metadata is untrusted input. Copy only the v1 enum
16
+ * vocabulary: unknown future claims and malformed profiles yield no report,
17
+ * rather than a green badge. Additive fields are stripped for compatibility.
18
+ */
19
+ function parseHarnessPermissionControls(value) {
20
+ try {
21
+ if (!value || typeof value !== 'object' || Array.isArray(value))
22
+ return null;
23
+ const row = value;
24
+ const { version, plan, approval, filesystem, organizationToolDenylist, networkEgress, strictToolPolicy, } = row;
25
+ const strict = strictToolPolicy;
26
+ if (!strict || typeof strict !== 'object' || Array.isArray(strict))
27
+ return null;
28
+ const strictModes = strict.modes;
29
+ const enforcement = strict.enforcement;
30
+ if (version !== 1 ||
31
+ !PLAN.includes(plan) ||
32
+ !APPROVAL.includes(approval) ||
33
+ !FILESYSTEM.includes(filesystem) ||
34
+ !['unsupported', 'unknown'].includes(organizationToolDenylist) ||
35
+ !['not-restricted-by-daemon', 'unknown'].includes(networkEgress) ||
36
+ !TOOL_ENFORCEMENT.includes(enforcement) ||
37
+ !Array.isArray(strictModes) ||
38
+ strictModes.length > 2 ||
39
+ !strictModes.every((mode) => mode === 'none' || mode === 'allowlist'))
40
+ return null;
41
+ const modes = [...new Set(strictModes)];
42
+ if (((enforcement === 'unsupported' || enforcement === 'unknown') && modes.length > 0) ||
43
+ ((enforcement === 'harness' || enforcement === 'negotiated') && modes.length === 0))
44
+ return null;
45
+ return Object.freeze({
46
+ version: 1,
47
+ plan: plan,
48
+ approval: approval,
49
+ filesystem: filesystem,
50
+ strictToolPolicy: Object.freeze({
51
+ modes: Object.freeze(modes),
52
+ enforcement: enforcement,
53
+ }),
54
+ organizationToolDenylist: organizationToolDenylist,
55
+ networkEgress: networkEgress,
56
+ });
57
+ }
58
+ catch {
59
+ // Malicious accessors/proxies must not break a read surface.
60
+ return null;
61
+ }
62
+ }
@@ -46,3 +46,5 @@ export { OVERSKY_RELEASE_PUBLIC_KEYS, type ReleaseKey } from './releaseKeys.js';
46
46
  export { MAX_SIGS, TRUSTED_FEED_ORIGINS, buildArtifactMessage, buildManifestMessage, getDeltaFor, isTrustedArtifactOrigin, verifyArtifact, verifyManifest, type DaemonVersionManifest, type DeltaDescriptor, type ManifestSignature, type PlatformArtifact, type VerifyResult, } from './releaseManifest.js';
47
47
  export { resolveConfigRoot, configRootPath, isConfigRootOverridden } from './configRoot.js';
48
48
  export { PROFILES_SEGMENT, SUPERSEDED_MARKER_SUFFIX, SUPERSEDED_COPY_SUFFIX, profileStateDir, legacyProfileStateDir, resolveProfileStateDir, resolveProfileStateFile, profileStateFileLocations, profileStateFilePathsToClear, consolidateProfileState, type ResolvedProfileStateDir, type ConsolidationSpec, type ConsolidationStatus, type ConsolidationOutcome, } from './profileStateDir.js';
49
+ export * from './harnessPermissionControls.js';
50
+ export { compareReleaseVersions } from './releaseVersions.js';
@@ -26,7 +26,7 @@ exports.runHeadersHelper = exports.SENTINEL_TRIGGERING_FAILURES = exports.__rese
26
26
  exports.jwkThumbprint = exports.generateDeviceKeyPair = exports.buildDeviceProof = exports.zeroizeKekCaches = exports.WindowsKek = exports.registerZeroizeHook = exports.MacosKek = exports.LinuxKek = exports.KekUnavailableError = exports.UnavailableKek = exports.InMemoryKek = exports.getKekStrategy = exports.__setKekStrategyForTest = exports.credEnvelopeWrap = exports.credEnvelopeUnwrap = exports.credEnvelopeSerialize = exports.credSealBuffer = exports.credOpenBuffer = exports.CRED_KEY_LEN = exports.isCredEnvelopeString = exports.generateDek = exports.CRED_ENVELOPE_MAGIC = exports.credEnvelopeEncrypt = exports.credEnvelopeDeserialize = exports.credEnvelopeDecrypt = exports.buildCredentialAad = exports.isValidRecoveryCode = exports.normalizeRecoveryCode = exports.parseRecoveryCode = exports.formatRecoveryCode = exports.MAX_TTL_DAYS = exports.MIN_TTL_DAYS = exports.parseTtlDays = exports.LoginExchangeError = exports.LoginInitError = exports.loginWithLocalhost = exports.LocalCallbackMalformedError = exports.LocalCallbackDeniedError = exports.LocalCallbackStateMismatchError = exports.LocalCallbackTimeoutError = exports.LocalCallbackBindError = exports.LocalCallbackError = exports.DEFAULT_LOGIN_TIMEOUT_MS = exports.startLocalCallback = exports.verifyChallenge = exports.safeEqual = exports.generateState = exports.challenge = exports.generateVerifier = exports.invalidateHeadersHelperCache = void 0;
27
27
  exports.qualifyMachineUuid = exports.getMachineUuidProfile = exports.setMachineUuidProfile = exports.getHardwareMachineUuid = exports.getMachineUuid = exports.__getDeviceIdentityStateForTest = exports.__setDeviceIdentityStateForTest = exports.__enrollmentBackoffPathForTest = exports.__enrollmentMarkerPathForTest = exports.__privateKeyPathForTest = exports.__publicKeyPathForTest = exports.describeDeviceKeySplit = exports.describeDeviceIdentityState = exports.getDevicePublicKey = exports.isDeviceIdentityActive = exports.clearEnrollmentMarker = exports.enrollWithServer = exports.signServerRequest = exports.resetDeviceIdentity = exports.initDeviceIdentity = exports.__resetShutdownGuardForTest = exports.__getCredEnvelopeStateForTest = exports.__setCredEnvelopeStateForTest = exports.__wrappedDekFilePathForTest = exports.DEFAULT_UNREADABLE_CREDENTIAL_HINT = exports.maybeDecryptOnRead = exports.maybeEncryptForWrite = exports.splitKeyConvergencePolicy = exports.describeCredEnvelopeState = exports.hasCredEnvelopePathConflict = exports.isCredEnvelopeActive = exports.shutdownCredEnvelope = exports.resetCredEnvelope = exports.initCredEnvelope = exports.verifyDpopProof = exports.normalizeDpopHtu = exports.isJwkThumbprint = exports.isAth = exports.createDpopKey = exports.accessTokenHash = exports.DPOP_PROOF_VERIFY_UNAVAILABLE = exports.DPOP_PROOF_TYP = exports.DPOP_PROOF_REQUIRED = exports.DPOP_PROOF_INVALID = exports.DPOP_MAX_AGE_SECONDS = exports.DPOP_HEADER = exports.verifyDeviceProof = exports.tokenHash = exports.publicJwkFromX = exports.normalizeHtu = void 0;
28
28
  exports.LOOPBACK_DEFAULT_MAX_BODY_BYTES = exports.LoopbackTargetRefusedError = exports.isLoopbackAddress = exports.isLoopbackHostname = exports.assertLoopbackHttpUrl = exports.loopbackFetch = exports.withLoopbackNoProxy = exports.envHasProxy = exports.LOOPBACK_NO_PROXY_HOSTS = exports.isDaemonChildEnv = exports.DAEMON_CHILD_ENV = exports.SENSITIVE_ENV_PREFIX_EXCEPTIONS = exports.SENSITIVE_ENV_NAME_TOKENS = exports.SENSITIVE_ENV_SUFFIXES = exports.SENSITIVE_ENV_PREFIXES = exports.SENSITIVE_ENV_VARS = exports.sanitizeSpawnEnv = exports.harnessLoadsClaudeSkillsLayout = exports.claudeSkillsLayoutEvidence = exports.HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT = exports.tierPermitsConfiguredAutoMode = exports.credentialPolicyForTier = exports.getHarnessTier = exports.HARNESS_TIERS = exports.pairUint8ArrayToBase64Url = exports.pairUint8ArrayToBase64 = exports.parsePairUrl = exports.parsePairPlaintext = exports.parsePairBundle = exports.openPairBundleBase64 = exports.generatePairKeyPair = exports.buildPairUrl = exports.buildPairAad = exports.pairBase64UrlToUint8Array = exports.pairBase64ToUint8Array = exports.assemblePairBundleBase64 = exports.PairBundleError = exports.PAIR_SECRET_LEN = exports.PAIR_PUBKEY_LEN = exports.PAIR_NONCE_LEN = exports.PAIR_BUNDLE_MAX_BASE64_LEN = exports.PAIR_AAD_MAX_LEN = exports.PAIR_AAD_PREFIX = exports.__resetCachedDaemonIdForTest = exports.legacyDaemonUuids = exports.ensureMachineDaemonId = exports.__resetMachineUuidCacheForTest = exports.HOSTED_RUNTIME_MACHINE_SOURCES = exports.hostedRuntimeIdOverride = exports.MACHINE_IDENTITY_FILENAME = void 0;
29
- exports.consolidateProfileState = exports.profileStateFilePathsToClear = exports.profileStateFileLocations = exports.resolveProfileStateFile = exports.resolveProfileStateDir = exports.legacyProfileStateDir = exports.profileStateDir = exports.SUPERSEDED_COPY_SUFFIX = exports.SUPERSEDED_MARKER_SUFFIX = exports.PROFILES_SEGMENT = exports.isConfigRootOverridden = exports.configRootPath = exports.resolveConfigRoot = exports.verifyManifest = exports.verifyArtifact = exports.isTrustedArtifactOrigin = exports.getDeltaFor = exports.buildManifestMessage = exports.buildArtifactMessage = exports.TRUSTED_FEED_ORIGINS = exports.MAX_SIGS = exports.OVERSKY_RELEASE_PUBLIC_KEYS = exports.resolveFirstPartyHarnessChannel = exports.resolveChannelManifestUrl = exports.isFirstPartyHarnessChannel = exports.firstPartyHarnessFeedCandidates = exports.firstPartyHarnessFeedBase = exports.firstPartyHarnessChannelPrefix = exports.FIRST_PARTY_HARNESS_CHANNELS = exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL = exports.migrateFirstPartyHarnessHome = exports.linkFirstPartyHarnessAliases = exports.firstPartyHarnessInstalledBinaryPath = exports.firstPartyHarnessHomeDir = exports.firstPartyHarnessBinDir = exports.FIRST_PARTY_HARNESS_PROVENANCE_SUFFIX = exports.FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX = exports.describeLegacyState = exports.findLegacyLocalState = exports.DEFAULT_BINARY_NAME = exports.LEGACY_NATIVE_ID_PREFIXES = exports.NATIVE_ID_PREFIX = exports.LEGACY_CONFIG_DIR_NAMES = exports.CONFIG_DIR_NAME = void 0;
29
+ exports.compareReleaseVersions = exports.consolidateProfileState = exports.profileStateFilePathsToClear = exports.profileStateFileLocations = exports.resolveProfileStateFile = exports.resolveProfileStateDir = exports.legacyProfileStateDir = exports.profileStateDir = exports.SUPERSEDED_COPY_SUFFIX = exports.SUPERSEDED_MARKER_SUFFIX = exports.PROFILES_SEGMENT = exports.isConfigRootOverridden = exports.configRootPath = exports.resolveConfigRoot = exports.verifyManifest = exports.verifyArtifact = exports.isTrustedArtifactOrigin = exports.getDeltaFor = exports.buildManifestMessage = exports.buildArtifactMessage = exports.TRUSTED_FEED_ORIGINS = exports.MAX_SIGS = exports.OVERSKY_RELEASE_PUBLIC_KEYS = exports.resolveFirstPartyHarnessChannel = exports.resolveChannelManifestUrl = exports.isFirstPartyHarnessChannel = exports.firstPartyHarnessFeedCandidates = exports.firstPartyHarnessFeedBase = exports.firstPartyHarnessChannelPrefix = exports.FIRST_PARTY_HARNESS_CHANNELS = exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL = exports.migrateFirstPartyHarnessHome = exports.linkFirstPartyHarnessAliases = exports.firstPartyHarnessInstalledBinaryPath = exports.firstPartyHarnessHomeDir = exports.firstPartyHarnessBinDir = exports.FIRST_PARTY_HARNESS_PROVENANCE_SUFFIX = exports.FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX = exports.describeLegacyState = exports.findLegacyLocalState = exports.DEFAULT_BINARY_NAME = exports.LEGACY_NATIVE_ID_PREFIXES = exports.NATIVE_ID_PREFIX = exports.LEGACY_CONFIG_DIR_NAMES = exports.CONFIG_DIR_NAME = void 0;
30
30
  // Daemon scope vocabulary — single source of truth for non-Mongoose tiers
31
31
  // (daemon, desktop, cli). Schema-bound canonical lives in
32
32
  // `@skrr-ai/data-schemas/common/daemonScopes`; the two are kept byte-equal
@@ -438,3 +438,6 @@ Object.defineProperty(exports, "resolveProfileStateFile", { enumerable: true, ge
438
438
  Object.defineProperty(exports, "profileStateFileLocations", { enumerable: true, get: function () { return profileStateDir_js_1.profileStateFileLocations; } });
439
439
  Object.defineProperty(exports, "profileStateFilePathsToClear", { enumerable: true, get: function () { return profileStateDir_js_1.profileStateFilePathsToClear; } });
440
440
  Object.defineProperty(exports, "consolidateProfileState", { enumerable: true, get: function () { return profileStateDir_js_1.consolidateProfileState; } });
441
+ __exportStar(require("./harnessPermissionControls.js"), exports);
442
+ var releaseVersions_js_1 = require("./releaseVersions.js");
443
+ Object.defineProperty(exports, "compareReleaseVersions", { enumerable: true, get: function () { return releaseVersions_js_1.compareReleaseVersions; } });
@@ -211,11 +211,14 @@ function setMachineUuidProfile(profile) {
211
211
  function getMachineUuidProfile() {
212
212
  return activeMachineProfile;
213
213
  }
214
- function readPersistedChoice(file, hardware, profile) {
214
+ function readPersistedChoice(file, hardware, profile, requireProfile = false) {
215
215
  try {
216
216
  const parsed = JSON.parse(node_fs_1.default.readFileSync(file, 'utf-8'));
217
217
  // A file from another machine (a restored backup, a synced home) is absent.
218
- if (parsed?.version !== 1 || parsed.hardware !== hardware)
218
+ if (parsed?.version !== 1 ||
219
+ parsed.hardware !== hardware ||
220
+ (parsed.profile !== undefined && parsed.profile !== profile) ||
221
+ (requireProfile && parsed.profile !== profile))
219
222
  return null;
220
223
  // Only the two values this module can produce are accepted: a hand-edited or
221
224
  // foreign string must never become an identity.
@@ -228,11 +231,18 @@ function readPersistedChoice(file, hardware, profile) {
228
231
  return null;
229
232
  }
230
233
  }
231
- function persistChoice(dir, file, hardware, machineUuid, reason) {
234
+ function persistChoice(dir, file, hardware, machineUuid, reason, profile) {
232
235
  try {
233
236
  node_fs_1.default.mkdirSync(dir, { recursive: true, mode: 0o700 });
234
237
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
235
- const body = { version: 1, hardware, machineUuid, reason, decidedAt: new Date().toISOString() };
238
+ const body = {
239
+ version: 1,
240
+ hardware,
241
+ machineUuid,
242
+ profile,
243
+ reason,
244
+ decidedAt: new Date().toISOString(),
245
+ };
236
246
  node_fs_1.default.writeFileSync(tmp, `${JSON.stringify(body)}\n`, { mode: 0o600 });
237
247
  node_fs_1.default.renameSync(tmp, file);
238
248
  }
@@ -241,6 +251,20 @@ function persistChoice(dir, file, hardware, machineUuid, reason) {
241
251
  // next process decides again from the same evidence.
242
252
  }
243
253
  }
254
+ /** A nonsecret recovery copy survives product uninstall. Each profile has its
255
+ * own file, so concurrent profiles never overwrite a shared identity map. A
256
+ * copied home cannot adopt another machine's value: the hardware is verified
257
+ * and only this module's hardware/qualified values are admitted. */
258
+ function recoveryChoicePath(profile) {
259
+ const name = node_crypto_1.default.createHash('sha256').update(profile).digest('hex');
260
+ return node_path_1.default.join(node_os_1.default.homedir(), '.local', 'state', 'skrr', 'machine-identities', `${name}.json`);
261
+ }
262
+ function preserveRecoveryChoice(profile, hardware, machineUuid) {
263
+ const file = recoveryChoicePath(profile);
264
+ if (readPersistedChoice(file, hardware, profile, true) === machineUuid)
265
+ return;
266
+ persistChoice(node_path_1.default.dirname(file), file, hardware, machineUuid, 'restored_profile', profile);
267
+ }
244
268
  /** The identity a NON-default profile registers under. Never throws. */
245
269
  function resolveProfileMachineUuid(profile, hardware) {
246
270
  const cacheKey = `${hardware}\u0000${profile}`;
@@ -250,6 +274,11 @@ function resolveProfileMachineUuid(profile, hardware) {
250
274
  const dir = node_path_1.default.join((0, configRoot_js_1.resolveConfigRoot)(), 'profiles', profile);
251
275
  const file = node_path_1.default.join(dir, exports.MACHINE_IDENTITY_FILENAME);
252
276
  let chosen = readPersistedChoice(file, hardware, profile);
277
+ if (!chosen) {
278
+ chosen = readPersistedChoice(recoveryChoicePath(profile), hardware, profile, true);
279
+ if (chosen)
280
+ persistChoice(dir, file, hardware, chosen, 'restored_profile', profile);
281
+ }
253
282
  if (!chosen) {
254
283
  const preExisting = PRE_EXISTING_PROFILE_EVIDENCE.some((name) => {
255
284
  try {
@@ -260,8 +289,9 @@ function resolveProfileMachineUuid(profile, hardware) {
260
289
  }
261
290
  });
262
291
  chosen = preExisting ? hardware : qualifyMachineUuid(hardware, profile);
263
- persistChoice(dir, file, hardware, chosen, preExisting ? 'pre_existing_profile' : 'new_profile');
292
+ persistChoice(dir, file, hardware, chosen, preExisting ? 'pre_existing_profile' : 'new_profile', profile);
264
293
  }
294
+ preserveRecoveryChoice(profile, hardware, chosen);
265
295
  profileChoiceCache.set(cacheKey, chosen);
266
296
  return chosen;
267
297
  }
@@ -0,0 +1,63 @@
1
+ import { type ReleaseKey } from './releaseKeys.js';
2
+ export type ReleaseChannel = 'stable' | 'canary' | 'beta' | 'internal';
3
+ export interface ContractRange {
4
+ minimum: number;
5
+ maximum: number;
6
+ }
7
+ export interface ComponentReleasePolicy {
8
+ version: string;
9
+ minimumVersion?: string;
10
+ contracts?: Record<string, ContractRange>;
11
+ }
12
+ export interface ReleasePolicySignature {
13
+ keyId: string;
14
+ sig: string;
15
+ }
16
+ export interface SignedReleasePolicy {
17
+ schemaVersion: 1;
18
+ releaseId: string;
19
+ issuedAt: string;
20
+ expiresAt: string;
21
+ channel: ReleaseChannel;
22
+ /** Percentage in basis points (0–10000), avoiding floating-point policy. */
23
+ rolloutBasisPoints: number;
24
+ cohortSeed: string;
25
+ components: Record<string, ComponentReleasePolicy>;
26
+ keyId: string;
27
+ sig: string;
28
+ sigs?: ReleasePolicySignature[];
29
+ }
30
+ export type PolicyVerifyResult = {
31
+ ok: true;
32
+ } | {
33
+ ok: false;
34
+ reason: string;
35
+ } | {
36
+ disabled: true;
37
+ };
38
+ export interface ReleasePolicyAssessment {
39
+ valid: boolean;
40
+ eligible: boolean;
41
+ reason?: string;
42
+ }
43
+ /** Canonical, versioned signature message. Never sign JSON.stringify directly. */
44
+ export declare function buildReleasePolicyMessage(policy: Omit<SignedReleasePolicy, 'sig' | 'sigs'>): string;
45
+ export declare function verifyReleasePolicy(policy: SignedReleasePolicy, keys?: readonly ReleaseKey[]): PolicyVerifyResult;
46
+ export declare function isReleasePolicyCohortEligible(policy: SignedReleasePolicy, subject: string): boolean;
47
+ /**
48
+ * Verify expiry, daemon target binding, contract ranges, channel, and cohort.
49
+ * `autonomous=false` intentionally bypasses cohort gating: an operator who
50
+ * explicitly runs an update may recover a machine outside the staged cohort,
51
+ * but cannot bypass invalid signatures, an expired policy, or incompatibility.
52
+ */
53
+ export declare function assessReleasePolicy(args: {
54
+ policy: SignedReleasePolicy;
55
+ targetDaemonVersion: string;
56
+ /** Defaults to daemon for backwards-compatible daemon policy assessment. */
57
+ targetComponent?: string;
58
+ channel: ReleaseChannel;
59
+ contracts: Record<string, number>;
60
+ cohortSubject: string;
61
+ autonomous: boolean;
62
+ now?: Date;
63
+ }): ReleasePolicyAssessment;
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildReleasePolicyMessage = buildReleasePolicyMessage;
4
+ exports.verifyReleasePolicy = verifyReleasePolicy;
5
+ exports.isReleasePolicyCohortEligible = isReleasePolicyCohortEligible;
6
+ exports.assessReleasePolicy = assessReleasePolicy;
7
+ /**
8
+ * Signed multi-component release policy.
9
+ *
10
+ * The daemon artifact manifest authenticates a daemon binary. This envelope
11
+ * authenticates the *decision* to offer that binary to a cohort, and records
12
+ * the component contract that makes a CLI/daemon/desktop combination safe.
13
+ * It is separately signed so it can be added to a v1 artifact manifest without
14
+ * changing the legacy manifest signature bytes or blocking old daemons.
15
+ */
16
+ const node_crypto_1 = require("node:crypto");
17
+ const releaseKeys_js_1 = require("./releaseKeys.js");
18
+ const MAX_SIGS = 8;
19
+ function stableJson(value) {
20
+ if (value === null ||
21
+ typeof value === 'boolean' ||
22
+ typeof value === 'number' ||
23
+ typeof value === 'string') {
24
+ return JSON.stringify(value);
25
+ }
26
+ if (Array.isArray(value))
27
+ return `[${value.map(stableJson).join(',')}]`;
28
+ if (!value || typeof value !== 'object')
29
+ throw new Error('policy contains an unsupported value');
30
+ const record = value;
31
+ return `{${Object.keys(record)
32
+ .sort()
33
+ .map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`)
34
+ .join(',')}}`;
35
+ }
36
+ /** Canonical, versioned signature message. Never sign JSON.stringify directly. */
37
+ function buildReleasePolicyMessage(policy) {
38
+ return ['oversky-release-policy', 'v1', stableJson(policy)].join('\n');
39
+ }
40
+ function verifyOne(message, signature, keys) {
41
+ if (keys.length === 0)
42
+ return { disabled: true };
43
+ if (!signature)
44
+ return { ok: false, reason: 'missing signature' };
45
+ const messageBytes = Buffer.from(message, 'utf8');
46
+ const signatureBytes = Buffer.from(signature, 'base64');
47
+ for (const keySpec of keys) {
48
+ try {
49
+ const key = (0, node_crypto_1.createPublicKey)({
50
+ key: Buffer.from(keySpec.publicKeySpkiB64, 'base64'),
51
+ format: 'der',
52
+ type: 'spki',
53
+ });
54
+ if ((0, node_crypto_1.verify)(null, messageBytes, key, signatureBytes))
55
+ return { ok: true };
56
+ }
57
+ catch {
58
+ /* malformed signatures and keys are a verification failure */
59
+ }
60
+ }
61
+ return { ok: false, reason: 'signature did not verify against any pinned key' };
62
+ }
63
+ function policyShapeIsValid(policy) {
64
+ if (!policy ||
65
+ typeof policy !== 'object' ||
66
+ policy.schemaVersion !== 1 ||
67
+ !/^[0-9A-Za-z._:-]+$/.test(policy.releaseId) ||
68
+ !['stable', 'canary', 'beta', 'internal'].includes(policy.channel) ||
69
+ !Number.isInteger(policy.rolloutBasisPoints) ||
70
+ policy.rolloutBasisPoints < 0 ||
71
+ policy.rolloutBasisPoints > 10_000 ||
72
+ typeof policy.cohortSeed !== 'string' ||
73
+ policy.cohortSeed.length < 8 ||
74
+ !policy.components ||
75
+ typeof policy.components !== 'object' ||
76
+ Array.isArray(policy.components)) {
77
+ return false;
78
+ }
79
+ const issued = Date.parse(policy.issuedAt);
80
+ const expires = Date.parse(policy.expiresAt);
81
+ if (!Number.isFinite(issued) || !Number.isFinite(expires) || expires <= issued)
82
+ return false;
83
+ return Object.values(policy.components).every((component) => component &&
84
+ typeof component === 'object' &&
85
+ !Array.isArray(component) &&
86
+ (!component.contracts ||
87
+ (typeof component.contracts === 'object' && !Array.isArray(component.contracts))) &&
88
+ typeof component.version === 'string' &&
89
+ component.version.length > 0 &&
90
+ Object.values(component.contracts ?? {}).every((range) => range &&
91
+ typeof range === 'object' &&
92
+ !Array.isArray(range) &&
93
+ Number.isInteger(range.minimum) &&
94
+ Number.isInteger(range.maximum) &&
95
+ range.minimum >= 0 &&
96
+ range.maximum >= range.minimum));
97
+ }
98
+ function verifyReleasePolicy(policy, keys = releaseKeys_js_1.OVERSKY_RELEASE_PUBLIC_KEYS) {
99
+ if (!policyShapeIsValid(policy))
100
+ return { ok: false, reason: 'invalid release-policy shape' };
101
+ const { sig, sigs, ...unsigned } = policy;
102
+ const message = buildReleasePolicyMessage(unsigned);
103
+ if (Array.isArray(sigs) && sigs.length > 0) {
104
+ if (keys.length === 0)
105
+ return { disabled: true };
106
+ for (const entry of sigs.slice(0, MAX_SIGS)) {
107
+ if (!entry || typeof entry.sig !== 'string')
108
+ continue;
109
+ const verdict = verifyOne(message, entry.sig, keys);
110
+ if ('ok' in verdict && verdict.ok)
111
+ return verdict;
112
+ }
113
+ return { ok: false, reason: 'no policy multi-signature verified against a pinned key' };
114
+ }
115
+ return verifyOne(message, sig, keys);
116
+ }
117
+ function isReleasePolicyCohortEligible(policy, subject) {
118
+ if (!subject || policy.rolloutBasisPoints === 0)
119
+ return false;
120
+ if (policy.rolloutBasisPoints === 10_000)
121
+ return true;
122
+ const bucket = (0, node_crypto_1.createHash)('sha256')
123
+ .update(`${policy.releaseId}\n${policy.cohortSeed}\n${subject}`, 'utf8')
124
+ .digest()
125
+ .readUInt32BE(0) % 10_000;
126
+ return bucket < policy.rolloutBasisPoints;
127
+ }
128
+ /**
129
+ * Verify expiry, daemon target binding, contract ranges, channel, and cohort.
130
+ * `autonomous=false` intentionally bypasses cohort gating: an operator who
131
+ * explicitly runs an update may recover a machine outside the staged cohort,
132
+ * but cannot bypass invalid signatures, an expired policy, or incompatibility.
133
+ */
134
+ function assessReleasePolicy(args) {
135
+ const now = args.now ?? new Date();
136
+ if (Date.parse(args.policy.expiresAt) <= now.getTime()) {
137
+ return { valid: false, eligible: false, reason: `policy expired at ${args.policy.expiresAt}` };
138
+ }
139
+ if (args.policy.channel !== args.channel) {
140
+ return {
141
+ valid: false,
142
+ eligible: false,
143
+ reason: `policy channel ${args.policy.channel} does not match ${args.channel}`,
144
+ };
145
+ }
146
+ const componentName = args.targetComponent ?? 'daemon';
147
+ const daemon = args.policy.components[componentName];
148
+ if (!daemon || daemon.version !== args.targetDaemonVersion) {
149
+ return {
150
+ valid: false,
151
+ eligible: false,
152
+ reason: `policy ${componentName} component does not bind the target artifact version`,
153
+ };
154
+ }
155
+ for (const [name, range] of Object.entries(daemon.contracts ?? {})) {
156
+ const actual = args.contracts[name];
157
+ if (!Number.isInteger(actual) || actual < range.minimum || actual > range.maximum) {
158
+ return {
159
+ valid: false,
160
+ eligible: false,
161
+ reason: `local contract ${name}=${String(actual)} is outside policy range ${range.minimum}..${range.maximum}`,
162
+ };
163
+ }
164
+ }
165
+ const eligible = !args.autonomous || isReleasePolicyCohortEligible(args.policy, args.cohortSubject);
166
+ return eligible
167
+ ? { valid: true, eligible: true }
168
+ : { valid: true, eligible: false, reason: 'machine is outside rollout cohort' };
169
+ }
@@ -0,0 +1,2 @@
1
+ /** SemVer precedence shared by every local release consumer. Invalid inputs are never ordered. */
2
+ export declare function compareReleaseVersions(a: string, b: string): -1 | 0 | 1 | null;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compareReleaseVersions = compareReleaseVersions;
4
+ /** SemVer precedence shared by every local release consumer. Invalid inputs are never ordered. */
5
+ function compareReleaseVersions(a, b) {
6
+ const parse = (value) => {
7
+ if (typeof value !== 'string')
8
+ return null;
9
+ const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(value.trim());
10
+ if (!match)
11
+ return null;
12
+ const prerelease = match[4]?.split('.') ?? [];
13
+ if (prerelease.some((part) => /^\d+$/.test(part) && part.length > 1 && part[0] === '0'))
14
+ return null;
15
+ return { core: match.slice(1, 4).map(BigInt), prerelease };
16
+ };
17
+ const left = parse(a);
18
+ const right = parse(b);
19
+ if (!left || !right)
20
+ return null;
21
+ for (let i = 0; i < 3; i++) {
22
+ if (left.core[i] !== right.core[i])
23
+ return left.core[i] < right.core[i] ? -1 : 1;
24
+ }
25
+ if (!left.prerelease.length || !right.prerelease.length) {
26
+ return left.prerelease.length === right.prerelease.length ? 0 : left.prerelease.length ? -1 : 1;
27
+ }
28
+ for (let i = 0; i < Math.max(left.prerelease.length, right.prerelease.length); i++) {
29
+ const l = left.prerelease[i];
30
+ const r = right.prerelease[i];
31
+ if (l === undefined || r === undefined)
32
+ return l === r ? 0 : l === undefined ? -1 : 1;
33
+ if (l === r)
34
+ continue;
35
+ const ln = /^\d+$/.test(l);
36
+ const rn = /^\d+$/.test(r);
37
+ if (ln && rn)
38
+ return BigInt(l) < BigInt(r) ? -1 : 1;
39
+ if (ln !== rn)
40
+ return ln ? -1 : 1;
41
+ return l < r ? -1 : 1;
42
+ }
43
+ return 0;
44
+ }
@@ -1,33 +1,5 @@
1
- /**
2
- * Session permission authority — the ONE rule that decides whether a session's
3
- * execution contract governs the run's permissions, shared by the server and
4
- * the daemon (OSK-11975 introduced it; it was implemented twice — the server
5
- * read `kind !== 'commitment'`, the daemon read the `permissionSource` marker —
6
- * which is exactly how a new contract kind or a new source value would drift
7
- * them apart).
8
- *
9
- * The wire model:
10
- *
11
- * - The contract is closed (`hasOnlyKeys` on the daemon), so "whose
12
- * permissions govern" can never ride INSIDE it. It is a top-level session
13
- * field: `permissionSource`.
14
- * - `'agent'` means the Agent's own configured permission mode, tool policy,
15
- * Grants, session env, MCP servers and machine consent govern the run,
16
- * exactly as for an unattended Task of the same Agent. Today only a
17
- * `commitment` contract may carry it.
18
- * - A `commitment` contract WITHOUT the marker (an older server mid-deploy)
19
- * still governs permissions — the marker is the version signal, so its
20
- * absence must keep the legacy fence, not silently lift it.
21
- * - `task_delivery` contracts always govern, marker or not.
22
- *
23
- * Server vs daemon asymmetry, on purpose: the server STAMPS the marker
24
- * (`permissionSourceForContract`), so on its own side the predicate reduces to
25
- * "a commitment never governs" — `contractGovernsPermissions(c,
26
- * permissionSourceForContract(c))` is exactly `kind !== 'commitment'`. The
27
- * daemon reads the marker it received, because it cannot distinguish "new
28
- * server, agent permissions" from "old server, contract fence" any other way.
29
- * Both call the same function; only the input differs.
30
- */
1
+ /** Commitment context never overrides Agent permissions. Version 3 admission
2
+ * rejects old contracts; typed Task delivery is a separate frozen operation. */
31
3
  /** The wire vocabulary for `permissionSource`. Top-level session field. */
32
4
  export declare const SESSION_PERMISSION_SOURCES: readonly ["agent"];
33
5
  export type SessionPermissionSource = (typeof SESSION_PERMISSION_SOURCES)[number];
@@ -37,7 +9,7 @@ export type SessionPermissionSource = (typeof SESSION_PERMISSION_SOURCES)[number
37
9
  * a daemon without it (`COMMITMENT_AGENT_PERMISSIONS_RUNTIME_UNSUPPORTED`), so
38
10
  * the marker never reaches a daemon that would ignore it.
39
11
  */
40
- export declare const COMMITMENT_AGENT_PERMISSIONS_CAPABILITY = "commitment_execution_contract_v2_agent_permissions";
12
+ export declare const COMMITMENT_AGENT_PERMISSIONS_CAPABILITY = "commitment_execution_contract_v3";
41
13
  /**
42
14
  * The minimal contract shape this rule reads. The predicate only ever needs
43
15
  * `kind` — keeping the input structural means this module never imports the
@@ -50,8 +22,7 @@ export type PermissionContractKind = {
50
22
  } | null | undefined;
51
23
  /**
52
24
  * Untrusted wire input: anything but an exact vocabulary literal reads as
53
- * absent. Never widen by coercion — an unrecognized future value must degrade
54
- * to "contract governs", the conservative answer.
25
+ * absent. An unknown marker carries no authority. Contract admission is validated separately.
55
26
  */
56
27
  export declare function normalizeSessionPermissionSource(value: unknown): SessionPermissionSource | undefined;
57
28
  /**
@@ -60,18 +31,10 @@ export declare function normalizeSessionPermissionSource(value: unknown): Sessio
60
31
  * kind keeps its own authority and stamps nothing.
61
32
  */
62
33
  export declare function permissionSourceForContract(contract: PermissionContractKind): SessionPermissionSource | undefined;
63
- /**
64
- * Whether the contract is the run's PERMISSION authority — the single place
65
- * this rule lives.
66
- *
67
- * True for a non-commitment contract regardless of marker, and for a
68
- * commitment whose server did not declare agent permissions. False for no
69
- * contract at all, and for a commitment carrying `permissionSource: 'agent'`,
70
- * which then follows the Agent's own permissions exactly as a Task does while
71
- * the contract keeps its non-permission roles (normalized shape, ephemeral
72
- * worktree, result validation, `GIT_OPTIONAL_LOCKS`, provider restriction).
34
+ /** Only typed Task delivery owns an effect-specific permission boundary.
35
+ * A Commitment is context for the Agent and cannot grant or restrict tools.
73
36
  */
74
- export declare function contractGovernsPermissions(contract: PermissionContractKind, permissionSource: unknown): boolean;
37
+ export declare function contractGovernsPermissions(contract: PermissionContractKind, _permissionSource?: unknown): boolean;
75
38
  /**
76
39
  * The contract when it governs permissions, else `undefined` — for the sites
77
40
  * that used `executionContract` as "is this run permission-fenced".
@@ -1,34 +1,6 @@
1
1
  "use strict";
2
- /**
3
- * Session permission authority — the ONE rule that decides whether a session's
4
- * execution contract governs the run's permissions, shared by the server and
5
- * the daemon (OSK-11975 introduced it; it was implemented twice — the server
6
- * read `kind !== 'commitment'`, the daemon read the `permissionSource` marker —
7
- * which is exactly how a new contract kind or a new source value would drift
8
- * them apart).
9
- *
10
- * The wire model:
11
- *
12
- * - The contract is closed (`hasOnlyKeys` on the daemon), so "whose
13
- * permissions govern" can never ride INSIDE it. It is a top-level session
14
- * field: `permissionSource`.
15
- * - `'agent'` means the Agent's own configured permission mode, tool policy,
16
- * Grants, session env, MCP servers and machine consent govern the run,
17
- * exactly as for an unattended Task of the same Agent. Today only a
18
- * `commitment` contract may carry it.
19
- * - A `commitment` contract WITHOUT the marker (an older server mid-deploy)
20
- * still governs permissions — the marker is the version signal, so its
21
- * absence must keep the legacy fence, not silently lift it.
22
- * - `task_delivery` contracts always govern, marker or not.
23
- *
24
- * Server vs daemon asymmetry, on purpose: the server STAMPS the marker
25
- * (`permissionSourceForContract`), so on its own side the predicate reduces to
26
- * "a commitment never governs" — `contractGovernsPermissions(c,
27
- * permissionSourceForContract(c))` is exactly `kind !== 'commitment'`. The
28
- * daemon reads the marker it received, because it cannot distinguish "new
29
- * server, agent permissions" from "old server, contract fence" any other way.
30
- * Both call the same function; only the input differs.
31
- */
2
+ /** Commitment context never overrides Agent permissions. Version 3 admission
3
+ * rejects old contracts; typed Task delivery is a separate frozen operation. */
32
4
  Object.defineProperty(exports, "__esModule", { value: true });
33
5
  exports.COMMITMENT_AGENT_PERMISSIONS_CAPABILITY = exports.SESSION_PERMISSION_SOURCES = void 0;
34
6
  exports.normalizeSessionPermissionSource = normalizeSessionPermissionSource;
@@ -43,11 +15,10 @@ exports.SESSION_PERMISSION_SOURCES = ['agent'];
43
15
  * a daemon without it (`COMMITMENT_AGENT_PERMISSIONS_RUNTIME_UNSUPPORTED`), so
44
16
  * the marker never reaches a daemon that would ignore it.
45
17
  */
46
- exports.COMMITMENT_AGENT_PERMISSIONS_CAPABILITY = 'commitment_execution_contract_v2_agent_permissions';
18
+ exports.COMMITMENT_AGENT_PERMISSIONS_CAPABILITY = 'commitment_execution_contract_v3';
47
19
  /**
48
20
  * Untrusted wire input: anything but an exact vocabulary literal reads as
49
- * absent. Never widen by coercion — an unrecognized future value must degrade
50
- * to "contract governs", the conservative answer.
21
+ * absent. An unknown marker carries no authority. Contract admission is validated separately.
51
22
  */
52
23
  function normalizeSessionPermissionSource(value) {
53
24
  return exports.SESSION_PERMISSION_SOURCES.includes(value)
@@ -62,21 +33,11 @@ function normalizeSessionPermissionSource(value) {
62
33
  function permissionSourceForContract(contract) {
63
34
  return contract?.kind === 'commitment' ? 'agent' : undefined;
64
35
  }
65
- /**
66
- * Whether the contract is the run's PERMISSION authority — the single place
67
- * this rule lives.
68
- *
69
- * True for a non-commitment contract regardless of marker, and for a
70
- * commitment whose server did not declare agent permissions. False for no
71
- * contract at all, and for a commitment carrying `permissionSource: 'agent'`,
72
- * which then follows the Agent's own permissions exactly as a Task does while
73
- * the contract keeps its non-permission roles (normalized shape, ephemeral
74
- * worktree, result validation, `GIT_OPTIONAL_LOCKS`, provider restriction).
36
+ /** Only typed Task delivery owns an effect-specific permission boundary.
37
+ * A Commitment is context for the Agent and cannot grant or restrict tools.
75
38
  */
76
- function contractGovernsPermissions(contract, permissionSource) {
77
- if (!contract)
78
- return false;
79
- return !(contract.kind === 'commitment' && normalizeSessionPermissionSource(permissionSource) === 'agent');
39
+ function contractGovernsPermissions(contract, _permissionSource) {
40
+ return Boolean(contract && contract.kind !== 'commitment');
80
41
  }
81
42
  /**
82
43
  * The contract when it governs permissions, else `undefined` — for the sites