@skrr-ai/cli 0.1.41 → 0.1.43
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.
- package/dist/commands/balance/overage.js +4 -3
- package/dist/commands/balance/plan.js +10 -6
- package/dist/commands/balance/usage/events.js +3 -2
- package/dist/commands/balance/usage.js +2 -1
- package/dist/commands/browser/install.js +3 -2
- package/dist/commands/browser/uninstall.js +3 -2
- package/dist/commands/code/install.js +6 -6
- package/dist/commands/followups/remind.js +4 -0
- package/dist/commands/harnesses/install.d.ts +29 -0
- package/dist/commands/harnesses/install.js +153 -25
- package/dist/commands/harnesses/installers.d.ts +10 -0
- package/dist/commands/harnesses/installers.js +31 -0
- package/dist/commands/harnesses/list.js +3 -2
- package/dist/commands/skills/import-as-actions.js +6 -15
- package/dist/commands/tasks/attachments/set-role.d.ts +24 -0
- package/dist/commands/tasks/attachments/set-role.js +60 -0
- package/dist/commands/tasks/attachments/upload.d.ts +1 -0
- package/dist/commands/tasks/attachments/upload.js +18 -52
- package/dist/commands/tasks/comments/add.d.ts +1 -0
- package/dist/commands/tasks/comments/add.js +67 -2
- package/dist/commands/tasks/complete.d.ts +1 -0
- package/dist/commands/tasks/complete.js +69 -1
- package/dist/commands/tasks/result/submit.d.ts +3 -0
- package/dist/commands/tasks/result/submit.js +78 -5
- package/dist/commands/tasks/update.js +4 -2
- package/dist/commands/tasks/updates/add.d.ts +1 -0
- package/dist/commands/tasks/updates/add.js +63 -0
- package/dist/lib/api-fetch.js +7 -2
- package/dist/lib/cli-installers.d.ts +63 -4
- package/dist/lib/cli-installers.js +98 -8
- package/dist/lib/dedicated-machines.js +4 -2
- package/dist/lib/file-mime.js +1 -1
- package/dist/lib/first-party-harness-agent.js +6 -5
- package/dist/lib/first-party-harness-doctor.js +15 -32
- package/dist/lib/first-party-harness-managed.d.ts +9 -4
- package/dist/lib/first-party-harness-managed.js +13 -10
- package/dist/lib/first-party-harness.d.ts +25 -21
- package/dist/lib/first-party-harness.js +40 -27
- package/dist/lib/followups.d.ts +2 -0
- package/dist/lib/harness-provider-input.d.ts +18 -12
- package/dist/lib/harness-provider-input.js +18 -12
- package/dist/lib/harness-tiers.d.ts +4 -3
- package/dist/lib/harness-tiers.js +4 -3
- package/dist/lib/html-text.js +25 -0
- package/dist/lib/local-skills.d.ts +27 -0
- package/dist/lib/local-skills.js +38 -0
- package/dist/lib/session-task-endpoints.d.ts +1 -1
- package/dist/lib/session-task-endpoints.js +3 -0
- package/dist/lib/task-asset-upload.d.ts +84 -0
- package/dist/lib/task-asset-upload.js +374 -0
- package/dist/lib/task-closure.d.ts +28 -0
- package/dist/lib/task-closure.js +37 -0
- package/dist/lib/tasks.d.ts +8 -0
- package/dist/lib/tasks.js +16 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +113 -39
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +148 -71
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +21 -33
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +24 -50
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.d.ts +19 -7
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.js +26 -10
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.d.ts +9 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.js +9 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +1 -13
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +113 -39
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +146 -70
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +21 -33
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +24 -49
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.d.ts +19 -7
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.js +27 -11
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.d.ts +9 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.js +9 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -4
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +4025 -3938
- package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
- package/oclif.manifest.json +12853 -12721
- package/package.json +2 -2
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
* Every product-named value — the binary's file name, the engine home, the env
|
|
20
20
|
* var names, the display name — is read from the identity module in
|
|
21
21
|
* `@skrr-ai/auth-core` (`docs/architecture/skrr-code-identifier-rename-2026-09-13.md`).
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* An installed engine is an ARTIFACT: one installed before a rename is still on
|
|
23
|
+
* disk under its old file name after that name stops being accepted anywhere, so
|
|
24
|
+
* resolution tries every artifact spelling (`firstPartyHarnessArtifactSpellings`),
|
|
25
|
+
* canonical first, in each location. That recognises a file; it admits no input.
|
|
24
26
|
*/
|
|
25
27
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
28
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -81,14 +83,15 @@ function engineHome(env = process.env) {
|
|
|
81
83
|
/**
|
|
82
84
|
* The user-level instruction root the engine owns.
|
|
83
85
|
*
|
|
84
|
-
* Read under the neutral variable first
|
|
85
|
-
* (`
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* engine
|
|
86
|
+
* Read under the platform's neutral variable first, then under the variable the
|
|
87
|
+
* ENGINE itself reads (`FIRST_PARTY_HARNESS_ENGINE.env.home`). The second is not a
|
|
88
|
+
* legacy fallback: it is the engine's own name, the engine honours it whatever the
|
|
89
|
+
* platform does, and a CLI that ignored it would report one instruction root while
|
|
90
|
+
* the engine it launches used another. {@link engineOwnedEnv} closes the other
|
|
91
|
+
* direction, handing a neutral-variable value to the engine under its own name.
|
|
89
92
|
*/
|
|
90
93
|
function engineInstructionHome(env = process.env) {
|
|
91
|
-
const configured = (0, auth_core_1.readFirstPartyHarnessEnv)(env, 'home').value;
|
|
94
|
+
const configured = (0, auth_core_1.readFirstPartyHarnessEnv)(env, 'home').value ?? engineHomeVariable(env);
|
|
92
95
|
// Falls through to `engineHome()` rather than re-deriving `~/.skrr` — the two
|
|
93
96
|
// describe the same root, and re-deriving it is what made them disagree under
|
|
94
97
|
// `OVERSKY_CONFIG_DIR` (OSK-300).
|
|
@@ -100,6 +103,11 @@ function engineInstructionHome(env = process.env) {
|
|
|
100
103
|
return node_path_1.default.join((0, node_os_1.homedir)(), configured.slice(2));
|
|
101
104
|
return node_path_1.default.resolve(configured);
|
|
102
105
|
}
|
|
106
|
+
/** The engine's own home variable, trimmed; undefined when unset or blank. */
|
|
107
|
+
function engineHomeVariable(env) {
|
|
108
|
+
const raw = env[auth_core_1.FIRST_PARTY_HARNESS_ENGINE.env.home];
|
|
109
|
+
return typeof raw === 'string' && raw.trim() !== '' ? raw.trim() : undefined;
|
|
110
|
+
}
|
|
103
111
|
/**
|
|
104
112
|
* The managed install location for one spelling of the binary (canonical by
|
|
105
113
|
* default) — where the daemon, the Desktop app, and
|
|
@@ -109,8 +117,9 @@ function managedEnginePath(env = process.env, provider = auth_core_1.FIRST_PARTY
|
|
|
109
117
|
return (0, auth_core_1.firstPartyHarnessInstalledBinaryPath)(env, provider, process.platform);
|
|
110
118
|
}
|
|
111
119
|
/**
|
|
112
|
-
* The same managed location under the PREVIOUS config root, for every
|
|
113
|
-
* the binary and every engine home name,
|
|
120
|
+
* The same managed location under the PREVIOUS config root, for every artifact
|
|
121
|
+
* spelling of the binary (former spellings included) and every engine home name,
|
|
122
|
+
* canonical first.
|
|
114
123
|
*
|
|
115
124
|
* The root moved `~/.oversky` → `~/.skrr` in the skrr rename, and an engine
|
|
116
125
|
* installed before that is still on disk under the old one. `daemon/src/
|
|
@@ -120,7 +129,7 @@ function managedEnginePath(env = process.env, provider = auth_core_1.FIRST_PARTY
|
|
|
120
129
|
*
|
|
121
130
|
* The engine-home move (`migrateFirstPartyHarnessHome`) only ever works under the
|
|
122
131
|
* CURRENT root, so nothing renames a binary here — which is why this list carries
|
|
123
|
-
* every spelling rather than only the canonical one.
|
|
132
|
+
* every artifact spelling rather than only the canonical one.
|
|
124
133
|
*
|
|
125
134
|
* Empty when an explicit `OVERSKY_CONFIG_DIR` is set, matching the daemon: that
|
|
126
135
|
* override names one root deliberately, and reaching past it to a hard-coded home
|
|
@@ -149,11 +158,13 @@ function fallbackPaths(exeName) {
|
|
|
149
158
|
];
|
|
150
159
|
}
|
|
151
160
|
/**
|
|
152
|
-
* The file names a PATH directory may hold the engine under, canonical first
|
|
153
|
-
*
|
|
161
|
+
* The file names a PATH directory may hold the engine under, canonical first —
|
|
162
|
+
* every artifact spelling, since a standalone install made before a rename keeps
|
|
163
|
+
* its old name. Windows also accepts a `.cmd` shim, as a package manager may
|
|
164
|
+
* install one.
|
|
154
165
|
*/
|
|
155
166
|
function pathExecutableNames(platform) {
|
|
156
|
-
return (0, auth_core_1.
|
|
167
|
+
return (0, auth_core_1.firstPartyHarnessArtifactSpellings)().flatMap((spelling) => platform === 'win32'
|
|
157
168
|
? [(0, auth_core_1.firstPartyHarnessBinaryName)(spelling, platform), `${spelling}.cmd`]
|
|
158
169
|
: [(0, auth_core_1.firstPartyHarnessBinaryName)(spelling, platform)]);
|
|
159
170
|
}
|
|
@@ -176,12 +187,12 @@ function isExecutable(p) {
|
|
|
176
187
|
* The env override is checked first and is absolute-only. A relative override
|
|
177
188
|
* would resolve against the caller's cwd, which for an agent-invoked command is
|
|
178
189
|
* attacker-influenced: a repo containing a file named like the engine could
|
|
179
|
-
* hijack it.
|
|
190
|
+
* hijack it.
|
|
180
191
|
*
|
|
181
192
|
* **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
|
|
183
|
-
* so an engine installed under an older name still resolves,
|
|
184
|
-
* one wins wherever both exist.
|
|
193
|
+
* every artifact spelling of the binary is tried, canonical first — the daemon's
|
|
194
|
+
* order — so an engine installed under an older or former name still resolves,
|
|
195
|
+
* and the canonical one wins wherever both exist.
|
|
185
196
|
*
|
|
186
197
|
* The managed location beating `PATH` is the load-bearing part, and it is a
|
|
187
198
|
* deliberate reversal of the obvious ordering.
|
|
@@ -210,7 +221,7 @@ function resolveEngine(env = process.env) {
|
|
|
210
221
|
const platform = process.platform;
|
|
211
222
|
const override = (0, auth_core_1.readFirstPartyHarnessEnv)(env, 'path');
|
|
212
223
|
if (override.value && override.name) {
|
|
213
|
-
const origin = { envName: override.name
|
|
224
|
+
const origin = { envName: override.name };
|
|
214
225
|
const candidate = override.value;
|
|
215
226
|
if (!node_path_1.default.isAbsolute(candidate))
|
|
216
227
|
return { path: null, source: 'unresolved', ...origin };
|
|
@@ -218,7 +229,7 @@ function resolveEngine(env = process.env) {
|
|
|
218
229
|
? { path: candidate, source: 'env', ...origin }
|
|
219
230
|
: { path: null, source: 'unresolved', ...origin };
|
|
220
231
|
}
|
|
221
|
-
for (const spelling of (0, auth_core_1.
|
|
232
|
+
for (const spelling of (0, auth_core_1.firstPartyHarnessArtifactSpellings)()) {
|
|
222
233
|
const managed = managedEnginePath(env, spelling);
|
|
223
234
|
if (isExecutable(managed))
|
|
224
235
|
return { path: managed, source: 'managed' };
|
|
@@ -595,18 +606,19 @@ mode) {
|
|
|
595
606
|
* `skrr code doctor` honour it while the engine it launches ignored it — two
|
|
596
607
|
* answers to "where do my instructions live" from one command.
|
|
597
608
|
*
|
|
598
|
-
* Only when the
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
609
|
+
* Only when the neutral variable supplied the value: a value set only under the
|
|
610
|
+
* engine's own name is inherited already, and rewriting it would only replace the
|
|
611
|
+
* user's spelling of a path with ours. When both are set the neutral one wins, as
|
|
612
|
+
* it does in {@link engineInstructionHome}, so the engine is handed that one. The
|
|
613
|
+
* resolved path is passed rather than the raw value, so the engine and the doctor
|
|
614
|
+
* agree on `~` expansion too.
|
|
602
615
|
*
|
|
603
616
|
* Not a credential and never one: it cannot reintroduce anything the sanitizer
|
|
604
617
|
* strips, and `extraEnv` still layers over it.
|
|
605
618
|
*/
|
|
606
619
|
function engineOwnedEnv(env) {
|
|
607
620
|
const out = {};
|
|
608
|
-
|
|
609
|
-
if (home.value && home.name !== auth_core_1.FIRST_PARTY_HARNESS_ENGINE.env.home) {
|
|
621
|
+
if ((0, auth_core_1.readFirstPartyHarnessEnv)(env, 'home').value) {
|
|
610
622
|
out[auth_core_1.FIRST_PARTY_HARNESS_ENGINE.env.home] = engineInstructionHome(env);
|
|
611
623
|
}
|
|
612
624
|
return out;
|
|
@@ -615,7 +627,8 @@ async function execEngine(args, env = process.env, options = {}) {
|
|
|
615
627
|
// Move an engine installed under an older binary name onto the canonical one,
|
|
616
628
|
// BEFORE resolving. Idempotent, lossless, and it never throws: a move that
|
|
617
629
|
// 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
|
|
630
|
+
// every artifact spelling — so the worst case is today's path, never a missing
|
|
631
|
+
// engine.
|
|
619
632
|
// Here and not in `resolveEngine`, which the doctor also calls and which must
|
|
620
633
|
// not move anything.
|
|
621
634
|
(0, auth_core_1.migrateFirstPartyHarnessHome)({ env });
|
package/dist/lib/followups.d.ts
CHANGED
|
@@ -56,6 +56,8 @@ export interface FollowUpRow {
|
|
|
56
56
|
export interface FollowUpCreateResponse {
|
|
57
57
|
followup: FollowUpRow | null;
|
|
58
58
|
reused?: boolean;
|
|
59
|
+
/** A reused reminder asked for at a different time moved to that time. */
|
|
60
|
+
rescheduled?: boolean;
|
|
59
61
|
dropped?: false | 'budget';
|
|
60
62
|
proposal?: boolean;
|
|
61
63
|
budget?: Json;
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Harness-valued INPUT, normalised at the edge.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* INGRESS: every flag, setting and filter that takes a harness name accepts
|
|
5
|
+
* exactly the identity's ACCEPTED spellings (`firstPartyHarnessSpellings`) and
|
|
6
|
+
* hands the rest of the CLI ONE value, so no comparison downstream has to know
|
|
7
|
+
* whether there were several (`docs/architecture/skrr-code-identifier-rename-2026-09-13.md`
|
|
8
|
+
* §2). During a rename's alias window that is the canonical spelling plus
|
|
9
|
+
* `FIRST_PARTY_HARNESS.aliases`, so a script written against either keeps
|
|
10
|
+
* working; outside one it is the canonical spelling alone. A FORMER spelling is
|
|
11
|
+
* accepted on no input — it is passed through unchanged, so a flag offering only
|
|
12
|
+
* the accepted spellings rejects it — even though the CLI still recognises it on
|
|
13
|
+
* an engine binary already on disk (`first-party-harness.ts`).
|
|
10
14
|
*
|
|
11
15
|
* Two destinations, two spellings, and the difference is the point:
|
|
12
16
|
*
|
|
13
17
|
* - to the SERVER (HTTP bodies, query params) and to the terminal: CANONICAL,
|
|
14
18
|
* which is what the platform stores and returns;
|
|
15
|
-
* - to the local DAEMON binary: the WIRE spelling
|
|
16
|
-
*
|
|
17
|
-
* the rename cannot be taught a new spelling by the CLI that
|
|
19
|
+
* - to the local DAEMON binary: the WIRE spelling (`wireProvider`). It sits on
|
|
20
|
+
* the old spelling from a rename's flip until its wire phase, because a daemon
|
|
21
|
+
* that predates the rename cannot be taught a new spelling by the CLI that
|
|
22
|
+
* talks to it, and is canonical otherwise.
|
|
18
23
|
*
|
|
19
24
|
* Every other harness name passes through unchanged — this normalises one
|
|
20
25
|
* harness's spellings, it is not a general lower-caser, and silently rewriting a
|
|
@@ -28,8 +33,9 @@ export declare function canonicalHarnessInputs(values: readonly string[]): strin
|
|
|
28
33
|
export declare function daemonHarnessArgument(value: string): string;
|
|
29
34
|
/**
|
|
30
35
|
* 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
|
|
32
|
-
* one, so oclif's own validation accepts an alias instead of rejecting
|
|
33
|
-
* the command ever runs
|
|
36
|
+
* names with every ACCEPTED spelling of the first-party harness in place of its
|
|
37
|
+
* canonical one, so oclif's own validation accepts an alias instead of rejecting
|
|
38
|
+
* it before the command ever runs — and rejects a former spelling, which no
|
|
39
|
+
* ingress accepts.
|
|
34
40
|
*/
|
|
35
41
|
export declare function harnessFlagOptions(names: readonly string[]): string[];
|
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Harness-valued INPUT, normalised at the edge.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* INGRESS: every flag, setting and filter that takes a harness name accepts
|
|
6
|
+
* exactly the identity's ACCEPTED spellings (`firstPartyHarnessSpellings`) and
|
|
7
|
+
* hands the rest of the CLI ONE value, so no comparison downstream has to know
|
|
8
|
+
* whether there were several (`docs/architecture/skrr-code-identifier-rename-2026-09-13.md`
|
|
9
|
+
* §2). During a rename's alias window that is the canonical spelling plus
|
|
10
|
+
* `FIRST_PARTY_HARNESS.aliases`, so a script written against either keeps
|
|
11
|
+
* working; outside one it is the canonical spelling alone. A FORMER spelling is
|
|
12
|
+
* accepted on no input — it is passed through unchanged, so a flag offering only
|
|
13
|
+
* the accepted spellings rejects it — even though the CLI still recognises it on
|
|
14
|
+
* an engine binary already on disk (`first-party-harness.ts`).
|
|
11
15
|
*
|
|
12
16
|
* Two destinations, two spellings, and the difference is the point:
|
|
13
17
|
*
|
|
14
18
|
* - to the SERVER (HTTP bodies, query params) and to the terminal: CANONICAL,
|
|
15
19
|
* which is what the platform stores and returns;
|
|
16
|
-
* - to the local DAEMON binary: the WIRE spelling
|
|
17
|
-
*
|
|
18
|
-
* the rename cannot be taught a new spelling by the CLI that
|
|
20
|
+
* - to the local DAEMON binary: the WIRE spelling (`wireProvider`). It sits on
|
|
21
|
+
* the old spelling from a rename's flip until its wire phase, because a daemon
|
|
22
|
+
* that predates the rename cannot be taught a new spelling by the CLI that
|
|
23
|
+
* talks to it, and is canonical otherwise.
|
|
19
24
|
*
|
|
20
25
|
* Every other harness name passes through unchanged — this normalises one
|
|
21
26
|
* harness's spellings, it is not a general lower-caser, and silently rewriting a
|
|
@@ -48,9 +53,10 @@ function daemonHarnessArgument(value) {
|
|
|
48
53
|
}
|
|
49
54
|
/**
|
|
50
55
|
* 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
|
|
52
|
-
* one, so oclif's own validation accepts an alias instead of rejecting
|
|
53
|
-
* the command ever runs
|
|
56
|
+
* names with every ACCEPTED spelling of the first-party harness in place of its
|
|
57
|
+
* canonical one, so oclif's own validation accepts an alias instead of rejecting
|
|
58
|
+
* it before the command ever runs — and rejects a former spelling, which no
|
|
59
|
+
* ingress accepts.
|
|
54
60
|
*/
|
|
55
61
|
function harnessFlagOptions(names) {
|
|
56
62
|
return canonicalHarnessInputs(names).flatMap((name) => (0, auth_core_1.isFirstPartyHarnessProvider)(name) ? [...(0, auth_core_1.firstPartyHarnessSpellings)()] : [name]);
|
|
@@ -47,9 +47,10 @@ export declare const HARNESS_TIER_AUTHORITY = "daemon/src/harness-trust.ts";
|
|
|
47
47
|
/**
|
|
48
48
|
* The tier the shared table records for a harness, or `null` for an unknown name.
|
|
49
49
|
*
|
|
50
|
-
* Canonicalised first, so no spelling of the first-party harness can reach
|
|
51
|
-
* lookup as an unknown name — the shared table also carries every
|
|
52
|
-
* this keeps the answer right even for one written in a different
|
|
50
|
+
* Canonicalised first, so no accepted spelling of the first-party harness can reach
|
|
51
|
+
* the lookup as an unknown name — the shared table also carries every accepted
|
|
52
|
+
* spelling, and this keeps the answer right even for one written in a different
|
|
53
|
+
* case. A retired spelling is an unknown name here, as everywhere trust is decided.
|
|
53
54
|
*/
|
|
54
55
|
export declare function mirroredTier(harness: string): HarnessTrustTier | null;
|
|
55
56
|
export interface TierDescription {
|
|
@@ -52,9 +52,10 @@ exports.HARNESS_TIER_AUTHORITY = 'daemon/src/harness-trust.ts';
|
|
|
52
52
|
/**
|
|
53
53
|
* The tier the shared table records for a harness, or `null` for an unknown name.
|
|
54
54
|
*
|
|
55
|
-
* Canonicalised first, so no spelling of the first-party harness can reach
|
|
56
|
-
* lookup as an unknown name — the shared table also carries every
|
|
57
|
-
* this keeps the answer right even for one written in a different
|
|
55
|
+
* Canonicalised first, so no accepted spelling of the first-party harness can reach
|
|
56
|
+
* the lookup as an unknown name — the shared table also carries every accepted
|
|
57
|
+
* spelling, and this keeps the answer right even for one written in a different
|
|
58
|
+
* case. A retired spelling is an unknown name here, as everywhere trust is decided.
|
|
58
59
|
*/
|
|
59
60
|
function mirroredTier(harness) {
|
|
60
61
|
const key = String((0, auth_core_1.canonicalHarnessProvider)(harness));
|
package/dist/lib/html-text.js
CHANGED
|
@@ -19,10 +19,35 @@ exports.htmlToDisplayText = htmlToDisplayText;
|
|
|
19
19
|
function looksLikeHtml(value) {
|
|
20
20
|
return /<\/?[a-z][^>]*>/i.test(value);
|
|
21
21
|
}
|
|
22
|
+
/** Pull one attribute out of a tag body — `src="…"`, `src='…'`, `src=…`. */
|
|
23
|
+
function tagAttr(tag, name) {
|
|
24
|
+
const m = new RegExp(`${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, 'i').exec(tag);
|
|
25
|
+
return m ? (m[1] ?? m[2] ?? m[3] ?? '') : '';
|
|
26
|
+
}
|
|
22
27
|
function htmlToDisplayText(value) {
|
|
23
28
|
if (!looksLikeHtml(value))
|
|
24
29
|
return value;
|
|
25
30
|
return (value
|
|
31
|
+
// A <video> is the same kind of meaning as an <img>, and the tag strip
|
|
32
|
+
// would erase it the same way, leaving a terminal reader no sign the
|
|
33
|
+
// description holds a recording at all. Markdown has no video syntax, so
|
|
34
|
+
// it becomes a labelled link to the stable reference. The element's
|
|
35
|
+
// children — `<source>` and fallback text — go with it; a `<source>` src
|
|
36
|
+
// is used when the element itself carries none (OSK-10129).
|
|
37
|
+
.replace(/<video\b([^>]*)>([\s\S]*?)<\/video\s*>|<video\b([^>]*)\/?>/gi, (_m, open, inner, bare) => {
|
|
38
|
+
const tag = String(open ?? bare ?? '');
|
|
39
|
+
const src = tagAttr(tag, 'src') || tagAttr(/<source\b[^>]*>/i.exec(inner ?? '')?.[0] ?? '', 'src');
|
|
40
|
+
const title = tagAttr(tag, 'title');
|
|
41
|
+
return src ? `[video: ${title || 'recording'}](${src})\n` : '';
|
|
42
|
+
})
|
|
43
|
+
// An <img> carries meaning — the asset reference or URL — that the
|
|
44
|
+
// generic tag strip below would erase entirely. Keep it as a markdown
|
|
45
|
+
// image so `task-asset:<id>` refs stay legible in a terminal (OSK-9856).
|
|
46
|
+
.replace(/<img\b[^>]*>/gi, (tag) => {
|
|
47
|
+
const src = tagAttr(tag, 'src');
|
|
48
|
+
const alt = tagAttr(tag, 'alt');
|
|
49
|
+
return src ? `` : '';
|
|
50
|
+
})
|
|
26
51
|
.replace(/<br\s*\/?>/gi, '\n')
|
|
27
52
|
.replace(/<\/(p|div|li|h[1-6])>/gi, '\n')
|
|
28
53
|
.replace(/<li[^>]*>/gi, ' - ')
|
|
@@ -112,3 +112,30 @@ export declare const SKILL_CONVENTION_DISCOVERY_SINCE = "0.8.44";
|
|
|
112
112
|
* add an explanation, and claiming an unknown daemon is old would be a guess.
|
|
113
113
|
*/
|
|
114
114
|
export declare function daemonPredates(version: string | null | undefined, floor: string): boolean;
|
|
115
|
+
/**
|
|
116
|
+
* The table, summary and exit decision for `skills import-as-actions`.
|
|
117
|
+
*
|
|
118
|
+
* A skill that imported without its action because the library was full used to
|
|
119
|
+
* show only `limit_reached`, an empty detail, "0 skill(s) failed" and exit 0, so
|
|
120
|
+
* a script or agent concluded every action it asked for was added (OSK-10185).
|
|
121
|
+
* `already_exists` stays a success: the action the caller wanted is there.
|
|
122
|
+
*/
|
|
123
|
+
export declare function summarizeSkillActionImport(result: {
|
|
124
|
+
targetAgentId: string;
|
|
125
|
+
actionsCreated: number;
|
|
126
|
+
results: Array<{
|
|
127
|
+
name: string;
|
|
128
|
+
status: string;
|
|
129
|
+
error?: string;
|
|
130
|
+
actionCreated?: boolean;
|
|
131
|
+
actionSkippedReason?: string;
|
|
132
|
+
actionError?: string;
|
|
133
|
+
}>;
|
|
134
|
+
actionsError?: {
|
|
135
|
+
message: string;
|
|
136
|
+
} | null;
|
|
137
|
+
}): {
|
|
138
|
+
rows: Array<Record<string, string>>;
|
|
139
|
+
summary: string;
|
|
140
|
+
failed: boolean;
|
|
141
|
+
};
|
package/dist/lib/local-skills.js
CHANGED
|
@@ -10,6 +10,7 @@ exports.looksLikeSkillPath = looksLikeSkillPath;
|
|
|
10
10
|
exports.findLocalSkillsNamed = findLocalSkillsNamed;
|
|
11
11
|
exports.describeStoreSkillMiss = describeStoreSkillMiss;
|
|
12
12
|
exports.daemonPredates = daemonPredates;
|
|
13
|
+
exports.summarizeSkillActionImport = summarizeSkillActionImport;
|
|
13
14
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
14
15
|
const instruction_input_1 = require("./instruction-input");
|
|
15
16
|
/**
|
|
@@ -211,3 +212,40 @@ function daemonPredates(version, floor) {
|
|
|
211
212
|
}
|
|
212
213
|
return false;
|
|
213
214
|
}
|
|
215
|
+
/** Why a skill imported but got no action, in words a person or an agent can act on. */
|
|
216
|
+
const ACTION_SKIP_DETAIL = {
|
|
217
|
+
limit_reached: "this agent's action library is full; delete an action, then run the import again to add it",
|
|
218
|
+
already_exists: 'an action for this skill is already offered at this scope',
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* The table, summary and exit decision for `skills import-as-actions`.
|
|
222
|
+
*
|
|
223
|
+
* A skill that imported without its action because the library was full used to
|
|
224
|
+
* show only `limit_reached`, an empty detail, "0 skill(s) failed" and exit 0, so
|
|
225
|
+
* a script or agent concluded every action it asked for was added (OSK-10185).
|
|
226
|
+
* `already_exists` stays a success: the action the caller wanted is there.
|
|
227
|
+
*/
|
|
228
|
+
function summarizeSkillActionImport(result) {
|
|
229
|
+
const failed = result.results.filter((r) => r.status === 'failed');
|
|
230
|
+
const overCap = result.results.filter((r) => r.status !== 'failed' && r.actionSkippedReason === 'limit_reached');
|
|
231
|
+
const rows = result.results.map((r) => ({
|
|
232
|
+
skill: r.name,
|
|
233
|
+
import: r.status,
|
|
234
|
+
action: r.actionCreated
|
|
235
|
+
? 'created'
|
|
236
|
+
: (r.actionSkippedReason ?? r.actionError ?? (r.status === 'failed' ? '-' : 'not added')),
|
|
237
|
+
detail: r.error ??
|
|
238
|
+
r.actionError ??
|
|
239
|
+
(r.actionSkippedReason ? (ACTION_SKIP_DETAIL[r.actionSkippedReason] ?? '') : ''),
|
|
240
|
+
}));
|
|
241
|
+
const summary = `${result.actionsCreated} action(s) created on ${result.targetAgentId}; ` +
|
|
242
|
+
`${failed.length} skill(s) failed` +
|
|
243
|
+
(overCap.length > 0
|
|
244
|
+
? `; ${overCap.length} skill(s) imported without an action because the action library is full.`
|
|
245
|
+
: '.');
|
|
246
|
+
return {
|
|
247
|
+
rows,
|
|
248
|
+
summary,
|
|
249
|
+
failed: failed.length > 0 || overCap.length > 0 || Boolean(result.actionsError),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* is no `task-read` and adding one to make the table tidy would name a resource
|
|
8
8
|
* after an access mode.
|
|
9
9
|
*/
|
|
10
|
-
export type SessionTaskOperation = 'completion' | 'move' | 'comment' | 'event' | 'update' | 'result' | 'resume' | 'resumeRead' | 'deliverable' | 'review' | 'assessment' | 'read';
|
|
10
|
+
export type SessionTaskOperation = 'completion' | 'move' | 'comment' | 'event' | 'update' | 'result' | 'resume' | 'resumeRead' | 'deliverable' | 'review' | 'assessment' | 'attachment' | 'attachmentIntent' | 'attachmentComplete' | 'read';
|
|
11
11
|
/** Path suffix per operation. Exported for the parity test, not for callers. */
|
|
12
12
|
export declare const SESSION_TASK_PATH_SUFFIX: Record<SessionTaskOperation, string>;
|
|
13
13
|
/**
|
|
@@ -47,6 +47,9 @@ exports.SESSION_TASK_PATH_SUFFIX = {
|
|
|
47
47
|
deliverable: 'task-deliverables',
|
|
48
48
|
review: 'task-review',
|
|
49
49
|
assessment: 'task-expectation-assessment',
|
|
50
|
+
attachment: 'task-attachments',
|
|
51
|
+
attachmentIntent: 'task-attachments/intent',
|
|
52
|
+
attachmentComplete: 'task-attachments/complete',
|
|
50
53
|
read: 'task',
|
|
51
54
|
};
|
|
52
55
|
/**
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export type TaskAssetRole = 'input' | 'evidence' | 'deliverable';
|
|
2
|
+
export interface UploadedTaskAsset {
|
|
3
|
+
id: string;
|
|
4
|
+
/**
|
|
5
|
+
* sha256 (hex) of the bytes the server STORED, as the server recorded them.
|
|
6
|
+
* Absent when the server did not report one — never back-filled from the
|
|
7
|
+
* local file, which is a different version for any canonicalized image.
|
|
8
|
+
*/
|
|
9
|
+
digest?: string;
|
|
10
|
+
filename: string;
|
|
11
|
+
mimeType: string;
|
|
12
|
+
/** True when the server returned an existing row for the same sha256. */
|
|
13
|
+
deduplicated: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** The citation an Update or Result appends to `evidenceRefs`. */
|
|
16
|
+
export interface TaskAssetEvidenceRef {
|
|
17
|
+
kind: 'task_attachment';
|
|
18
|
+
id: string;
|
|
19
|
+
/** `sha256:<hex>` of the stored bytes; omitted when the server named none. */
|
|
20
|
+
digest?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read the Task an upload targets, through the same credential the upload
|
|
24
|
+
* will use. In a session the task comes from the claim, so the read is the
|
|
25
|
+
* bound task and `taskId` is only reconciled against it — a scoped credential
|
|
26
|
+
* refuses to act on any other task before a byte leaves the machine.
|
|
27
|
+
*/
|
|
28
|
+
export declare function readTaskForUpload(taskId: string): Promise<Record<string, unknown>>;
|
|
29
|
+
/**
|
|
30
|
+
* Upload `filePath` as a Task asset. In a session the task comes from the
|
|
31
|
+
* claim — `taskId` is only reconciled against the binding, never sent.
|
|
32
|
+
* Throws a plain Error; callers decide how to surface it.
|
|
33
|
+
*/
|
|
34
|
+
export declare function uploadTaskAssetFile(opts: {
|
|
35
|
+
taskId: string;
|
|
36
|
+
filePath: string;
|
|
37
|
+
role?: TaskAssetRole;
|
|
38
|
+
}): Promise<UploadedTaskAsset>;
|
|
39
|
+
/**
|
|
40
|
+
* Upload every `--attach` file and return the reference objects an Update or
|
|
41
|
+
* Result appends to `evidenceRefs`. Serial: half a dozen files is the
|
|
42
|
+
* realistic case and parallel uploads would race the session rate limiter.
|
|
43
|
+
*/
|
|
44
|
+
export declare function attachTaskAssetFiles(opts: {
|
|
45
|
+
taskId: string;
|
|
46
|
+
files: string[];
|
|
47
|
+
role: TaskAssetRole;
|
|
48
|
+
}): Promise<TaskAssetEvidenceRef[]>;
|
|
49
|
+
/**
|
|
50
|
+
* The markdown image a comment body carries for an asset. The alt text is the
|
|
51
|
+
* filename, so its brackets are escaped: `shot [final].png` would otherwise
|
|
52
|
+
* close the alt early and break the reference.
|
|
53
|
+
*/
|
|
54
|
+
export declare function taskAssetMarkdownRef(asset: {
|
|
55
|
+
id: string;
|
|
56
|
+
filename: string;
|
|
57
|
+
}): string;
|
|
58
|
+
/** sha256 of a file, read as a stream: a 250MB recording never sits in memory. */
|
|
59
|
+
export declare function sha256OfFile(filePath: string): Promise<string>;
|
|
60
|
+
/**
|
|
61
|
+
* POST the file to the presigned URL as multipart/form-data, streamed from
|
|
62
|
+
* disk.
|
|
63
|
+
*
|
|
64
|
+
* Built by hand rather than with `FormData` + `fetch` for two reasons, both
|
|
65
|
+
* about large files. `FormData` needs a `Blob`, which here means the whole
|
|
66
|
+
* file in memory — the double buffering this replaced read up to 250MB and
|
|
67
|
+
* then copied it again. And storage refuses a POST without a Content-Length
|
|
68
|
+
* (it does not take chunked uploads), which a streamed `fetch` body cannot
|
|
69
|
+
* promise; the exact length is known here, because every part except the file
|
|
70
|
+
* is a small in-memory string and the file's size came from `stat`.
|
|
71
|
+
*
|
|
72
|
+
* `file` goes LAST — S3 presigned POST evaluates fields in order and ignores
|
|
73
|
+
* anything after it. The fields carry `x-amz-checksum-sha256`, so storage
|
|
74
|
+
* itself rejects bytes that do not match the digest the intent declared,
|
|
75
|
+
* including a file that changed between hashing and upload.
|
|
76
|
+
*/
|
|
77
|
+
export declare function postFileToStorage(p: {
|
|
78
|
+
uploadUrl: string;
|
|
79
|
+
uploadFields: Record<string, string>;
|
|
80
|
+
filePath: string;
|
|
81
|
+
size: number;
|
|
82
|
+
filename: string;
|
|
83
|
+
mimeType: string;
|
|
84
|
+
}): Promise<void>;
|