@skrr-ai/cli 0.1.45 → 0.1.47

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 (73) hide show
  1. package/dist/base-command.d.ts +13 -1
  2. package/dist/base-command.js +37 -3
  3. package/dist/commands/agents/actions/create.js +11 -4
  4. package/dist/commands/browser/cloud-agents.js +4 -0
  5. package/dist/commands/code/handover.d.ts +11 -0
  6. package/dist/commands/code/handover.js +104 -1
  7. package/dist/commands/code/install.js +31 -0
  8. package/dist/commands/code/update.d.ts +46 -0
  9. package/dist/commands/code/update.js +242 -0
  10. package/dist/commands/commitments/handover.d.ts +29 -0
  11. package/dist/commands/commitments/handover.js +99 -0
  12. package/dist/commands/convos/handover.d.ts +1 -0
  13. package/dist/commands/convos/handover.js +4 -0
  14. package/dist/commands/daemon/index.js +2 -0
  15. package/dist/commands/daemon/update.d.ts +19 -0
  16. package/dist/commands/daemon/update.js +48 -0
  17. package/dist/commands/followups/watch.js +40 -5
  18. package/dist/commands/spaces/briefs/get.js +8 -0
  19. package/dist/commands/spaces/briefs/list.js +2 -0
  20. package/dist/commands/spaces/briefs/set.d.ts +3 -0
  21. package/dist/commands/spaces/briefs/set.js +78 -13
  22. package/dist/commands/spaces/expertise-suggestions.d.ts +12 -0
  23. package/dist/commands/spaces/expertise-suggestions.js +71 -0
  24. package/dist/commands/spaces/members/add.d.ts +1 -0
  25. package/dist/commands/spaces/members/add.js +11 -3
  26. package/dist/commands/spaces/members/list.js +11 -0
  27. package/dist/commands/tasks/assign.d.ts +9 -0
  28. package/dist/commands/tasks/assign.js +108 -1
  29. package/dist/commands/tasks/fork.d.ts +32 -0
  30. package/dist/commands/tasks/fork.js +171 -0
  31. package/dist/commands/tasks/handover.d.ts +23 -0
  32. package/dist/commands/tasks/handover.js +205 -0
  33. package/dist/commands/tasks/promote.d.ts +23 -0
  34. package/dist/commands/tasks/promote.js +82 -0
  35. package/dist/commands/tasks/runs.d.ts +16 -0
  36. package/dist/commands/tasks/runs.js +46 -1
  37. package/dist/commands/tasks/show.d.ts +23 -0
  38. package/dist/commands/tasks/show.js +59 -0
  39. package/dist/lib/api-fetch.d.ts +7 -1
  40. package/dist/lib/api-fetch.js +9 -2
  41. package/dist/lib/assignee-resolver.d.ts +22 -0
  42. package/dist/lib/assignee-resolver.js +26 -0
  43. package/dist/lib/code-handover.d.ts +50 -0
  44. package/dist/lib/code-handover.js +135 -0
  45. package/dist/lib/commitments.d.ts +22 -0
  46. package/dist/lib/commitments.js +42 -0
  47. package/dist/lib/dedicated-wait.js +22 -1
  48. package/dist/lib/followups.d.ts +18 -0
  49. package/dist/lib/followups.js +44 -0
  50. package/dist/lib/node-adapter.js +6 -0
  51. package/dist/lib/task-extras.d.ts +44 -0
  52. package/dist/lib/task-extras.js +73 -0
  53. package/dist/lib/update-check.d.ts +80 -7
  54. package/dist/lib/update-check.js +365 -33
  55. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +16 -1
  56. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +15 -0
  57. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.js +45 -0
  58. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessSkillLayout.d.ts +65 -0
  59. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessSkillLayout.js +99 -0
  60. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -0
  61. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +10 -2
  62. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +16 -1
  63. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +15 -0
  64. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.js +45 -0
  65. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessSkillLayout.d.ts +65 -0
  66. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessSkillLayout.js +94 -0
  67. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -0
  68. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +5 -0
  69. package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
  70. package/dist/node_modules/@skrr-ai/data-provider/index.js +2262 -2241
  71. package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
  72. package/oclif.manifest.json +34562 -33947
  73. package/package.json +2 -2
@@ -64,6 +64,13 @@ const firstPartyHarness_js_1 = require("./firstPartyHarness.js");
64
64
  function olderSpellings() {
65
65
  return (0, firstPartyHarness_js_1.firstPartyHarnessArtifactSpellings)().filter((spelling) => spelling !== firstPartyHarness_js_1.FIRST_PARTY_HARNESS.provider);
66
66
  }
67
+ /**
68
+ * Entries rescued from a legacy home when the canonical one already exists as a
69
+ * shell. `bin` is what managed resolution needs; `skills` is the user's own
70
+ * data. Deliberately a short, named list rather than "everything": a merge this
71
+ * module cannot enumerate (there is no readdir here) would be guessing.
72
+ */
73
+ const STRANDED_HOME_ENTRIES = ['bin', 'skills'];
67
74
  /** Suffix of the rollback copy the installer keeps beside the binary. */
