@skrr-ai/cli 0.1.32 → 0.1.33

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 (111) hide show
  1. package/bin/run.js +2 -2
  2. package/dist/commands/agents/list.d.ts +2 -1
  3. package/dist/commands/agents/list.js +2 -1
  4. package/dist/commands/agents/show.js +2 -1
  5. package/dist/commands/balance/statement.js +8 -4
  6. package/dist/commands/browser/install.js +6 -2
  7. package/dist/commands/browser/uninstall.js +6 -2
  8. package/dist/commands/code/acp.js +2 -1
  9. package/dist/commands/code/doctor.d.ts +1 -1
  10. package/dist/commands/code/doctor.js +6 -5
  11. package/dist/commands/code/handover.d.ts +2 -2
  12. package/dist/commands/code/handover.js +2 -2
  13. package/dist/commands/code/index.d.ts +2 -2
  14. package/dist/commands/code/index.js +18 -17
  15. package/dist/commands/code/install.d.ts +3 -3
  16. package/dist/commands/code/install.js +18 -11
  17. package/dist/commands/code/migrate.js +3 -2
  18. package/dist/commands/code/oversky-agent.d.ts +5 -4
  19. package/dist/commands/code/oversky-agent.js +24 -22
  20. package/dist/commands/code/run.js +2 -1
  21. package/dist/commands/harnesses/list.d.ts +9 -1
  22. package/dist/commands/harnesses/list.js +28 -7
  23. package/dist/commands/harnesses/show.js +7 -2
  24. package/dist/commands/instructions/install.js +16 -6
  25. package/dist/commands/learning/delete.d.ts +9 -0
  26. package/dist/commands/learning/delete.js +39 -0
  27. package/dist/commands/learning/export.d.ts +5 -0
  28. package/dist/commands/learning/export.js +22 -0
  29. package/dist/commands/learning/reset.d.ts +9 -0
  30. package/dist/commands/learning/reset.js +34 -0
  31. package/dist/commands/learning/set.d.ts +10 -0
  32. package/dist/commands/learning/set.js +52 -0
  33. package/dist/commands/learning/status.d.ts +8 -0
  34. package/dist/commands/learning/status.js +35 -0
  35. package/dist/commands/logout.js +10 -5
  36. package/dist/commands/machines/dedicated/attach.js +2 -1
  37. package/dist/commands/tasks/runs.js +3 -1
  38. package/dist/commands/workspaces/use.js +2 -1
  39. package/dist/lib/adaptive-learning.d.ts +37 -0
  40. package/dist/lib/adaptive-learning.js +39 -0
  41. package/dist/lib/code-handover.d.ts +1 -1
  42. package/dist/lib/code-handover.js +3 -2
  43. package/dist/lib/config.d.ts +30 -4
  44. package/dist/lib/config.js +2 -1
  45. package/dist/lib/daemon-installer.d.ts +1 -1
  46. package/dist/lib/daemon-target.js +2 -2
  47. package/dist/lib/dedicated-machines.d.ts +2 -2
  48. package/dist/lib/dedicated-machines.js +6 -3
  49. package/dist/lib/exec-runtime-binary.d.ts +2 -2
  50. package/dist/lib/exec-runtime-binary.js +2 -2
  51. package/dist/lib/{sky-code-agent.d.ts → first-party-harness-agent.d.ts} +20 -19
  52. package/dist/lib/{sky-code-agent.js → first-party-harness-agent.js} +85 -47
  53. package/dist/lib/{sky-code-broker.d.ts → first-party-harness-broker.d.ts} +6 -6
  54. package/dist/lib/{sky-code-broker.js → first-party-harness-broker.js} +23 -23
  55. package/dist/lib/{sky-code-doctor.d.ts → first-party-harness-doctor.d.ts} +12 -2
  56. package/dist/lib/{sky-code-doctor.js → first-party-harness-doctor.js} +181 -94
  57. package/dist/lib/{sky-code-managed.d.ts → first-party-harness-managed.d.ts} +55 -30
  58. package/dist/lib/{sky-code-managed.js → first-party-harness-managed.js} +92 -35
  59. package/dist/lib/{sky-code.d.ts → first-party-harness.d.ts} +107 -36
  60. package/dist/lib/{sky-code.js → first-party-harness.js} +172 -73
  61. package/dist/lib/harness-provider-input.d.ts +35 -0
  62. package/dist/lib/harness-provider-input.js +57 -0
  63. package/dist/lib/harness-tiers.d.ts +8 -2
  64. package/dist/lib/harness-tiers.js +11 -6
  65. package/dist/lib/task-instruction-offer.js +10 -4
  66. package/dist/lib/worklogs.js +2 -2
  67. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/configRoot.d.ts +2 -2
  68. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/configRoot.js +2 -2
  69. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +248 -0
  70. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +361 -0
  71. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +147 -0
  72. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +180 -0
  73. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.d.ts +111 -0
  74. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.js +314 -0
  75. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.d.ts +11 -5
  76. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.js +14 -7
  77. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +3 -1
  78. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +51 -14
  79. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseManifest.js +1 -1
  80. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +4 -4
  81. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +4 -4
  82. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/configRoot.d.ts +2 -2
  83. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/configRoot.js +2 -2
  84. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +248 -0
  85. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +332 -0
  86. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +147 -0
  87. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +170 -0
  88. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.d.ts +111 -0
  89. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.js +303 -0
  90. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.d.ts +11 -5
  91. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.js +14 -7
  92. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +3 -1
  93. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +12 -2
  94. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseManifest.js +1 -1
  95. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +4 -4
  96. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +4 -4
  97. package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
  98. package/dist/node_modules/@skrr-ai/data-provider/index.js +4816 -4782
  99. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/index.d.ts +1 -1
  100. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/index.js +1 -1
  101. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.js +1 -1
  102. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/index.d.ts +1 -1
  103. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/index.js +1 -1
  104. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.js +1 -1
  105. package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
  106. package/oclif.manifest.json +34682 -34371
  107. package/package.json +5 -2
  108. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.d.ts +0 -96
  109. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/skyCodeChannels.js +0 -115
  110. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.d.ts +0 -96
  111. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/skyCodeChannels.js +0 -107
