@skrr-ai/cli 0.1.51 → 0.1.53

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 (67) hide show
  1. package/dist/base-command.d.ts +7 -0
  2. package/dist/base-command.js +38 -8
  3. package/dist/commands/agents/chat.js +2 -1
  4. package/dist/commands/agents/create.js +21 -8
  5. package/dist/commands/agents/update.d.ts +55 -0
  6. package/dist/commands/agents/update.js +119 -23
  7. package/dist/commands/commitments/next-tick.js +9 -1
  8. package/dist/commands/context.js +2 -1
  9. package/dist/commands/daemon/config.js +16 -1
  10. package/dist/commands/doctor.js +8 -1
  11. package/dist/commands/login.d.ts +2 -1
  12. package/dist/commands/login.js +26 -7
  13. package/dist/commands/logout.js +3 -0
  14. package/dist/commands/machines/dedicated/show.js +25 -2
  15. package/dist/commands/tasks/approvals/list.d.ts +22 -0
  16. package/dist/commands/tasks/approvals/list.js +80 -3
  17. package/dist/commands/tasks/create.d.ts +5 -0
  18. package/dist/commands/tasks/create.js +28 -7
  19. package/dist/commands/tasks/start.d.ts +14 -0
  20. package/dist/commands/tasks/start.js +31 -1
  21. package/dist/commands/tasks/update.js +30 -3
  22. package/dist/commands/whoami.js +5 -3
  23. package/dist/lib/agent-config.d.ts +15 -0
  24. package/dist/lib/agent-config.js +46 -10
  25. package/dist/lib/agentic-stream.d.ts +21 -1
  26. package/dist/lib/agentic-stream.js +40 -6
  27. package/dist/lib/auth-failure.js +4 -1
  28. package/dist/lib/auth-storage.d.ts +24 -0
  29. package/dist/lib/auth-storage.js +348 -13
  30. package/dist/lib/cli-id.d.ts +22 -0
  31. package/dist/lib/cli-id.js +56 -0
  32. package/dist/lib/cli-identity.d.ts +49 -0
  33. package/dist/lib/cli-identity.js +159 -0
  34. package/dist/lib/commitments.d.ts +4 -0
  35. package/dist/lib/commitments.js +12 -1
  36. package/dist/lib/config.d.ts +17 -0
  37. package/dist/lib/config.js +15 -3
  38. package/dist/lib/cred-envelope.d.ts +12 -1
  39. package/dist/lib/cred-envelope.js +20 -3
  40. package/dist/lib/daemonBroker.d.ts +26 -4
  41. package/dist/lib/daemonBroker.js +82 -10
  42. package/dist/lib/daemonBrokerRefusal.d.ts +51 -0
  43. package/dist/lib/daemonBrokerRefusal.js +127 -0
  44. package/dist/lib/dedicated-machines.d.ts +53 -4
  45. package/dist/lib/dedicated-machines.js +95 -5
  46. package/dist/lib/delegated-cli.js +16 -0
  47. package/dist/lib/first-party-harness-broker.js +6 -4
  48. package/dist/lib/keychain.d.ts +39 -0
  49. package/dist/lib/keychain.js +254 -40
  50. package/dist/lib/label-ref.d.ts +34 -0
  51. package/dist/lib/label-ref.js +72 -1
  52. package/dist/lib/login.js +24 -12
  53. package/dist/lib/oauthLogin.js +2 -8
  54. package/dist/lib/refresh.js +6 -2
  55. package/dist/lib/tasks.js +33 -8
  56. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.d.ts +13 -1
  57. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.js +9 -3
  58. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  59. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +4 -3
  60. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.d.ts +13 -1
  61. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.js +8 -3
  62. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  63. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
  64. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  65. package/dist/node_modules/@skrr-ai/data-provider/index.js +3138 -3100
  66. package/oclif.manifest.json +26470 -26449
  67. package/package.json +1 -1