68
75
  exports.FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX = '.previous';
69
76
  /**
@@ -186,6 +193,44 @@ function migrateFirstPartyHarnessHome(options = {}) {
186
193
  break;
187
194
  }
188
195
  }
196
+ else if (isRealDirectory(fs.lstat(canonicalHome))) {
197
+ // 1b. The canonical home EXISTS, so the rename above is impossible — but it
198
+ // may be a shell rather than the real home. A managed write that reaches
199
+ // `<root>/<appName>` before this migration runs creates the directory, and
200
+ // from then on step 1 skips forever and the legacy home keeps the binary.
201
+ // Observed on a real machine: `<root>/<appName>` holding a 2-byte `{}`
202
+ // config written hours after `<root>/<legacy appName>/bin` received the
203
+ // signed 107MB engine, which left managed resolution pointing at a bin
204
+ // directory that did not exist and every managed session with no engine to
205
+ // spawn.
206
+ //
207
+ // A rename cannot merge two directories, and this module has no readdir, so
208
+ // this moves the two entries that carry weight and nothing else: `bin`,
209
+ // which is what resolution needs, and `skills`, which is user data. An
210
+ // entry already present at the canonical home is never touched — the shell
211
+ // may be empty, but it is not automatically wrong.
212
+ for (const legacyAppName of firstPartyHarness_js_1.FIRST_PARTY_HARNESS_ENGINE.legacyAppNames) {
213
+ const aliasHome = node_path_1.default.join(root, legacyAppName);
214
+ if (!isRealDirectory(fs.lstat(aliasHome)))
215
+ continue;
216
+ for (const entry of STRANDED_HOME_ENTRIES) {
217
+ const from = node_path_1.default.join(aliasHome, entry);
218
+ const to = node_path_1.default.join(canonicalHome, entry);
219
+ if (!isRealDirectory(fs.lstat(from)))
220
+ continue;
221
+ if (fs.lstat(to) !== null)
222
+ continue;
223
+ try {
224
+ fs.rename(from, to);
225
+ moved.push(`${from} -> ${to}`);
226
+ }
227
+ catch (err) {
228
+ errors.push(`could not move ${from} to ${to}: ${describe(err)}`);
229
+ }
230
+ }
231
+ break;
232
+ }
233
+ }
189
234
  // 2. The binary and its companions — follow the provider — inside the home.
190
235
  const binDir = node_path_1.default.join(canonicalHome, 'bin');
191
236
  if (!isRealDirectory(fs.lstat(binDir))) {
@@ -0,0 +1,65 @@
1
+ /**
2
+ * OSK-10409 — which harnesses resolve the `.claude/skills/<name>/SKILL.md`
3
+ * layout, shared by the server and the daemon.
4
+ *
5
+ * ── Why this lives in auth-core ────────────────────────────────────────────
6
+ *
7
+ * The same reason `harnessTrust.ts` does: it is a DECLARED per-harness fact
8
+ * that several processes have to agree on, and the cost of them disagreeing is
9
+ * silent. It is not auth, and it does not pretend to be — auth-core is where
10
+ * this repo already keeps the small tables that a harness's identity answers.
11
+ *
12
+ * ── What the fact IS ───────────────────────────────────────────────────────
13
+ *
14
+ * The daemon materializes a session's skills at
15
+ * `<root>/.claude/skills/<name>/SKILL.md` and hands `<root>` to the harness —
16
+ * `--add-dir` for the Claude CLI, ACP `additionalDirectories` for Devin. A
17
+ * harness on this list READS that layout: it enumerates the tree, surfaces the
18
+ * manifest line, and resolves the body from disk. A harness that is not on it
19
+ * ignores the tree entirely, so nothing laid there ever reaches its model.
20
+ *
21
+ * It is a capability with EVIDENCE, never a brand check. The gate this replaced
22
+ * read `provider !== 'claude'` on the stated premise that every other harness
23
+ * ignores the layout, and the premise was false for Devin (OSK-10264): the
24
+ * daemon lays the same tree, passes it on `additionalDirectories`, and Devin
25
+ * loads it — measured live on 3000.10.31. Add a harness here only with the same
26
+ * kind of evidence, that its sessions actually surface a skill laid out this
27
+ * way. A harness that ignores the layout gains nothing and pays for the wire.
28
+ *
29
+ * ── Three readers, in three directions ─────────────────────────────────────
30
+ *
31
+ * 1. The server's PUSH gate (`buildDaemonServerSkills`): a harness on this
32
+ * list is one a pushed skill should be sent to.
33
+ * 2. The server's INVOKED-BODY gate (`buildInvokedSkillsPrompt`, OSK-10361):
34
+ * a harness on this list must NOT also have the body pasted into its
35
+ * prompt — it already has it from the tree, and on ACP the paste lands in
36
+ * the transcript at full skill size on every invoked turn.
37
+ * 3. The daemon's SHADOWING protection (`skill-shadowing.ts`, OSK-10044): a
38
+ * harness on this list resolves `/<name>` from its own `.claude/skills`
39
+ * roots, so a picked skill can be shadowed there by a same-named one — and
40
+ * the daemon is the only place that collision can be seen. That gate spelled
41
+ * `claude` and so never reached Devin, which the server-side paste was
42
+ * accidentally masking until (2) stopped pasting.
43
+ *
44
+ * Reader 3 is why the table moved out of the API: the daemon cannot require an
45
+ * api-side module, and a second copy of a table whose whole value is that every
46
+ * reader agrees is worse than no table at all.
47
+ */
48
+ /**
49
+ * Harness provider → the evidence that its sessions load the layout.
50
+ *
51
+ * The reason string is part of the entry, not decoration: an entry added
52
+ * without one is an entry nobody can audit later.
53
+ */
54
+ export declare const HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT: ReadonlyMap<string, string>;
55
+ /**
56
+ * Whether this harness resolves a skill from the `.claude/skills` tree the
57
+ * daemon materializes, so the body reaches it without being pasted anywhere.
58
+ *
59
+ * An absent provider is NOT a native resolver. "We do not know what this is"
60
+ * must never mean "it reads the layout" — that direction withholds the body
61
+ * from a harness that needed it, which is a turn that looks like the skill ran.
62
+ */
63
+ export declare function harnessLoadsClaudeSkillsLayout(provider: string | null | undefined): boolean;
64
+ /** The stated evidence for a harness on the list, or `null` when it is not. */
65
+ export declare function claudeSkillsLayoutEvidence(provider: string | null | undefined): string | null;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ /**
3
+ * OSK-10409 — which harnesses resolve the `.claude/skills/<name>/SKILL.md`
4
+ * layout, shared by the server and the daemon.
5
+ *
6
+ * ── Why this lives in auth-core ────────────────────────────────────────────
7
+ *
8
+ * The same reason `harnessTrust.ts` does: it is a DECLARED per-harness fact
9
+ * that several processes have to agree on, and the cost of them disagreeing is
10
+ * silent. It is not auth, and it does not pretend to be — auth-core is where
11
+ * this repo already keeps the small tables that a harness's identity answers.
12
+ *
13
+ * ── What the fact IS ───────────────────────────────────────────────────────
14
+ *
15
+ * The daemon materializes a session's skills at
16
+ * `<root>/.claude/skills/<name>/SKILL.md` and hands `<root>` to the harness —
17
+ * `--add-dir` for the Claude CLI, ACP `additionalDirectories` for Devin. A
18
+ * harness on this list READS that layout: it enumerates the tree, surfaces the
19
+ * manifest line, and resolves the body from disk. A harness that is not on it
20
+ * ignores the tree entirely, so nothing laid there ever reaches its model.
21
+ *
22
+ * It is a capability with EVIDENCE, never a brand check. The gate this replaced
23
+ * read `provider !== 'claude'` on the stated premise that every other harness
24
+ * ignores the layout, and the premise was false for Devin (OSK-10264): the
25
+ * daemon lays the same tree, passes it on `additionalDirectories`, and Devin
26
+ * loads it — measured live on 3000.10.31. Add a harness here only with the same
27
+ * kind of evidence, that its sessions actually surface a skill laid out this
28
+ * way. A harness that ignores the layout gains nothing and pays for the wire.
29
+ *
30
+ * ── Three readers, in three directions ─────────────────────────────────────
31
+ *
32
+ * 1. The server's PUSH gate (`buildDaemonServerSkills`): a harness on this
33
+ * list is one a pushed skill should be sent to.
34
+ * 2. The server's INVOKED-BODY gate (`buildInvokedSkillsPrompt`, OSK-10361):
35
+ * a harness on this list must NOT also have the body pasted into its
36
+ * prompt — it already has it from the tree, and on ACP the paste lands in
37
+ * the transcript at full skill size on every invoked turn.
38
+ * 3. The daemon's SHADOWING protection (`skill-shadowing.ts`, OSK-10044): a
39
+ * harness on this list resolves `/<name>` from its own `.claude/skills`
40
+ * roots, so a picked skill can be shadowed there by a same-named one — and
41
+ * the daemon is the only place that collision can be seen. That gate spelled
42
+ * `claude` and so never reached Devin, which the server-side paste was
43
+ * accidentally masking until (2) stopped pasting.
44
+ *
45
+ * Reader 3 is why the table moved out of the API: the daemon cannot require an
46
+ * api-side module, and a second copy of a table whose whole value is that every
47
+ * reader agrees is worse than no table at all.
48
+ */
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT = void 0;
51
+ exports.harnessLoadsClaudeSkillsLayout = harnessLoadsClaudeSkillsLayout;
52
+ exports.claudeSkillsLayoutEvidence = claudeSkillsLayoutEvidence;
53
+ const firstPartyHarness_js_1 = require("./firstPartyHarness.js");
54
+ /**
55
+ * Harness provider → the evidence that its sessions load the layout.
56
+ *
57
+ * The reason string is part of the entry, not decoration: an entry added
58
+ * without one is an entry nobody can audit later.
59
+ */
60
+ exports.HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT = new Map([
61
+ ['claude', 'Claude Code reads .claude/skills natively from --add-dir'],
62
+ [
63
+ 'devin',
64
+ 'Devin imports the Claude convention from additionalDirectories ' +
65
+ '(read_config_from.claude, on by default) — verified live on 3000.10.31',
66
+ ],
67
+ ]);
68
+ /**
69
+ * Canonicalise the way every reader of this table must.
70
+ *
71
+ * The provider is lower-cased and trimmed, then passed through
72
+ * `canonicalHarnessProvider` so an accepted alias of the first-party harness
73
+ * resolves to the same decision as its canonical slug rather than falling
74
+ * through to "not a native resolver". Callers with a RICHER normalizer (the API
75
+ * has `normalizeSessionProvider`, which also folds `claude-code`) apply theirs
76
+ * first; this is the floor, not a replacement.
77
+ */
78
+ function canonicalize(provider) {
79
+ return String((0, firstPartyHarness_js_1.canonicalHarnessProvider)(provider.trim().toLowerCase()));
80
+ }
81
+ /**
82
+ * Whether this harness resolves a skill from the `.claude/skills` tree the
83
+ * daemon materializes, so the body reaches it without being pasted anywhere.
84
+ *
85
+ * An absent provider is NOT a native resolver. "We do not know what this is"
86
+ * must never mean "it reads the layout" — that direction withholds the body
87
+ * from a harness that needed it, which is a turn that looks like the skill ran.
88
+ */
89
+ function harnessLoadsClaudeSkillsLayout(provider) {
90
+ if (!provider || typeof provider !== 'string')
91
+ return false;
92
+ return exports.HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT.has(canonicalize(provider));
93
+ }
94
+ /** The stated evidence for a harness on the list, or `null` when it is not. */
95
+ function claudeSkillsLayoutEvidence(provider) {
96
+ if (!provider || typeof provider !== 'string')
97
+ return null;
98
+ return exports.HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT.get(canonicalize(provider)) ?? null;
99
+ }
@@ -32,6 +32,7 @@ export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, enrollWithS
32
32
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
33
33
  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';