@@ -1,27 +1,37 @@
1
1
  "use strict";
2
2
  /**
3
- * WL-2.2 — locate and forward to the local `sky-code` engine.
3
+ * WL-2.2 — locate and forward to the local first-party harness engine.
4
4
  *
5
5
  * The same `gh → git` shape `exec-runtime-binary.ts` uses, for the same reason: the
6
- * Sky CLI is a REST client, the engine is a separately-built, separately-
6
+ * skrr CLI is a REST client, the engine is a separately-built, separately-
7
7
  * released binary with its own lifetime and failure model. Unifying them at
8
8
  * the command surface (`skrr code`) gives users one thing to type without
9
9
  * coupling the two release cycles.
10
10
  *
11
11
  * The naming constraint from the plan (§3) is load-bearing and easy to get
12
12
  * wrong: skrr already publishes `oversky` (daemon) and `skrr` (control
13
- * plane). This must NOT become a third top-level executable. `sky-code` is an
14
- * internal binary name; `skrr code` is the only public entry point.
13
+ * plane). This must NOT become a third top-level executable. The engine's file
14
+ * name (`firstPartyHarnessBinaryName()`) is internal; `skrr code` is the only
15
+ * public entry point.
16
+ *
17
+ * ── No product name in this file ──────────────────────────────────────────
18
+ *
19
+ * Every product-named value — the binary's file name, the engine home, the env
20
+ * var names, the display name — is read from the identity module in
21
+ * `@skrr-ai/auth-core` (`docs/architecture/skrr-code-identifier-rename-2026-09-13.md`).
22
+ * The engine's file name has more than one spelling while a rename is in
23
+ * flight, so resolution tries every spelling, canonical first, in each location.
15
24
  */
16
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
17
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
27
  };
19
28
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.ENGINE_START_REFUSED_EXIT_CODE = exports.EngineStartRefusedError = exports.DEDICATED_RUNTIME_ENGINE_MESSAGE = exports.DEDICATED_WORKLOAD_UID_ENV = exports.ENGINE_PATH_ENV = void 0;
29
+ exports.ENGINE_START_REFUSED_EXIT_CODE = exports.EngineStartRefusedError = exports.DEDICATED_RUNTIME_ENGINE_MESSAGE = exports.DEDICATED_WORKLOAD_UID_ENV = exports.ENGINE_NOT_INSTALLED = void 0;
21
30
  exports.engineHome = engineHome;
22
- exports.skyCodeInstructionHome = skyCodeInstructionHome;
31
+ exports.engineInstructionHome = engineInstructionHome;
23
32
  exports.managedEnginePath = managedEnginePath;
24
- exports.legacyManagedEnginePath = legacyManagedEnginePath;
33
+ exports.legacyManagedEnginePaths = legacyManagedEnginePaths;
34
+ exports.legacyManagedEngineBinDirs = legacyManagedEngineBinDirs;
25
35
  exports.resolveEngine = resolveEngine;
26
36
  exports.buildEngineArgs = buildEngineArgs;
27
37
  exports.isDedicatedRuntimeWorkload = isDedicatedRuntimeWorkload;
@@ -30,6 +40,7 @@ exports.inheritedProviderCredentials = inheritedProviderCredentials;
30
40
  exports.hasProviderCredential = hasProviderCredential;
31
41
  exports.strippedProviderCredentialNames = strippedProviderCredentialNames;
32
42
  exports.engineSpawnEnv = engineSpawnEnv;
43
+ exports.engineOwnedEnv = engineOwnedEnv;
33
44
  exports.execEngine = execEngine;
34
45
  const node_child_process_1 = require("node:child_process");
35
46
  const node_fs_1 = require("node:fs");
@@ -42,25 +53,42 @@ const config_1 = require("./config");
42
53
  // read it to decide whether a run was managed; that decision now arrives as a
43
54
  // declared `credentialMode`, and re-introducing the import here would be the first
44
55
  // step back toward inferring a security-relevant fact from a string.
45
- /** Env override, mirroring the daemon's `OVERSKY_SKY_CODE_PATH`. */
46
- exports.ENGINE_PATH_ENV = 'OVERSKY_SKY_CODE_PATH';
47
56
  /**
48
- * The engine's user-state root. skrr Code's child of skrr's `~/.skrr/`.
57
+ * The error `execEngine` throws when no engine resolves. A value the commands
58
+ * compare against, so it is exported rather than retyped at each catch site.
59
+ */
60
+ exports.ENGINE_NOT_INSTALLED = 'engine-not-installed';
61
+ /**
62
+ * The engine's user-state root: the engine home under skrr's `~/.skrr/`.
49
63
  *
50
64
  * Honours `OVERSKY_CONFIG_DIR`, the same root override the daemon reads
51
65
  * (`daemon/src/bun-entry.ts`). Without it the CLI would keep looking under
52
66
  * `$HOME` for a user who relocated their skrr root, and would resolve a
53
67
  * different engine than the daemon — the exact divergence the managed-first
54
68
  * ordering below exists to prevent.
69
+ *
70
+ * The directory NAME follows the engine's own namespace, not the provider slug
71
+ * (`firstPartyHarnessHomeDirname`, contract §5): the engine reads its config and
72
+ * the managed `AGENTS.md` from here, so a home named after anything else would be
73
+ * a directory the engine never reads.
55
74
  */