@@ -27,6 +27,7 @@ Object.defineProperty(exports, "PermanentAuthFailure", { enumerable: true, get:
27
27
  Object.defineProperty(exports, "TransientAuthFailure", { enumerable: true, get: function () { return auth_core_1.TransientAuthFailure; } });
28
28
  const auth_storage_1 = require("./auth-storage");
29
29
  const config_1 = require("./config");
30
+ const cli_id_1 = require("./cli-id");
30
31
  const credential_resolver_1 = require("./credential-resolver");
31
32
  const device_identity_1 = require("./device-identity");
32
33
  exports.CLI_REFRESH_EARLY_MS = 5 * 60 * 1000;
@@ -182,7 +183,10 @@ async function ensureFreshCliCredential(opts = {}) {
182
183
  */
183
184
  async function refreshCliToken() {
184
185
  const cfg = (0, config_1.loadConfig)();
185
- if (!cfg.cliId) {
186
+ // The id the stored credential was minted under — the machine id, or this
187
+ // profile's own (OSK-12156). One resolver, so refresh cannot disagree with login.
188
+ const cliId = (0, cli_id_1.resolveCliId)(cfg);
189
+ if (!cliId) {
186
190
  throw new CliRefreshUnavailableError('No CLI identity persisted (cliId). Run `skrr login` to initialize.');
187
191
  }
188
192
  const origin = getCliOrigin();
@@ -195,7 +199,7 @@ async function refreshCliToken() {
195
199
  if (!bundle?.refreshToken) {
196
200
  throw new CliRefreshUnavailableError('No refresh token available — sign in with `skrr login`.');
197
201
  }
198
- const pair = await (0, auth_core_1.refreshDaemonToken)(serverUrl, bundle.refreshToken, cfg.cliId,
202
+ const pair = await (0, auth_core_1.refreshDaemonToken)(serverUrl, bundle.refreshToken, cliId,
199
203
  // L11 Phase 3 — attach a device-identity proof to every refresh.
200
204
  // The signer returns null when device identity isn't initialized
201
205
  // (Phase 1 binary, KEK rotation hot path), and the server's lenient
package/dist/lib/tasks.js CHANGED
@@ -587,14 +587,24 @@ async function resolveTaskCreateScope(payload, opts = {}) {
587
587
  payload.spaceId = parentSpaceId;
588
588
  }
589
589
  const projectId = stringValue(payload.projectId);
590
- if (projectId && !stringValue(payload.spaceId)) {
590
+ // A Mongo `_id` is resolved even beside --space: the server addresses a
591
+ // project by its `id` or its ref only, so passing the `_id` through would
592
+ // fail there with less to say about it (OSK-12054).
593
+ if (projectId && (!stringValue(payload.spaceId) || looksLikeStorageId(projectId))) {
591
594
  const project = await getCachedProject(projectId, opts.projectCache);
592
595
  resolution.project = project;
593
- const projectSpaceId = stringValue(project.spaceId);
594
- if (!projectSpaceId) {
595
- throw new Error(`Project ${projectId} did not return an owning space`);
596
+ // Accepting a second spelling obliges returning the canonical one: the
597
+ // write carries `id`, never whatever the caller happened to paste.
598
+ const canonicalId = stringValue(project.id);
599
+ if (canonicalId && canonicalId !== projectId)
600
+ payload.projectId = canonicalId;
601
+ if (!stringValue(payload.spaceId)) {
602
+ const projectSpaceId = stringValue(project.spaceId);
603
+ if (!projectSpaceId) {
604
+ throw new Error(`Project ${projectId} did not return an owning space`);
605
+ }
606
+ payload.spaceId = projectSpaceId;
596
607
  }
597
- payload.spaceId = projectSpaceId;
598
608
  }
599
609
  return resolution;
600
610
  }
@@ -894,21 +904,36 @@ async function getCachedTask(taskIdValue, cache) {
894
904
  }
895
905
  return cache.get(taskIdValue);
896
906
  }
907
+ /** A 24-hex Mongo ObjectId — the storage `_id` entity JSON prints beside `id`. */
908
+ const STORAGE_ID_RE = /^[0-9a-f]{24}$/i;
909
+ function looksLikeStorageId(value) {
910
+ return STORAGE_ID_RE.test(value);
911
+ }
912
+ /**
913
+ * Entity JSON prints both `id` (canonical) and `_id` (storage) with nothing
914
+ * saying which one commands take, so a pasted `_id` is accepted as the same
915
+ * project rather than refused as unknown (OSK-12054). The caller then writes
916
+ * the canonical `id` — see `resolveTaskCreateScope`.
917
+ */
918
+ function projectMatchesId(project, ref) {
919
+ return String(project.id ?? '') === ref || String(project._id ?? '') === ref;
920
+ }
897
921
  async function getCachedProject(projectId, cache) {
898
922
  const load = async () => {
899
923
  const filtered = await projects_1.projectApi.search({ q: projectId, limit: 20 });
900
- let exact = (0, projects_1.projectsFrom)(filtered).filter((project) => String(project.id ?? project._id ?? '') === projectId);
924
+ let exact = (0, projects_1.projectsFrom)(filtered).filter((project) => projectMatchesId(project, projectId));
901
925
  // The cross-space endpoint's `q` contract covers title/description/ref,
902
926
  // not UUID. Fall back to the bounded visible project catalog so canonical
903
927
  // IDs still resolve without inventing a second project read endpoint.
904
928
  if (exact.length === 0) {
905
929
  const catalog = await projects_1.projectApi.search({ limit: 200 });
906
- exact = (0, projects_1.projectsFrom)(catalog).filter((project) => String(project.id ?? project._id ?? '') === projectId);
930
+ exact = (0, projects_1.projectsFrom)(catalog).filter((project) => projectMatchesId(project, projectId));
907
931
  }
908
932
  if (exact.length !== 1) {
909
933
  throw new Error(exact.length > 1
910
934
  ? `Project id ${projectId} matched multiple projects`
911
- : `Project id ${projectId} was not found; pass a canonical project id or add --space`);
935
+ : `Project ${projectId} was not found among the projects you can see. Pass the ` +
936
+ "project's `id` (the uuid `projects list` prints), or its ref (OSK-P-5) together with --space");
912
937
  }
913
938
  return exact[0];
914
939
  };
@@ -141,7 +141,19 @@ export interface ReadTransformResult {
141
141
  * path is active — caller should re-write to upgrade. */
142
142
  needsMigration: boolean;
143
143
  }
144
- export declare function maybeDecryptOnRead(stored: string): ReadTransformResult;
144
+ /** What the caller knows about the stored value that the envelope does not. */
145
+ export interface DecryptOnReadOptions {
146
+ /**
147
+ * Replaces the default `decrypt_failed` hint. The default says a re-login
148
+ * rebuilds the credential, which is true only of THIS config's own value. A
149
+ * caller reading a value another config sealed under its own key knows the
150
+ * failure is expected and a re-login here changes nothing (OSK-12135).
151
+ */
152
+ unreadableHint?: string;
153
+ }
154
+ /** The `decrypt_failed` hint when the caller has no better explanation. */
155
+ export declare const DEFAULT_UNREADABLE_CREDENTIAL_HINT = "stored credential is unreadable on this machine \u2014 signing in again rebuilds it";
156
+ export declare function maybeDecryptOnRead(stored: string, options?: DecryptOnReadOptions): ReadTransformResult;
145
157
  /** @internal Test seam — wrapped-DEK path resolver. */
146
158
  export declare function __wrappedDekFilePathForTest(profile: string): string;
147
159
  export {};
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_UNREADABLE_CREDENTIAL_HINT = void 0;
6
7
  exports.__setStateForTest = __setStateForTest;
7
8
  exports.__getStateForTest = __getStateForTest;
8
9
  exports.__clearWarnDedupeForTest = __clearWarnDedupeForTest;
@@ -165,7 +166,10 @@ function emit(eventName, success, metadata) {
165
166
  metadata: { event: eventName, ...metadata },
166
167
  });
167
168
  if (!success) {
168
- const signature = `${eventName}:${metadata.kekId ?? ''}:${metadata.kekKind ?? ''}:${metadata.message ?? ''}`;
169
+ // The hint is part of the signature: the same crypto failure on this
170
+ // config's own credential and on another config's is two different facts,
171
+ // and the second must not swallow the first (OSK-12135).
172
+ const signature = `${eventName}:${metadata.kekId ?? ''}:${metadata.kekKind ?? ''}:${metadata.message ?? ''}:${metadata.hint ?? ''}`;
169
173
  if (!warnedSignatures.has(signature)) {
170
174
  if (warnedSignatures.size >= MAX_WARNED_SIGNATURES)
171
175
  warnedSignatures.clear();
@@ -755,7 +759,9 @@ function wrapIfActiveOrPassthrough(plaintext) {
755
759
  return plaintext;
756
760
  }
757
761
  }
758
- function maybeDecryptOnRead(stored) {
762
+ /** The `decrypt_failed` hint when the caller has no better explanation. */
763
+ exports.DEFAULT_UNREADABLE_CREDENTIAL_HINT = 'stored credential is unreadable on this machine — signing in again rebuilds it';
764
+ function maybeDecryptOnRead(stored, options = {}) {
759
765
  if (typeof stored !== 'string' || stored.length === 0) {
760
766
  return { plaintext: stored, needsMigration: false };
761
767
  }
@@ -787,7 +793,7 @@ function maybeDecryptOnRead(stored) {
787
793
  // The crypto error alone reads as a crash; the actionable truth is
788
794
  // that this stored credential is unreadable by THIS binary (usually
789
795
  // a signing-identity or profile drift) and re-login rebuilds it.
790
- hint: 'stored credential is unreadable on this machine — signing in again rebuilds it',
796
+ hint: options.unreadableHint ?? exports.DEFAULT_UNREADABLE_CREDENTIAL_HINT,
791
797
  });
792
798
  return { plaintext: null, needsMigration: false };
793
799
  }
@@ -27,7 +27,7 @@ export { formatRecoveryCode, parseRecoveryCode, normalizeRecoveryCode, isValidRe
27
27
  export { buildCredentialAad, decrypt as credEnvelopeDecrypt, deserialize as credEnvelopeDeserialize, encrypt as credEnvelopeEncrypt, ENVELOPE_MAGIC as CRED_ENVELOPE_MAGIC, generateDek, isEnvelopeString as isCredEnvelopeString, KEY_LEN as CRED_KEY_LEN, openBuffer as credOpenBuffer, sealBuffer as credSealBuffer, serialize as credEnvelopeSerialize, unwrap as credEnvelopeUnwrap, wrap as credEnvelopeWrap, type SealedBlob as CredSealedBlob, } from './credentialEnvelope.js';
28
28
  export { __setKekStrategyForTest, getKekStrategy, InMemoryKek, UnavailableKek, KekUnavailableError, LinuxKek, MacosKek, registerZeroizeHook, WindowsKek, zeroizeKekCaches, type KekStrategy, } from './kek/index.js';
29
29
  export { buildProof as buildDeviceProof, generateDeviceKeyPair, jwkThumbprint, normalizeHtu, publicJwkFromX, tokenHash, verifyProof as verifyDeviceProof, type DeviceKeyPair, type DeviceProofClaims, type DeviceProofHeader, type Ed25519PrivateJwk, type Ed25519PublicJwk, type VerifyFailureReason as DeviceProofVerifyFailureReason, type VerifyFailure as DeviceProofVerifyFailure, type VerifyOptions as DeviceProofVerifyOptions, type VerifySuccess as DeviceProofVerifySuccess, } from './deviceKey.js';
30
- export { initCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, maybeEncryptForWrite, maybeDecryptOnRead, __wrappedDekFilePathForTest, __setStateForTest as __setCredEnvelopeStateForTest, __getStateForTest as __getCredEnvelopeStateForTest, __resetShutdownGuardForTest, type ReadTransformResult, } from './credentialEnvelopeBridge.js';
30
+ export { initCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, maybeEncryptForWrite, maybeDecryptOnRead, DEFAULT_UNREADABLE_CREDENTIAL_HINT, type DecryptOnReadOptions, __wrappedDekFilePathForTest, __setStateForTest as __setCredEnvelopeStateForTest, __getStateForTest as __getCredEnvelopeStateForTest, __resetShutdownGuardForTest, type ReadTransformResult, } from './credentialEnvelopeBridge.js';
31
31
  export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, type EnrollmentResult, } from './deviceIdentityBridge.js';
32
32
  export { getMachineUuid, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
33
33
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
@@ -24,9 +24,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.generateVerifier = exports.invalidateHeadersHelperCache = exports.runHeadersHelper = exports.SENTINEL_TRIGGERING_FAILURES = exports.__resetAuthHelperFailureReasonForTest = exports.getLastAuthHelperFailureReason = exports.getDefaultHelperTtlMs = exports.invokeAuthHelper = exports.AuthHelperUnavailableError = exports.AuthHelperUntrustedError = exports.CI_TOKEN_PREFIX = exports.classifyTokenKind = exports.invalidateAuthHelperCache = exports.__resetCredentialResolverForTest = exports.CREDENTIAL_PRECEDENCE = exports.CredentialResolver = exports.__setFdReaderForTest = exports.readAuthFromFd = exports.createRefreshScheduler = exports.UNKNOWN_401_BUDGET = exports.__resetUnknown401StreaksForTest = exports.__refreshDiagnostics = exports.installLockExitHandlers = exports.clearReauthState = exports.needsReauthState = exports.persistReauthState = exports.withAuthLock = exports.revokeDaemonRefreshSession = exports.refreshDaemonToken = exports.classifyRefreshResponse = exports.EXIT_NEEDS_REAUTH = exports.reauthCommand = exports.formatReauthMessage = exports.handleMidStreamAuthExpiry = exports.isAuthExpiredSseEvent = exports.decodeJwtPayload = exports.decodeJwtExpiry = exports.TransientAuthFailure = exports.PermanentAuthFailure = exports.emitAuthTelemetry = exports.getAuthBinaryName = exports.getAuthKillSwitches = exports.getAuthConfigDir = exports.getAuthLogger = exports.configureAuthCore = exports.OAUTH_CLI_SCOPE = exports.isDaemonScope = exports.VALID_DAEMON_SCOPE_SET = exports.ALL_DAEMON_SCOPES = exports.SCOPES = void 0;
26
26
  exports.publicJwkFromX = exports.normalizeHtu = 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 = void 0;
27
- 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.getMachineUuid = exports.__getDeviceIdentityStateForTest = exports.__setDeviceIdentityStateForTest = exports.__enrollmentBackoffPathForTest = exports.__enrollmentMarkerPathForTest = exports.__privateKeyPathForTest = exports.__publicKeyPathForTest = 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.maybeDecryptOnRead = exports.maybeEncryptForWrite = exports.describeCredEnvelopeState = exports.hasCredEnvelopePathConflict = exports.isCredEnvelopeActive = exports.shutdownCredEnvelope = exports.resetCredEnvelope = exports.initCredEnvelope = exports.verifyDeviceProof = exports.tokenHash = void 0;
28
- 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 = 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.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 = 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 = void 0;
27
+ 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.getMachineUuid = exports.__getDeviceIdentityStateForTest = exports.__setDeviceIdentityStateForTest = exports.__enrollmentBackoffPathForTest = exports.__enrollmentMarkerPathForTest = exports.__privateKeyPathForTest = exports.__publicKeyPathForTest = 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.describeCredEnvelopeState = exports.hasCredEnvelopePathConflict = exports.isCredEnvelopeActive = exports.shutdownCredEnvelope = exports.resetCredEnvelope = exports.initCredEnvelope = exports.verifyDeviceProof = exports.tokenHash = void 0;
28
+ 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 = 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.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 = 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 = 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
@@ -234,6 +234,7 @@ Object.defineProperty(exports, "hasCredEnvelopePathConflict", { enumerable: true
234
234
  Object.defineProperty(exports, "describeCredEnvelopeState", { enumerable: true, get: function () { return credentialEnvelopeBridge_js_1.describeCredEnvelopeState; } });
235
235
  Object.defineProperty(exports, "maybeEncryptForWrite", { enumerable: true, get: function () { return credentialEnvelopeBridge_js_1.maybeEncryptForWrite; } });
236
236
  Object.defineProperty(exports, "maybeDecryptOnRead", { enumerable: true, get: function () { return credentialEnvelopeBridge_js_1.maybeDecryptOnRead; } });
237
+ Object.defineProperty(exports, "DEFAULT_UNREADABLE_CREDENTIAL_HINT", { enumerable: true, get: function () { return credentialEnvelopeBridge_js_1.DEFAULT_UNREADABLE_CREDENTIAL_HINT; } });
237
238
  // Test seams — `__`-prefixed so consumers don't import them in
238
239
  // production code paths. Re-exported here so CLI/daemon spec files
239
240
  // can resolve the wrapped-DEK path against the same module instance.
@@ -141,7 +141,19 @@ export interface ReadTransformResult {
141
141
  * path is active — caller should re-write to upgrade. */
142
142
  needsMigration: boolean;
143
143
  }
144
- export declare function maybeDecryptOnRead(stored: string): ReadTransformResult;
144
+ /** What the caller knows about the stored value that the envelope does not. */
145
+ export interface DecryptOnReadOptions {
146
+ /**
147
+ * Replaces the default `decrypt_failed` hint. The default says a re-login
148
+ * rebuilds the credential, which is true only of THIS config's own value. A
149
+ * caller reading a value another config sealed under its own key knows the
150
+ * failure is expected and a re-login here changes nothing (OSK-12135).
151
+ */
152
+ unreadableHint?: string;
153
+ }
154
+ /** The `decrypt_failed` hint when the caller has no better explanation. */
155
+ export declare const DEFAULT_UNREADABLE_CREDENTIAL_HINT = "stored credential is unreadable on this machine \u2014 signing in again rebuilds it";
156
+ export declare function maybeDecryptOnRead(stored: string, options?: DecryptOnReadOptions): ReadTransformResult;
145
157
  /** @internal Test seam — wrapped-DEK path resolver. */
146
158
  export declare function __wrappedDekFilePathForTest(profile: string): string;
147
159
  export {};
@@ -146,7 +146,10 @@ function emit(eventName, success, metadata) {
146
146
  metadata: { event: eventName, ...metadata },
147
147
  });
148
148
  if (!success) {
149
- const signature = `${eventName}:${metadata.kekId ?? ''}:${metadata.kekKind ?? ''}:${metadata.message ?? ''}`;
149
+ // The hint is part of the signature: the same crypto failure on this
150
+ // config's own credential and on another config's is two different facts,
151
+ // and the second must not swallow the first (OSK-12135).
152
+ const signature = `${eventName}:${metadata.kekId ?? ''}:${metadata.kekKind ?? ''}:${metadata.message ?? ''}:${metadata.hint ?? ''}`;
150
153
  if (!warnedSignatures.has(signature)) {
151
154
  if (warnedSignatures.size >= MAX_WARNED_SIGNATURES)
152
155
  warnedSignatures.clear();
@@ -736,7 +739,9 @@ export function wrapIfActiveOrPassthrough(plaintext) {
736
739
  return plaintext;
737
740
  }
738
741
  }
739
- export function maybeDecryptOnRead(stored) {
742
+ /** The `decrypt_failed` hint when the caller has no better explanation. */
743
+ export const DEFAULT_UNREADABLE_CREDENTIAL_HINT = 'stored credential is unreadable on this machine — signing in again rebuilds it';
744
+ export function maybeDecryptOnRead(stored, options = {}) {
740
745
  if (typeof stored !== 'string' || stored.length === 0) {
741
746
  return { plaintext: stored, needsMigration: false };
742
747
  }
@@ -768,7 +773,7 @@ export function maybeDecryptOnRead(stored) {
768
773
  // The crypto error alone reads as a crash; the actionable truth is
769
774
  // that this stored credential is unreadable by THIS binary (usually
770
775
  // a signing-identity or profile drift) and re-login rebuilds it.
771
- hint: 'stored credential is unreadable on this machine — signing in again rebuilds it',
776
+ hint: options.unreadableHint ?? DEFAULT_UNREADABLE_CREDENTIAL_HINT,
772
777
  });
773
778
  return { plaintext: null, needsMigration: false };
774
779
  }
@@ -27,7 +27,7 @@ export { formatRecoveryCode, parseRecoveryCode, normalizeRecoveryCode, isValidRe
27
27
  export { buildCredentialAad, decrypt as credEnvelopeDecrypt, deserialize as credEnvelopeDeserialize, encrypt as credEnvelopeEncrypt, ENVELOPE_MAGIC as CRED_ENVELOPE_MAGIC, generateDek, isEnvelopeString as isCredEnvelopeString, KEY_LEN as CRED_KEY_LEN, openBuffer as credOpenBuffer, sealBuffer as credSealBuffer, serialize as credEnvelopeSerialize, unwrap as credEnvelopeUnwrap, wrap as credEnvelopeWrap, type SealedBlob as CredSealedBlob, } from './credentialEnvelope.js';
28
28
  export { __setKekStrategyForTest, getKekStrategy, InMemoryKek, UnavailableKek, KekUnavailableError, LinuxKek, MacosKek, registerZeroizeHook, WindowsKek, zeroizeKekCaches, type KekStrategy, } from './kek/index.js';
29
29
  export { buildProof as buildDeviceProof, generateDeviceKeyPair, jwkThumbprint, normalizeHtu, publicJwkFromX, tokenHash, verifyProof as verifyDeviceProof, type DeviceKeyPair, type DeviceProofClaims, type DeviceProofHeader, type Ed25519PrivateJwk, type Ed25519PublicJwk, type VerifyFailureReason as DeviceProofVerifyFailureReason, type VerifyFailure as DeviceProofVerifyFailure, type VerifyOptions as DeviceProofVerifyOptions, type VerifySuccess as DeviceProofVerifySuccess, } from './deviceKey.js';
30
- export { initCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, maybeEncryptForWrite, maybeDecryptOnRead, __wrappedDekFilePathForTest, __setStateForTest as __setCredEnvelopeStateForTest, __getStateForTest as __getCredEnvelopeStateForTest, __resetShutdownGuardForTest, type ReadTransformResult, } from './credentialEnvelopeBridge.js';
30
+ export { initCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, maybeEncryptForWrite, maybeDecryptOnRead, DEFAULT_UNREADABLE_CREDENTIAL_HINT, type DecryptOnReadOptions, __wrappedDekFilePathForTest, __setStateForTest as __setCredEnvelopeStateForTest, __getStateForTest as __getCredEnvelopeStateForTest, __resetShutdownGuardForTest, type ReadTransformResult, } from './credentialEnvelopeBridge.js';
31
31
  export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, type EnrollmentResult, } from './deviceIdentityBridge.js';
32
32
  export { getMachineUuid, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
33
33
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
@@ -108,7 +108,7 @@ export { buildProof as buildDeviceProof, generateDeviceKeyPair, jwkThumbprint, n
108
108
  // Daemon and CLI consume these; the underlying primitives live above.
109
109
  export {
110
110
  // credEnvelope bridge
111
- initCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, maybeEncryptForWrite, maybeDecryptOnRead,
111
+ initCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, maybeEncryptForWrite, maybeDecryptOnRead, DEFAULT_UNREADABLE_CREDENTIAL_HINT,
112
112
  // Test seams — `__`-prefixed so consumers don't import them in
113
113
  // production code paths. Re-exported here so CLI/daemon spec files
114
114
  // can resolve the wrapped-DEK path against the same module instance.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skrr-ai/auth-core",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "main": "dist/cjs/index.js",
5
5
  "types": "dist/esm/index.d.ts",
6
6
  "exports": {