34
34
  export { HARNESS_TIERS, getHarnessTier, credentialPolicyForTier, tierPermitsConfiguredAutoMode, type HarnessTrustTier, type CredentialDeliveryPolicy, type CredentialMode, } from './harnessTrust.js';
35
+ export { HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT, claudeSkillsLayoutEvidence, harnessLoadsClaudeSkillsLayout, } from './harnessSkillLayout.js';
35
36
  export { sanitizeSpawnEnv, SENSITIVE_ENV_VARS, SENSITIVE_ENV_PREFIXES, SENSITIVE_ENV_SUFFIXES, SENSITIVE_ENV_NAME_TOKENS, SENSITIVE_ENV_PREFIX_EXCEPTIONS, } from './spawnEnv.js';
36
37
  export { CONFIG_DIR_NAME, LEGACY_CONFIG_DIR_NAMES, NATIVE_ID_PREFIX, LEGACY_NATIVE_ID_PREFIXES, DEFAULT_BINARY_NAME, } from './localIdentity.js';
37
38
  export { findLegacyLocalState, describeLegacyState, type LegacyStateFinding, type LegacyStateReport, } from './legacyStatePreflight.js';
@@ -25,8 +25,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.challenge = 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.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.tokenHash = 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 = void 0;
27
27
  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.__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 = void 0;