56
75
  function engineHome(env = process.env) {
57
- // `configRoot` and not a second reading of the environment: deriving the root
58
- // twice is what let this function and `getConfigPath()` disagree (OSK-300).
59
- return node_path_1.default.join((0, config_1.configRoot)(env), 'sky-code');
76
+ // The shared resolver and not a second reading of the environment: deriving
77
+ // the root twice is what let this function and `getConfigPath()` disagree
78
+ // (OSK-300).
79
+ return (0, auth_core_1.firstPartyHarnessHomeDir)(env);
60
80
  }
61
- /** The user-level instruction root owned by skrr Code. */
62
- function skyCodeInstructionHome(env = process.env) {
63
- const configured = env.SKY_CODE_HOME?.trim();
81
+ /**
82
+ * The user-level instruction root the engine owns.
83
+ *
84
+ * Read under the neutral variable first and its older name second
85
+ * (`readFirstPartyHarnessEnv`). The older name is also the one the ENGINE reads,
86
+ * which is why {@link engineOwnedEnv} hands the resolved value to the engine under
87
+ * that name — otherwise the doctor would honour the neutral variable and the
88
+ * engine would not.
89
+ */
90
+ function engineInstructionHome(env = process.env) {
91
+ const configured = (0, auth_core_1.readFirstPartyHarnessEnv)(env, 'home').value;
64
92
  // Falls through to `engineHome()` rather than re-deriving `~/.skrr` — the two
65
93
  // describe the same root, and re-deriving it is what made them disagree under
66
94
  // `OVERSKY_CONFIG_DIR` (OSK-300).
@@ -73,14 +101,16 @@ function skyCodeInstructionHome(env = process.env) {
73
101
  return node_path_1.default.resolve(configured);
74
102
  }
75
103
  /**
76
- * The managed install location — where the daemon, the Desktop app, and
77
- * `scripts/sky-code/build.mjs --install` all put the engine.
104
+ * The managed install location for one spelling of the binary (canonical by
105
+ * default) — where the daemon, the Desktop app, and
106
+ * `scripts/first-party-harness/build.mjs --install` all put the engine.
78
107
  */
79
- function managedEnginePath(exeName, env = process.env) {
80
- return node_path_1.default.join(engineHome(env), 'bin', exeName);
108
+ function managedEnginePath(env = process.env, provider = auth_core_1.FIRST_PARTY_HARNESS.provider) {
109
+ return (0, auth_core_1.firstPartyHarnessInstalledBinaryPath)(env, provider, process.platform);
81
110
  }
82
111
  /**
83
- * The same managed location under the PREVIOUS config root.
112
+ * The same managed location under the PREVIOUS config root, for every spelling of
113
+ * the binary and every engine home name, canonical first.
84
114
  *
85
115
  * The root moved `~/.oversky` → `~/.skrr` in the skrr rename, and an engine
86
116
  * installed before that is still on disk under the old one. `daemon/src/
@@ -88,14 +118,22 @@ function managedEnginePath(exeName, env = process.env) {
88
118
  * the same machine the daemon found the engine and `skrr code` reported that no
89
119
  * engine was installed — the same product, the same 106 MB binary, two answers.
90
120
  *
91
- * Returns null when an explicit `OVERSKY_CONFIG_DIR` is set, matching the
92
- * daemon: that override names one root deliberately, and reaching past it to a
93
- * hard-coded home would defeat the pinning {@link engineHome} relies on.
121
+ * The engine-home move (`migrateFirstPartyHarnessHome`) only ever works under the
122
+ * CURRENT root, so nothing renames a binary here — which is why this list carries
123
+ * every spelling rather than only the canonical one.
124
+ *
125
+ * Empty when an explicit `OVERSKY_CONFIG_DIR` is set, matching the daemon: that
126
+ * override names one root deliberately, and reaching past it to a hard-coded home
127
+ * would defeat the pinning {@link engineHome} relies on.
94
128
  */
95
- function legacyManagedEnginePath(exeName, env = process.env) {
129
+ function legacyManagedEnginePaths(env = process.env) {
130
+ return legacyManagedEngineBinDirs(env).flatMap((dir) => (0, auth_core_1.firstPartyHarnessBinaryNames)(process.platform).map((name) => node_path_1.default.join(dir, name)));
131
+ }
132
+ /** The `bin` directories {@link legacyManagedEnginePaths} looks in. */
133
+ function legacyManagedEngineBinDirs(env = process.env) {
96
134
  if ((0, auth_core_1.isConfigRootOverridden)(env))
97
- return null;
98
- return node_path_1.default.join((0, node_os_1.homedir)(), '.oversky', 'sky-code', 'bin', exeName);
135
+ return [];
136
+ return [(0, auth_core_1.firstPartyHarnessHomeDirname)(), ...auth_core_1.FIRST_PARTY_HARNESS_ENGINE.legacyAppNames].map((home) => node_path_1.default.join((0, config_1.priorConfigDir)(), home, 'bin'));
99
137
  }
100
138
  /**
101
139
  * Unmanaged well-known locations, checked after PATH.
@@ -110,6 +148,15 @@ function fallbackPaths(exeName) {
110
148
  node_path_1.default.join((0, node_os_1.homedir)(), '.local', 'bin', exeName),
111
149
  ];
112
150
  }
151
+ /**
152
+ * The file names a PATH directory may hold the engine under, canonical first.
153
+ * Windows also accepts a `.cmd` shim, as a package manager may install one.
154
+ */
155
+ function pathExecutableNames(platform) {
156
+ return (0, auth_core_1.firstPartyHarnessSpellings)().flatMap((spelling) => platform === 'win32'
157
+ ? [(0, auth_core_1.firstPartyHarnessBinaryName)(spelling, platform), `${spelling}.cmd`]
158
+ : [(0, auth_core_1.firstPartyHarnessBinaryName)(spelling, platform)]);
159
+ }
113
160
  function isExecutable(p) {
114
161
  try {
115
162
  (0, node_fs_1.accessSync)(p, node_fs_1.constants.X_OK);
@@ -126,19 +173,23 @@ function isExecutable(p) {
126
173
  * binary am I actually running" is the first question when a managed session
127
174
  * and an interactive session behave differently.
128
175
  *
129
- * `env` is checked first and is absolute-only. A relative `OVERSKY_SKY_CODE_PATH`
176
+ * The env override is checked first and is absolute-only. A relative override
130
177
  * would resolve against the caller's cwd, which for an agent-invoked command is
131
- * attacker-influenced: a repo containing `./sky-code` could hijack the engine.
178
+ * attacker-influenced: a repo containing a file named like the engine could
179
+ * hijack it. It is read under its neutral name first and its older name second.
132
180
  *
133
- * **Order: env → managed → PATH → well-known (WL-6.2).**
181
+ * **Order: env → managed → PATH → well-known (WL-6.2).** Within each location
182
+ * every spelling of the binary is tried, canonical first — the daemon's order —
183
+ * so an engine installed under an older name still resolves, and the canonical
184
+ * one wins wherever both exist.
134
185
  *
135
186
  * The managed location beating `PATH` is the load-bearing part, and it is a
136
187
  * deliberate reversal of the obvious ordering.
137
188
  *
138
189
  * WL-6.2's acceptance criterion is that "Desktop-bundled and standalone
139
190
  * installations resolve the same version deterministically". With `PATH` first
140
- * they do not: a Desktop app that ships a pinned, signed engine into
141
- * `~/.skrr/sky-code/bin` silently loses to whatever `sky-code` happens to sit
191
+ * they do not: a Desktop app that ships a pinned, signed engine into the managed
192
+ * `bin` directory silently loses to whatever engine binary happens to sit
142
193
  * earlier on the user's `PATH` — a months-old dev build, a Homebrew copy of a
143
194
  * different version, a shim from an uninstalled package manager. The user sees
144
195
  * one product and gets two engines depending on their shell, and the daemon
@@ -151,33 +202,40 @@ function isExecutable(p) {
151
202
  *
152
203
  * `skrr code doctor` prints the winning `source`, so a surprising resolution is
153
204
  * one command away from being explained rather than being a mystery.
205
+ *
206
+ * Pure: it reads the filesystem and moves nothing. The engine-home move runs in
207
+ * {@link execEngine}, before it resolves, so the doctor stays safe to run anywhere.
154
208
  */
155
209
  function resolveEngine(env = process.env) {
156
- const exeName = process.platform === 'win32' ? 'sky-code.exe' : 'sky-code';
157
- const override = env[exports.ENGINE_PATH_ENV];
158
- if (override && override.trim()) {
159
- const candidate = override.trim();
210
+ const platform = process.platform;
211
+ const override = (0, auth_core_1.readFirstPartyHarnessEnv)(env, 'path');
212
+ if (override.value && override.name) {
213
+ const origin = { envName: override.name, legacyEnv: override.legacy };
214
+ const candidate = override.value;
160
215
  if (!node_path_1.default.isAbsolute(candidate))
161
- return { path: null, source: 'unresolved' };
216
+ return { path: null, source: 'unresolved', ...origin };
162
217
  return isExecutable(candidate)
163
- ? { path: candidate, source: 'env' }
164
- : { path: null, source: 'unresolved' };
218
+ ? { path: candidate, source: 'env', ...origin }
219
+ : { path: null, source: 'unresolved', ...origin };
220
+ }
221
+ for (const spelling of (0, auth_core_1.firstPartyHarnessSpellings)()) {
222
+ const managed = managedEnginePath(env, spelling);
223
+ if (isExecutable(managed))
224
+ return { path: managed, source: 'managed' };
165
225
  }
166
- const managed = managedEnginePath(exeName, env);
167
- if (isExecutable(managed))
168
- return { path: managed, source: 'managed' };
169
226
  // The pre-rename managed location, SECOND. After the current one, so a fresh
170
227
  // install always wins; before PATH, for the same WL-6.2 reason the current one
171
228
  // beats PATH — a binary skrr installed outranks whatever happens to sit on the
172
229
  // user's PATH, and which config root it was installed under does not change
173
230
  // that. Reported as its own `source` so `skrr code doctor` can say where it
174
231
  // came from rather than quietly resolving something surprising.
175
- const legacy = legacyManagedEnginePath(exeName, env);
176
- if (legacy && isExecutable(legacy))
177
- return { path: legacy, source: 'legacy-managed' };
232
+ for (const legacy of legacyManagedEnginePaths(env)) {
233
+ if (isExecutable(legacy))
234
+ return { path: legacy, source: 'legacy-managed' };
235
+ }
178
236
  const PATH = env.PATH ?? '';
179
- const sep = process.platform === 'win32' ? ';' : ':';
180
- const exeNames = process.platform === 'win32' ? ['sky-code.exe', 'sky-code.cmd'] : ['sky-code'];
237
+ const sep = platform === 'win32' ? ';' : ':';
238
+ const exeNames = pathExecutableNames(platform);
181
239
  for (const dir of PATH.split(sep)) {
182
240
  if (!dir)
183
241
  continue;
@@ -187,9 +245,11 @@ function resolveEngine(env = process.env) {
187
245
  return { path: candidate, source: 'path' };
188
246
  }
189
247
  }
190
- for (const candidate of fallbackPaths(exeName)) {
191
- if (isExecutable(candidate))
192
- return { path: candidate, source: 'fallback' };
248
+ for (const exeName of (0, auth_core_1.firstPartyHarnessBinaryNames)(platform)) {
249
+ for (const candidate of fallbackPaths(exeName)) {
250
+ if (isExecutable(candidate))
251
+ return { path: candidate, source: 'fallback' };
252
+ }
193
253
  }
194
254
  return { path: null, source: 'unresolved' };
195
255
  }
@@ -239,20 +299,20 @@ exports.DEDICATED_RUNTIME_ENGINE_MESSAGE = 'On a Dedicated Runtime, coding engin
239
299
  * Message shown when no engine is installed.
240
300
  *
241
301
  * Points at the DAEMON's managed installer rather than at a package manager.
242
- * The earlier version of this text offered `brew install
243
- * dush1023/oversky/sky-code` and `curl .../sky-code/install.sh | bash`; neither
244
- * exists — the tap carries only the `oversky` formula and the feed serves no
245
- * install script — so a user following it hit a 404 and a 403 and reasonably
246
- * concluded the engine was not real.
302
+ * The earlier version of this text offered a Homebrew formula for the engine and
303
+ * a `curl .../install.sh | bash` on the update feed; neither exists — the tap
304
+ * carries only the `oversky` formula and the feed serves no install script — so
305
+ * a user following it hit a 404 and a 403 and reasonably concluded the engine
306
+ * was not real.
247
307
  *
248
308
  * The managed path is also the only one that establishes provenance: it
249
309
  * verifies the signed manifest and writes the sidecar the daemon reads before
250
310
  * it will hand the engine a platform credential. A hand-placed binary works, and
251
311
  * since the owner decision of 2026-08-11 it runs at the SAME tier as a managed
252
- * one — the daemon's floor for `sky-code` is Tier 1 regardless of provenance
253
- * (OSK-3894; this said "stays at Tier 2"). The managed path is still the
254
- * recommendation, but the reason is now "you can prove which bytes ran" rather
255
- * than "otherwise you get a weaker credential".
312
+ * one — the daemon's floor for the first-party harness is Tier 1 regardless of
313
+ * provenance (OSK-3894; this said "stays at Tier 2"). The managed path is still
314
+ * the recommendation, but the reason is now "you can prove which bytes ran"
315
+ * rather than "otherwise you get a weaker credential".
256
316
  *
257
317
  * Keep this in step with what actually ships. An install instruction that does
258
318
  * not work is worse than none: it moves the failure from "I could not find how
@@ -262,9 +322,10 @@ exports.DEDICATED_RUNTIME_ENGINE_MESSAGE = 'On a Dedicated Runtime, coding engin
262
322
  * below fails there (see {@link DEDICATED_RUNTIME_ENGINE_MESSAGE}).
263
323
  */
264
324
  function notInstalledMessage(env = process.env) {
325
+ const { command, displayName } = auth_core_1.FIRST_PARTY_HARNESS;
265
326
  if (isDedicatedRuntimeWorkload(env)) {
266
327
  return [
267
- 'The skrr Code engine is not on this machine.',
328
+ `The ${displayName} engine (\`${(0, auth_core_1.firstPartyHarnessBinaryName)()}\`) is not on this machine.`,
268
329
  '',
269
330
  exports.DEDICATED_RUNTIME_ENGINE_MESSAGE,
270
331
  'This machine was built from an image without it; it arrives when the',
@@ -272,18 +333,18 @@ function notInstalledMessage(env = process.env) {
272
333
  ].join('\n');
273
334
  }
274
335
  return [
275
- 'The skrr Code engine (`sky-code`) is not installed on this machine.',
336
+ `The ${displayName} engine (\`${(0, auth_core_1.firstPartyHarnessBinaryName)()}\`) is not installed on this machine.`,
276
337
  '',
277
338
  'Install it through the daemon, which verifies the signed release:',
278
- ' • `skrr code install` — from here, no browser needed',
279
- ' • Or in the web app: the runtime menu beside the chat input → skrr Code → Install',
339
+ ` • \`${command} install\` — from here, no browser needed`,
340
+ ` • Or in the web app: the runtime menu beside the chat input → ${displayName} → Install`,
280
341
  ' • Either way a daemon must be running here: `skrr daemon status`',
281
342
  '',
282
- `Or point the CLI at an existing build: export ${exports.ENGINE_PATH_ENV}=/absolute/path/to/sky-code`,
343
+ `Or point the CLI at an existing build: export ${auth_core_1.FIRST_PARTY_HARNESS_ENV.path.name}=/absolute/path/to/${(0, auth_core_1.firstPartyHarnessBinaryName)()}`,
283
344
  ' (an unsigned build runs, and since 2026-08-11 at the same trust tier as a',
284
- ' signed one — see `skrr code doctor` under trust-tier)',
345
+ ` signed one — see \`${command} doctor\` under trust-tier)`,
285
346
  '',
286
- 'Run `skrr code doctor` for a full diagnosis. Its `release-channel` line',
347
+ `Run \`${command} doctor\` for a full diagnosis. Its \`release-channel\` line`,
287
348
  'names the channel this machine follows and the exact manifest URL to check',
288
349
  'for a published release.',
289
350
  ].join('\n');
@@ -344,7 +405,7 @@ const BYOK_CREDENTIAL_NAMES = new Set(['HUGGINGFACE_HUB_TOKEN']);
344
405
  * shared config, a file the engine can read anyway. Stripping them therefore
345
406
  * protected nothing and cost an entire provider.
346
407
  *
347
- * Dogfooded as OSK-262, where `sky-code run "say hi"` answered on a Bedrock
408
+ * Dogfooded as OSK-262, where the bare engine's `run "say hi"` answered on a Bedrock
348
409
  * model and the identical run through `skrr code` died in the engine's generic
349
410
  * error handler — because `AWS_PROFILE` did not survive the sanitizer.
350
411
  * `env -u AWS_PROFILE` on the bare engine reproduced the failure exactly.
@@ -389,7 +450,7 @@ const ENGINE_NEVER_INHERITS_PREFIXES = ['OVERSKY_', 'AWS_'];
389
450
  *
390
451
  * Exported because two callers need ONE answer to "does this machine have a
391
452
  * provider key at all": this module, to decide what the engine inherits, and the
392
- * BYOK fallback line in `sky-code-broker.ts`, which used to promise "continuing
453
+ * BYOK fallback line in `first-party-harness-broker.ts`, which used to promise "continuing
393
454
  * with your own provider credentials" on a machine that had none (invariant I4 —
394
455
  * managed, BYOK and offline must never be confused). Two definitions of that
395
456
  * question is how the promise and the reality drift apart.
@@ -495,10 +556,14 @@ function strippedProviderCredentialNames(env = process.env) {
495
556
  *
496
557
  * One sharp edge to know about before adding a subcommand: the shared sanitizer
497
558
  * strips anything ending in `_PASSWORD`, which includes the engine's own
498
- * `SKY_CODE_SERVER_PASSWORD` (Basic auth for `sky-code serve` / `web`). No
499
- * `skrr code` subcommand reaches those today — they are run against the engine
500
- * binary directly — so nothing regresses; a future `skrr code serve` would have to
501
- * pass that credential deliberately rather than rely on inheritance.
559
+ * server-password variable (`FIRST_PARTY_HARNESS_ENGINE.env.serverPassword`, Basic
560
+ * auth for the engine's `serve` / `web`). No `skrr code` subcommand reaches those
561
+ * today — they are run against the engine binary directly — so nothing regresses;
562
+ * a future `skrr code serve` would have to pass that credential deliberately,
563
+ * under that name, rather than rely on inheritance.
564
+ *
565
+ * The layering, in full: sanitized parent → the user's own model credential (BYOK
566
+ * only) → {@link engineOwnedEnv} → `extraEnv`.
502
567
  */
503
568
  function engineSpawnEnv(env, extraEnv,
504
569
  // Required, not defaulted: the safe value differs per call and a parameter that
@@ -507,6 +572,7 @@ mode) {
507
572
  const spawnEnv = {
508
573
  ...(0, auth_core_1.sanitizeSpawnEnv)(env),
509
574
  ...(mode === 'managed' ? {} : inheritedProviderCredentials(env)),
575
+ ...engineOwnedEnv(env),
510
576
  ...extraEnv,
511
577
  };
512
578
  if (mode !== 'managed')
@@ -519,7 +585,40 @@ mode) {
519
585
  // otherwise have received rather than replacing it.
520
586
  return { ...spawnEnv, ...(0, inference_broker_1.inferenceBrokerNoProxyEnv)(spawnEnv) };
521
587
  }
588
+ /**
589
+ * Settings the platform reads under NEUTRAL names, handed to the engine under the
590
+ * engine's OWN names.
591
+ *
592
+ * The engine is a fork with its own variable namespace
593
+ * (`FIRST_PARTY_HARNESS_ENGINE.env`), and it reads none of the platform's neutral
594
+ * names. So an operator who sets the neutral home override would see
595
+ * `skrr code doctor` honour it while the engine it launches ignored it — two
596
+ * answers to "where do my instructions live" from one command.
597
+ *
598
+ * Only when the value came from a name the engine does NOT read: a value set under
599
+ * the engine's own name is inherited already, and rewriting it would only replace
600
+ * the user's spelling of a path with ours. The resolved path is passed rather than
601
+ * the raw value, so the engine and the doctor agree on `~` expansion too.
602
+ *
603
+ * Not a credential and never one: it cannot reintroduce anything the sanitizer
604
+ * strips, and `extraEnv` still layers over it.
605
+ */
606
+ function engineOwnedEnv(env) {
607
+ const out = {};
608
+ const home = (0, auth_core_1.readFirstPartyHarnessEnv)(env, 'home');
609
+ if (home.value && home.name !== auth_core_1.FIRST_PARTY_HARNESS_ENGINE.env.home) {
610
+ out[auth_core_1.FIRST_PARTY_HARNESS_ENGINE.env.home] = engineInstructionHome(env);
611
+ }
612
+ return out;
613
+ }
522
614
  async function execEngine(args, env = process.env, options = {}) {
615
+ // Move an engine installed under an older binary name onto the canonical one,
616
+ // BEFORE resolving. Idempotent, lossless, and it never throws: a move that
617
+ // cannot be made leaves every file where it was, and `resolveEngine` still tries
618
+ // every spelling — so the worst case is today's path, never a missing engine.
619
+ // Here and not in `resolveEngine`, which the doctor also calls and which must
620
+ // not move anything.
621
+ (0, auth_core_1.migrateFirstPartyHarnessHome)({ env });
523
622
  // Resolve the binary FIRST, before `prepare` runs.
524
623
  //
525
624
  // Order matters: a caller learns "not installed" from here, and starting a
@@ -527,7 +626,7 @@ async function execEngine(args, env = process.env, options = {}) {
527
626
  // engine that never runs.
528
627
  const { path: bin } = resolveEngine(env);
529
628
  if (!bin)
530
- throw new Error('sky-code-not-installed');
629
+ throw new Error(exports.ENGINE_NOT_INSTALLED);
531
630
  // Assumed until the spawn tells us otherwise, so a teardown that runs after a
532
631
  // spawn ERROR does not claim the run was interrupted.
533
632
  const outcome = { interrupted: false };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Harness-valued INPUT, normalised at the edge.
3
+ *
4
+ * The first-party harness has more than one accepted spelling while a rename is
5
+ * in flight (`FIRST_PARTY_HARNESS.aliases`). Every flag, setting and filter that
6
+ * takes a harness name must accept all of them — a script written against either
7
+ * spelling keeps working — and must hand the rest of the CLI ONE value, so no
8
+ * comparison downstream has to know there were two
9
+ * (`docs/architecture/skrr-code-identifier-rename-2026-09-13.md` §2).
10
+ *
11
+ * Two destinations, two spellings, and the difference is the point:
12
+ *
13
+ * - to the SERVER (HTTP bodies, query params) and to the terminal: CANONICAL,
14
+ * which is what the platform stores and returns;
15
+ * - to the local DAEMON binary: the WIRE spelling, which every daemon version
16
+ * already installed on a user's machine understands. A daemon that predates
17
+ * the rename cannot be taught a new spelling by the CLI that talks to it.
18
+ *
19
+ * Every other harness name passes through unchanged — this normalises one
20
+ * harness's spellings, it is not a general lower-caser, and silently rewriting a
21
+ * name this module knows nothing about would be a behaviour change nobody asked for.
22
+ */
23
+ /** The canonical provider for any spelling of the first-party harness; anything else trimmed. */
24
+ export declare function canonicalHarnessInput(value: string): string;
25
+ /** Canonicalise a repeatable flag, keeping the first occurrence of each value. */
26
+ export declare function canonicalHarnessInputs(values: readonly string[]): string[];
27
+ /** The spelling to hand the LOCAL daemon binary for a harness-valued argument. */
28
+ export declare function daemonHarnessArgument(value: string): string;
29
+ /**
30
+ * The `options` list for an oclif flag that enumerates harness names: the given
31
+ * names with every spelling of the first-party harness in place of its canonical
32
+ * one, so oclif's own validation accepts an alias instead of rejecting it before
33
+ * the command ever runs.
34
+ */
35
+ export declare function harnessFlagOptions(names: readonly string[]): string[];
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /**
3
+ * Harness-valued INPUT, normalised at the edge.
4
+ *
5
+ * The first-party harness has more than one accepted spelling while a rename is
6
+ * in flight (`FIRST_PARTY_HARNESS.aliases`). Every flag, setting and filter that
7
+ * takes a harness name must accept all of them — a script written against either
8
+ * spelling keeps working — and must hand the rest of the CLI ONE value, so no
9
+ * comparison downstream has to know there were two
10
+ * (`docs/architecture/skrr-code-identifier-rename-2026-09-13.md` §2).
11
+ *
12
+ * Two destinations, two spellings, and the difference is the point:
13
+ *
14
+ * - to the SERVER (HTTP bodies, query params) and to the terminal: CANONICAL,
15
+ * which is what the platform stores and returns;
16
+ * - to the local DAEMON binary: the WIRE spelling, which every daemon version
17
+ * already installed on a user's machine understands. A daemon that predates
18
+ * the rename cannot be taught a new spelling by the CLI that talks to it.
19
+ *
20
+ * Every other harness name passes through unchanged — this normalises one
21
+ * harness's spellings, it is not a general lower-caser, and silently rewriting a
22
+ * name this module knows nothing about would be a behaviour change nobody asked for.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.canonicalHarnessInput = canonicalHarnessInput;
26
+ exports.canonicalHarnessInputs = canonicalHarnessInputs;
27
+ exports.daemonHarnessArgument = daemonHarnessArgument;
28
+ exports.harnessFlagOptions = harnessFlagOptions;
29
+ const auth_core_1 = require("@skrr-ai/auth-core");
30
+ /** The canonical provider for any spelling of the first-party harness; anything else trimmed. */
31
+ function canonicalHarnessInput(value) {
32
+ const trimmed = value.trim();
33
+ return String((0, auth_core_1.canonicalHarnessProvider)(trimmed));
34
+ }
35
+ /** Canonicalise a repeatable flag, keeping the first occurrence of each value. */
36
+ function canonicalHarnessInputs(values) {
37
+ const out = [];
38
+ for (const value of values) {
39
+ const canonical = canonicalHarnessInput(value);
40
+ if (canonical && !out.includes(canonical))
41
+ out.push(canonical);
42
+ }
43
+ return out;
44
+ }
45
+ /** The spelling to hand the LOCAL daemon binary for a harness-valued argument. */
46
+ function daemonHarnessArgument(value) {
47
+ return (0, auth_core_1.wireHarnessProvider)(canonicalHarnessInput(value));
48
+ }
49
+ /**
50
+ * The `options` list for an oclif flag that enumerates harness names: the given
51
+ * names with every spelling of the first-party harness in place of its canonical
52
+ * one, so oclif's own validation accepts an alias instead of rejecting it before
53
+ * the command ever runs.
54
+ */
55
+ function harnessFlagOptions(names) {
56
+ return canonicalHarnessInputs(names).flatMap((name) => (0, auth_core_1.isFirstPartyHarnessProvider)(name) ? [...(0, auth_core_1.firstPartyHarnessSpellings)()] : [name]);
57
+ }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * ── What this file used to be, and why it changed twice ────────────────────
5
5
  *
6
- * `skrr code doctor` reported "sky-code is Tier 2 … its tool calls are adjudicated
6
+ * `skrr code doctor` reported "<the first-party harness> is Tier 2 … its tool calls are adjudicated
7
7
  * per call by the daemon" while the daemon had recorded Tier 1 since the owner
8
8
  * decision of 2026-08-11. The audience that runs `doctor` runs it BECAUSE
9
9
  * permissions are confusing them, so that line was wrong to exactly the people it
@@ -44,7 +44,13 @@ export { HARNESS_TIERS, getHarnessTier };
44
44
  */
45
45
  export declare const HARNESS_TIER_TABLE = "packages/auth-core/src/harnessTrust.ts";
46
46
  export declare const HARNESS_TIER_AUTHORITY = "daemon/src/harness-trust.ts";
47
- /** The tier the shared table records for a harness, or `null` for an unknown name. */
47
+ /**
48
+ * The tier the shared table records for a harness, or `null` for an unknown name.
49
+ *
50
+ * Canonicalised first, so no spelling of the first-party harness can reach the
51
+ * lookup as an unknown name — the shared table also carries every spelling, and
52
+ * this keeps the answer right even for one written in a different case.
53
+ */
48
54
  export declare function mirroredTier(harness: string): HarnessTrustTier | null;
49
55
  export interface TierDescription {
50
56
  /** What is true, in one line. */
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * ── What this file used to be, and why it changed twice ────────────────────
6
6
  *
7
- * `skrr code doctor` reported "sky-code is Tier 2 … its tool calls are adjudicated
7
+ * `skrr code doctor` reported "<the first-party harness> is Tier 2 … its tool calls are adjudicated
8
8
  * per call by the daemon" while the daemon had recorded Tier 1 since the owner
9
9
  * decision of 2026-08-11. The audience that runs `doctor` runs it BECAUSE
10
10
  * permissions are confusing them, so that line was wrong to exactly the people it
@@ -49,11 +49,16 @@ Object.defineProperty(exports, "getHarnessTier", { enumerable: true, get: functi
49
49
  */
50
50
  exports.HARNESS_TIER_TABLE = 'packages/auth-core/src/harnessTrust.ts';
51
51
  exports.HARNESS_TIER_AUTHORITY = 'daemon/src/harness-trust.ts';
52
- /** The tier the shared table records for a harness, or `null` for an unknown name. */
52
+ /**
53
+ * The tier the shared table records for a harness, or `null` for an unknown name.
54
+ *
55
+ * Canonicalised first, so no spelling of the first-party harness can reach the
56
+ * lookup as an unknown name — the shared table also carries every spelling, and
57
+ * this keeps the answer right even for one written in a different case.
58
+ */
53
59
  function mirroredTier(harness) {
54
- return Object.prototype.hasOwnProperty.call(auth_core_1.HARNESS_TIERS, harness)
55
- ? (0, auth_core_1.getHarnessTier)(harness)
56
- : null;
60
+ const key = String((0, auth_core_1.canonicalHarnessProvider)(harness));
61
+ return Object.prototype.hasOwnProperty.call(auth_core_1.HARNESS_TIERS, key) ? (0, auth_core_1.getHarnessTier)(key) : null;
57
62
  }
58
63
  /**
59
64
  * Render the user-facing meaning of a tier.
@@ -79,7 +84,7 @@ function describeTier(harness, tier) {
79
84
  detail: `${harness} is Tier 1: an operator-configured \`auto\` permission mode STANDS ` +
80
85
  '(it is not downgraded to per-call adjudication), and direct platform-key ' +
81
86
  'delivery is permitted. A tier still never approves an individual tool call.',
82
- remedy: 'skrr does not yet sign sky-code releases, so in practice an UNSIGNED build can ' +
87
+ remedy: `skrr does not yet sign ${auth_core_1.FIRST_PARTY_HARNESS.displayName} releases, so in practice an UNSIGNED build can ` +
83
88
  'receive the platform master key. The provenance machinery still runs and still ' +
84
89
  'verifies binaries — signing stops being what earns the key and becomes what lets ' +
85
90
  `you prove which bytes held it. Reverting to the strict posture is one line in ${exports.HARNESS_TIER_TABLE}.`,
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.maybeOfferTaskManagementInstructions = maybeOfferTaskManagementInstructions;
4
+ const auth_core_1 = require("@skrr-ai/auth-core");
4
5
  const data_provider_1 = require("@skrr-ai/data-provider");
5
6
  const config_1 = require("./config");
6
7
  const daemon_target_1 = require("./daemon-target");
@@ -31,8 +32,11 @@ function detectedProviders(daemon) {
31
32
  providers.push('claude');
32
33
  if (capabilities.includes('codex_session'))
33
34
  providers.push('codex');
34
- if (capabilities.includes('sky-code_session'))
35
- providers.push('sky-code');
35
+ // A daemon advertises the first-party harness under whichever spelling its
36
+ // version speaks; any of them means the harness is there, and the offer names
37
+ // it canonically, which is what the server stores.
38
+ if (capabilities.some(auth_core_1.isFirstPartyHarnessCapability))
39
+ providers.push(auth_core_1.FIRST_PARTY_HARNESS_PROVIDER);
36
40
  return providers;
37
41
  }
38
42
  function providerLabel(provider) {
@@ -40,14 +44,16 @@ function providerLabel(provider) {
40
44
  return 'Claude Code';
41
45
  if (provider === 'codex')
42
46
  return 'Codex';
43
- return 'skrr Code';
47
+ return auth_core_1.FIRST_PARTY_HARNESS.displayName;
44
48
  }
45
49
  function providerPath(provider) {
46
50
  if (provider === 'claude')
47
51
  return '~/.claude/CLAUDE.md';
48
52
  if (provider === 'codex')
49
53
  return '~/.codex/AGENTS.md';
50
- return '~/.skrr/sky-code/AGENTS.md';
54
+ // The engine home follows the ENGINE's namespace, not the provider slug — the
55
+ // engine reads its managed `AGENTS.md` from there (contract §5).
56
+ return `~/.skrr/${(0, auth_core_1.firstPartyHarnessHomeDirname)()}/AGENTS.md`;
51
57
  }
52
58
  function shouldSkipOffer() {
53
59
  return Boolean(!process.stdin.isTTY ||
@@ -14,8 +14,8 @@ exports.renderWorklogEntry = renderWorklogEntry;
14
14
  * ── Why this file exists ──────────────────────────────────────────────────
15
15
  *
16
16
  * Worklog writing was an MCP capability, and MCP tools execute in the CLOUD.
17
- * An agent pinned to a LOCAL daemon runtime — Claude Code, Codex, skrr Code
18
- * alike — could read a worklog and could not write one, because the route
17
+ * An agent pinned to a LOCAL daemon runtime — Claude Code, Codex, the
18
+ * first-party harness alike — could read a worklog and could not write one, because the route
19
19
  * exposed three endpoints and all three were reads (OSK-3495).
20
20
  *
21
21
  * That is the same gap the CLI already closes for every sibling domain: tasks,