28
- 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.getDeltaFor = exports.buildManifestMessage = exports.buildArtifactMessage = 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.SENSITIVE_ENV_PREFIX_EXCEPTIONS = exports.SENSITIVE_ENV_NAME_TOKENS = exports.SENSITIVE_ENV_SUFFIXES = exports.SENSITIVE_ENV_PREFIXES = exports.SENSITIVE_ENV_VARS = exports.sanitizeSpawnEnv = exports.tierPermitsConfiguredAutoMode = exports.credentialPolicyForTier = exports.getHarnessTier = void 0;
29
- exports.consolidateProfileState = void 0;
28
+ 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.getDeltaFor = exports.buildManifestMessage = exports.buildArtifactMessage = 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.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 = void 0;
29
+ exports.consolidateProfileState = exports.profileStateFilePathsToClear = exports.profileStateFileLocations = exports.resolveProfileStateFile = 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
@@ -295,6 +295,14 @@ Object.defineProperty(exports, "HARNESS_TIERS", { enumerable: true, get: functio
295
295
  Object.defineProperty(exports, "getHarnessTier", { enumerable: true, get: function () { return harnessTrust_js_1.getHarnessTier; } });
296
296
  Object.defineProperty(exports, "credentialPolicyForTier", { enumerable: true, get: function () { return harnessTrust_js_1.credentialPolicyForTier; } });
297
297
  Object.defineProperty(exports, "tierPermitsConfiguredAutoMode", { enumerable: true, get: function () { return harnessTrust_js_1.tierPermitsConfiguredAutoMode; } });
298
+ // OSK-10409 — which harnesses resolve the `.claude/skills` layout the daemon
299
+ // materializes. One declared table with per-harness evidence, read by the
300
+ // server's two skill gates and by the daemon's shadowing protection. See
301
+ // harnessSkillLayout.ts for why a second copy would be worse than none.
302
+ var harnessSkillLayout_js_1 = require("./harnessSkillLayout.js");
303
+ Object.defineProperty(exports, "HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT", { enumerable: true, get: function () { return harnessSkillLayout_js_1.HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT; } });
304
+ Object.defineProperty(exports, "claudeSkillsLayoutEvidence", { enumerable: true, get: function () { return harnessSkillLayout_js_1.claudeSkillsLayoutEvidence; } });
305
+ Object.defineProperty(exports, "harnessLoadsClaudeSkillsLayout", { enumerable: true, get: function () { return harnessSkillLayout_js_1.harnessLoadsClaudeSkillsLayout; } });
298
306
  // OSK-3892 — spawn-env sanitization, shared by the daemon (which has stripped
299
307
  // its own credentials before every harness spawn all along) and the CLI (which
300
308
  // spawns the SAME engine and did not). `daemon/src/env-sanitize.ts` re-exports
@@ -313,6 +313,21 @@ declare const ENGINE_ENV_SUFFIXES: Readonly<{
313
313
  allowUpstreamEgress: "ALLOW_UPSTREAM_EGRESS";
314
314
  compatOpencode: "COMPAT_OPENCODE";
315
315
  noCompatPrompts: "NO_COMPAT_PROMPTS";
316
+ /**
317
+ * Advisory input-token budget for a managed session, set by the daemon from
318
+ * the server-issued `requestEnvelope.maxInputTokens` so the engine's
319
+ * auto-compaction can trigger before the relay refuses the request. The
320
+ * engine must treat it as a hint: enforcement stays server-side.
321
+ */
322
+ maxInputTokens: "MAX_INPUT_TOKENS";
323
+ /**
324
+ * The session id the engine stamps into processes it spawns — the one
325
+ * variable on this table the engine WRITES rather than reads, so a child
326
+ * process (e.g. `skrr code handover --carry-session`) can name the session it
327
+ * is running inside without being told. Read with
328
+ * `readFirstPartyHarnessEngineEnv`: current namespace first, then legacy.
329
+ */
330
+ sessionId: "SESSION_ID";
316
331
  }>;
317
332
  export type FirstPartyHarnessEngineEnvKey = keyof typeof ENGINE_ENV_SUFFIXES;
318
333
  /**
@@ -350,7 +365,7 @@ export declare const FIRST_PARTY_HARNESS_ENGINE: Readonly<{
350
365
  * `readFirstPartyHarnessEngineEnv` to read one the way the engine does and
351
366
  * `firstPartyHarnessEngineEnvEntries` to set one every engine build honours.
352
367
  */
353
- env: Readonly<Record<"appName" | "serverPassword" | "serverUsername" | "home" | "config" | "scriptName" | "allowUpstreamEgress" | "compatOpencode" | "noCompatPrompts", string>>;
368
+ env: Readonly<Record<"appName" | "serverPassword" | "serverUsername" | "home" | "config" | "scriptName" | "allowUpstreamEgress" | "compatOpencode" | "noCompatPrompts" | "maxInputTokens" | "sessionId", string>>;
354
369
  }>;
355
370
  /**
356
371
  * Every name the engine reads for one purpose, current namespace first — the
@@ -415,6 +415,21 @@ const ENGINE_ENV_SUFFIXES = Object.freeze({
415
415
  allowUpstreamEgress: 'ALLOW_UPSTREAM_EGRESS',
416
416
  compatOpencode: 'COMPAT_OPENCODE',
417
417
  noCompatPrompts: 'NO_COMPAT_PROMPTS',
418
+ /**
419
+ * Advisory input-token budget for a managed session, set by the daemon from
420
+ * the server-issued `requestEnvelope.maxInputTokens` so the engine's
421
+ * auto-compaction can trigger before the relay refuses the request. The
422
+ * engine must treat it as a hint: enforcement stays server-side.
423
+ */
424
+ maxInputTokens: 'MAX_INPUT_TOKENS',
425
+ /**
426
+ * The session id the engine stamps into processes it spawns — the one
427
+ * variable on this table the engine WRITES rather than reads, so a child
428
+ * process (e.g. `skrr code handover --carry-session`) can name the session it
429
+ * is running inside without being told. Read with
430
+ * `readFirstPartyHarnessEngineEnv`: current namespace first, then legacy.
431
+ */
432
+ sessionId: 'SESSION_ID',
418
433
  });
419
434
  function engineEnvName(appName, key) {
420
435
  return `${appName.toUpperCase().replace(/-/g, '_')}_${ENGINE_ENV_SUFFIXES[key]}`;
@@ -53,6 +53,13 @@ import { FIRST_PARTY_HARNESS, FIRST_PARTY_HARNESS_ENGINE, firstPartyHarnessArtif
53
53
  function olderSpellings() {
54
54
  return firstPartyHarnessArtifactSpellings().filter((spelling) => spelling !== FIRST_PARTY_HARNESS.provider);
55
55
  }
56
+ /**
57
+ * Entries rescued from a legacy home when the canonical one already exists as a
58
+ * shell. `bin` is what managed resolution needs; `skills` is the user's own
59
+ * data. Deliberately a short, named list rather than "everything": a merge this
60
+ * module cannot enumerate (there is no readdir here) would be guessing.
61
+ */
62
+ const STRANDED_HOME_ENTRIES = ['bin', 'skills'];
56
63
  /** Suffix of the rollback copy the installer keeps beside the binary. */
57
64
  export const FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX = '.previous';
58
65
  /**
@@ -175,6 +182,44 @@ export function migrateFirstPartyHarnessHome(options = {}) {
175
182
  break;
176
183
  }
177
184
  }
185
+ else if (isRealDirectory(fs.lstat(canonicalHome))) {
186
+ // 1b. The canonical home EXISTS, so the rename above is impossible — but it
187
+ // may be a shell rather than the real home. A managed write that reaches
188
+ // `<root>/<appName>` before this migration runs creates the directory, and
189
+ // from then on step 1 skips forever and the legacy home keeps the binary.
190
+ // Observed on a real machine: `<root>/<appName>` holding a 2-byte `{}`
191
+ // config written hours after `<root>/<legacy appName>/bin` received the
192
+ // signed 107MB engine, which left managed resolution pointing at a bin
193
+ // directory that did not exist and every managed session with no engine to
194
+ // spawn.
195
+ //
196
+ // A rename cannot merge two directories, and this module has no readdir, so
197
+ // this moves the two entries that carry weight and nothing else: `bin`,
198
+ // which is what resolution needs, and `skills`, which is user data. An
199
+ // entry already present at the canonical home is never touched — the shell
200
+ // may be empty, but it is not automatically wrong.
201
+ for (const legacyAppName of FIRST_PARTY_HARNESS_ENGINE.legacyAppNames) {
202
+ const aliasHome = path.join(root, legacyAppName);
203
+ if (!isRealDirectory(fs.lstat(aliasHome)))
204
+ continue;
205
+ for (const entry of STRANDED_HOME_ENTRIES) {
206
+ const from = path.join(aliasHome, entry);
207
+ const to = path.join(canonicalHome, entry);
208
+ if (!isRealDirectory(fs.lstat(from)))
209
+ continue;
210
+ if (fs.lstat(to) !== null)
211
+ continue;
212
+ try {
213
+ fs.rename(from, to);
214
+ moved.push(`${from} -> ${to}`);
215
+ }
216
+ catch (err) {
217
+ errors.push(`could not move ${from} to ${to}: ${describe(err)}`);
218
+ }
219
+ }
220
+ break;
221
+ }
222
+ }
178
223
  // 2. The binary and its companions — follow the provider — inside the home.
179
224
  const binDir = path.join(canonicalHome, 'bin');
180
225
  if (!isRealDirectory(fs.lstat(binDir))) {
@@ -0,0 +1,65 @@
1
+ /**
2
+ * OSK-10409 — which harnesses resolve the `.claude/skills/<name>/SKILL.md`
3
+ * layout, shared by the server and the daemon.
4
+ *
5
+ * ── Why this lives in auth-core ────────────────────────────────────────────
6
+ *
7
+ * The same reason `harnessTrust.ts` does: it is a DECLARED per-harness fact
8
+ * that several processes have to agree on, and the cost of them disagreeing is
9
+ * silent. It is not auth, and it does not pretend to be — auth-core is where
10
+ * this repo already keeps the small tables that a harness's identity answers.
11
+ *
12
+ * ── What the fact IS ───────────────────────────────────────────────────────
13
+ *
14
+ * The daemon materializes a session's skills at
15
+ * `<root>/.claude/skills/<name>/SKILL.md` and hands `<root>` to the harness —
16
+ * `--add-dir` for the Claude CLI, ACP `additionalDirectories` for Devin. A
17
+ * harness on this list READS that layout: it enumerates the tree, surfaces the
18
+ * manifest line, and resolves the body from disk. A harness that is not on it
19
+ * ignores the tree entirely, so nothing laid there ever reaches its model.
20
+ *
21
+ * It is a capability with EVIDENCE, never a brand check. The gate this replaced
22
+ * read `provider !== 'claude'` on the stated premise that every other harness
23
+ * ignores the layout, and the premise was false for Devin (OSK-10264): the
24
+ * daemon lays the same tree, passes it on `additionalDirectories`, and Devin
25
+ * loads it — measured live on 3000.10.31. Add a harness here only with the same
26
+ * kind of evidence, that its sessions actually surface a skill laid out this
27
+ * way. A harness that ignores the layout gains nothing and pays for the wire.
28
+ *
29
+ * ── Three readers, in three directions ─────────────────────────────────────
30
+ *
31
+ * 1. The server's PUSH gate (`buildDaemonServerSkills`): a harness on this
32
+ * list is one a pushed skill should be sent to.
33
+ * 2. The server's INVOKED-BODY gate (`buildInvokedSkillsPrompt`, OSK-10361):
34
+ * a harness on this list must NOT also have the body pasted into its
35
+ * prompt — it already has it from the tree, and on ACP the paste lands in
36
+ * the transcript at full skill size on every invoked turn.
37
+ * 3. The daemon's SHADOWING protection (`skill-shadowing.ts`, OSK-10044): a
38
+ * harness on this list resolves `/<name>` from its own `.claude/skills`
39
+ * roots, so a picked skill can be shadowed there by a same-named one — and
40
+ * the daemon is the only place that collision can be seen. That gate spelled
41
+ * `claude` and so never reached Devin, which the server-side paste was
42
+ * accidentally masking until (2) stopped pasting.
43
+ *
44
+ * Reader 3 is why the table moved out of the API: the daemon cannot require an
45
+ * api-side module, and a second copy of a table whose whole value is that every
46
+ * reader agrees is worse than no table at all.
47
+ */
48
+ /**
49
+ * Harness provider → the evidence that its sessions load the layout.
50
+ *
51
+ * The reason string is part of the entry, not decoration: an entry added
52
+ * without one is an entry nobody can audit later.
53
+ */
54
+ export declare const HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT: ReadonlyMap<string, string>;
55
+ /**
56
+ * Whether this harness resolves a skill from the `.claude/skills` tree the
57
+ * daemon materializes, so the body reaches it without being pasted anywhere.
58
+ *
59
+ * An absent provider is NOT a native resolver. "We do not know what this is"
60
+ * must never mean "it reads the layout" — that direction withholds the body
61
+ * from a harness that needed it, which is a turn that looks like the skill ran.
62
+ */
63
+ export declare function harnessLoadsClaudeSkillsLayout(provider: string | null | undefined): boolean;
64
+ /** The stated evidence for a harness on the list, or `null` when it is not. */
65
+ export declare function claudeSkillsLayoutEvidence(provider: string | null | undefined): string | null;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * OSK-10409 — which harnesses resolve the `.claude/skills/<name>/SKILL.md`
3
+ * layout, shared by the server and the daemon.
4
+ *
5
+ * ── Why this lives in auth-core ────────────────────────────────────────────
6
+ *
7
+ * The same reason `harnessTrust.ts` does: it is a DECLARED per-harness fact
8
+ * that several processes have to agree on, and the cost of them disagreeing is
9
+ * silent. It is not auth, and it does not pretend to be — auth-core is where
10
+ * this repo already keeps the small tables that a harness's identity answers.
11
+ *
12
+ * ── What the fact IS ───────────────────────────────────────────────────────
13
+ *
14
+ * The daemon materializes a session's skills at
15
+ * `<root>/.claude/skills/<name>/SKILL.md` and hands `<root>` to the harness —
16
+ * `--add-dir` for the Claude CLI, ACP `additionalDirectories` for Devin. A
17
+ * harness on this list READS that layout: it enumerates the tree, surfaces the
18
+ * manifest line, and resolves the body from disk. A harness that is not on it
19
+ * ignores the tree entirely, so nothing laid there ever reaches its model.
20
+ *
21
+ * It is a capability with EVIDENCE, never a brand check. The gate this replaced
22
+ * read `provider !== 'claude'` on the stated premise that every other harness
23
+ * ignores the layout, and the premise was false for Devin (OSK-10264): the
24
+ * daemon lays the same tree, passes it on `additionalDirectories`, and Devin
25
+ * loads it — measured live on 3000.10.31. Add a harness here only with the same
26
+ * kind of evidence, that its sessions actually surface a skill laid out this
27
+ * way. A harness that ignores the layout gains nothing and pays for the wire.
28
+ *
29
+ * ── Three readers, in three directions ─────────────────────────────────────
30
+ *
31
+ * 1. The server's PUSH gate (`buildDaemonServerSkills`): a harness on this
32
+ * list is one a pushed skill should be sent to.
33
+ * 2. The server's INVOKED-BODY gate (`buildInvokedSkillsPrompt`, OSK-10361):
34
+ * a harness on this list must NOT also have the body pasted into its
35
+ * prompt — it already has it from the tree, and on ACP the paste lands in
36
+ * the transcript at full skill size on every invoked turn.
37
+ * 3. The daemon's SHADOWING protection (`skill-shadowing.ts`, OSK-10044): a
38
+ * harness on this list resolves `/<name>` from its own `.claude/skills`
39
+ * roots, so a picked skill can be shadowed there by a same-named one — and
40
+ * the daemon is the only place that collision can be seen. That gate spelled
41
+ * `claude` and so never reached Devin, which the server-side paste was
42
+ * accidentally masking until (2) stopped pasting.
43
+ *
44
+ * Reader 3 is why the table moved out of the API: the daemon cannot require an
45
+ * api-side module, and a second copy of a table whose whole value is that every
46
+ * reader agrees is worse than no table at all.
47
+ */
48
+ import { canonicalHarnessProvider } from './firstPartyHarness.js';
49
+ /**
50
+ * Harness provider → the evidence that its sessions load the layout.
51
+ *
52
+ * The reason string is part of the entry, not decoration: an entry added
53
+ * without one is an entry nobody can audit later.
54
+ */
55
+ export const HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT = new Map([
56
+ ['claude', 'Claude Code reads .claude/skills natively from --add-dir'],
57
+ [
58
+ 'devin',
59
+ 'Devin imports the Claude convention from additionalDirectories ' +
60
+ '(read_config_from.claude, on by default) — verified live on 3000.10.31',
61
+ ],
62
+ ]);
63
+ /**
64
+ * Canonicalise the way every reader of this table must.
65
+ *
66
+ * The provider is lower-cased and trimmed, then passed through
67
+ * `canonicalHarnessProvider` so an accepted alias of the first-party harness
68
+ * resolves to the same decision as its canonical slug rather than falling
69
+ * through to "not a native resolver". Callers with a RICHER normalizer (the API
70
+ * has `normalizeSessionProvider`, which also folds `claude-code`) apply theirs
71
+ * first; this is the floor, not a replacement.
72
+ */
73
+ function canonicalize(provider) {
74
+ return String(canonicalHarnessProvider(provider.trim().toLowerCase()));
75
+ }
76
+ /**
77
+ * Whether this harness resolves a skill from the `.claude/skills` tree the
78
+ * daemon materializes, so the body reaches it without being pasted anywhere.
79
+ *
80
+ * An absent provider is NOT a native resolver. "We do not know what this is"
81
+ * must never mean "it reads the layout" — that direction withholds the body
82
+ * from a harness that needed it, which is a turn that looks like the skill ran.
83
+ */
84
+ export function harnessLoadsClaudeSkillsLayout(provider) {
85
+ if (!provider || typeof provider !== 'string')
86
+ return false;
87
+ return HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT.has(canonicalize(provider));
88
+ }
89
+ /** The stated evidence for a harness on the list, or `null` when it is not. */
90
+ export function claudeSkillsLayoutEvidence(provider) {
91
+ if (!provider || typeof provider !== 'string')
92
+ return null;
93
+ return HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT.get(canonicalize(provider)) ?? null;
94
+ }
@@ -32,6 +32,7 @@ export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, enrollWithS
32
32
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
33
33
  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';
34
34
  export { HARNESS_TIERS, getHarnessTier, credentialPolicyForTier, tierPermitsConfiguredAutoMode, type HarnessTrustTier, type CredentialDeliveryPolicy, type CredentialMode, } from './harnessTrust.js';
35
+ export { HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT, claudeSkillsLayoutEvidence, harnessLoadsClaudeSkillsLayout, } from './harnessSkillLayout.js';
35
36
  export { sanitizeSpawnEnv, SENSITIVE_ENV_VARS, SENSITIVE_ENV_PREFIXES, SENSITIVE_ENV_SUFFIXES, SENSITIVE_ENV_NAME_TOKENS, SENSITIVE_ENV_PREFIX_EXCEPTIONS, } from './spawnEnv.js';
36
37
  export { CONFIG_DIR_NAME, LEGACY_CONFIG_DIR_NAMES, NATIVE_ID_PREFIX, LEGACY_NATIVE_ID_PREFIXES, DEFAULT_BINARY_NAME, } from './localIdentity.js';
37
38
  export { findLegacyLocalState, describeLegacyState, type LegacyStateFinding, type LegacyStateReport, } from './legacyStatePreflight.js';
@@ -130,6 +130,11 @@ export { PAIR_AAD_PREFIX, PAIR_AAD_MAX_LEN, PAIR_BUNDLE_MAX_BASE64_LEN, PAIR_NON
130
130
  // in the daemon on purpose: a tier is a published fact, provenance is evidence
131
131
  // about bytes on disk that only the spawning process can gather.
132
132
  export { HARNESS_TIERS, getHarnessTier, credentialPolicyForTier, tierPermitsConfiguredAutoMode, } from './harnessTrust.js';
133
+ // OSK-10409 — which harnesses resolve the `.claude/skills` layout the daemon
134
+ // materializes. One declared table with per-harness evidence, read by the
135
+ // server's two skill gates and by the daemon's shadowing protection. See
136
+ // harnessSkillLayout.ts for why a second copy would be worse than none.
137
+ export { HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT, claudeSkillsLayoutEvidence, harnessLoadsClaudeSkillsLayout, } from './harnessSkillLayout.js';
133
138
  // OSK-3892 — spawn-env sanitization, shared by the daemon (which has stripped
134
139
  // its own credentials before every harness spawn all along) and the CLI (which
135
140
  // spawns the SAME engine and did not). `daemon/src/env-sanitize.ts` re-exports
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skrr-ai/auth-core",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "main": "dist/cjs/index.js",
5
5
  "types": "dist/esm/index.d.ts",
6
6
  "exports": {
@@ -33,6 +33,16 @@
33
33
  "types": "./dist/cjs/firstPartyHarness.d.ts",
34
34
  "default": "./dist/cjs/firstPartyHarness.js"
35
35
  }
36
+ },
37
+ "./harness-skill-layout": {
38
+ "import": {
39
+ "types": "./dist/esm/harnessSkillLayout.d.ts",
40
+ "default": "./dist/esm/harnessSkillLayout.js"
41
+ },
42
+ "require": {
43
+ "types": "./dist/cjs/harnessSkillLayout.d.ts",
44
+ "default": "./dist/cjs/harnessSkillLayout.js"
45
+ }
36
46
  }
37
47
  },
38
48
  "dependencies": {