@skrr-ai/cli 0.1.56 → 0.1.58
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/base-command.d.ts +11 -0
- package/dist/base-command.js +80 -6
- package/dist/commands/agents/chat.js +5 -3
- package/dist/commands/agents/create.js +7 -0
- package/dist/commands/agents/show.js +13 -2
- package/dist/commands/agents/update.js +7 -0
- package/dist/commands/harnesses/ping.d.ts +2 -0
- package/dist/commands/harnesses/ping.js +10 -1
- package/dist/commands/login.js +20 -1
- package/dist/commands/logout.js +8 -0
- package/dist/commands/pair.js +1 -1
- package/dist/commands/tasks/move.js +11 -0
- package/dist/lib/agent-model-check.d.ts +19 -0
- package/dist/lib/agent-model-check.js +68 -0
- package/dist/lib/agentic-stream.d.ts +24 -0
- package/dist/lib/agentic-stream.js +117 -5
- package/dist/lib/auth-core-init.d.ts +0 -9
- package/dist/lib/auth-core-init.js +9 -0
- package/dist/lib/auth-storage.d.ts +42 -0
- package/dist/lib/auth-storage.js +75 -7
- package/dist/lib/commitments.js +4 -0
- package/dist/lib/config.d.ts +4 -0
- package/dist/lib/config.js +4 -2
- package/dist/lib/daemonBroker.d.ts +23 -1
- package/dist/lib/daemonBroker.js +66 -17
- package/dist/lib/daemonHandoff.d.ts +1 -1
- package/dist/lib/daemonHandoff.js +39 -1
- package/dist/lib/internal-id.d.ts +28 -0
- package/dist/lib/internal-id.js +41 -0
- package/dist/lib/login.js +2 -2
- package/dist/lib/oauthLogin.js +3 -1
- package/dist/lib/workflow-state-hint.d.ts +10 -0
- package/dist/lib/workflow-state-hint.js +27 -0
- package/dist/node_modules/@skrr-ai/data-provider/index.js +3292 -3281
- package/oclif.manifest.json +29800 -29800
- package/package.json +1 -1
package/dist/base-command.d.ts
CHANGED
|
@@ -109,6 +109,17 @@ export declare abstract class BaseCommand extends Command {
|
|
|
109
109
|
* silently matched nothing reaches the search.
|
|
110
110
|
*/
|
|
111
111
|
protected resolveSpaceReference(value: string): Promise<string>;
|
|
112
|
+
/**
|
|
113
|
+
* A 24-hex value: almost always the internal `_id` copied from `--json`,
|
|
114
|
+
* which prints it first (OSK-12205). No space route accepts it, so passing it
|
|
115
|
+
* through produced a not-found or a raw 404 that never said to use `id`.
|
|
116
|
+
*
|
|
117
|
+
* One listing tells the cases apart: a space whose `id` really is this value
|
|
118
|
+
* passes through unchanged; one whose `_id` is this value fails naming the
|
|
119
|
+
* exact `id` to use. A listing that fails, or finds neither, is not proof of
|
|
120
|
+
* anything, so it keeps the old pass-through and lets the route answer.
|
|
121
|
+
*/
|
|
122
|
+
private resolveObjectIdShapedSpace;
|
|
112
123
|
/**
|
|
113
124
|
* Turn a project reference into `{ projectRef, spaceId }`, or fail the
|
|
114
125
|
* command.
|
package/dist/base-command.js
CHANGED
|
@@ -20,6 +20,7 @@ const core_1 = require("@oclif/core");
|
|
|
20
20
|
Object.defineProperty(exports, "Flags", { enumerable: true, get: function () { return core_1.Flags; } });
|
|
21
21
|
const option_hint_1 = require("./lib/option-hint");
|
|
22
22
|
const failed_lookup_1 = require("./lib/failed-lookup");
|
|
23
|
+
const internal_id_1 = require("./lib/internal-id");
|
|
23
24
|
const http_error_1 = require("./lib/http-error");
|
|
24
25
|
// Re-exported from its home in `lib/keychain`, beside the other profile
|
|
25
26
|
// functions. It lived here while `init()` was its only caller; a lib module
|
|
@@ -260,6 +261,27 @@ class BaseCommand extends core_1.Command {
|
|
|
260
261
|
// can't wedge the CLI; on timeout we fall through to legacy
|
|
261
262
|
// plaintext and the resolver still finds the token.
|
|
262
263
|
await (0, auth_core_init_1.prefetchCliAuth)();
|
|
264
|
+
// On a brokered Dedicated guest a stored cli-auth.json predates broker
|
|
265
|
+
// mode, and the broker's success path retires it (revoke + delete). That
|
|
266
|
+
// could never run when the file carried an access token: the resolver
|
|
267
|
+
// below adopted it first, and the auto-broker only asks when the resolver
|
|
268
|
+
// found nothing — so a live pre-broker family was used and kept forever
|
|
269
|
+
// (OSK-12194). Broker FIRST in exactly that case. The file is retired only
|
|
270
|
+
// after a redeem succeeds; if the broker cannot answer, the resolver still
|
|
271
|
+
// falls back to the file, so a working login is never lost to an outage.
|
|
272
|
+
// `maybeAutoBroker` applies the same skip rules as below (bare, env token,
|
|
273
|
+
// login/logout/pair), and a laptop never has the guest descriptor.
|
|
274
|
+
let earlyAutoBroker = null;
|
|
275
|
+
if (!this.bareMode && !parsed.token && (0, daemonBroker_1.shouldBrokerBeforeStoredCredential)()) {
|
|
276
|
+
earlyAutoBroker = await (0, daemonBroker_1.maybeAutoBroker)({
|
|
277
|
+
cliConfig: this.cliConfig,
|
|
278
|
+
bareMode: this.bareMode,
|
|
279
|
+
commandId: this.id,
|
|
280
|
+
});
|
|
281
|
+
if (!earlyAutoBroker.triggered)
|
|
282
|
+
earlyAutoBroker = null;
|
|
283
|
+
}
|
|
284
|
+
const brokeredFirst = earlyAutoBroker?.outcome?.ok === true;
|
|
263
285
|
// Resolve credential via the Phase H.1 precedence chain. The
|
|
264
286
|
// adapter then cherry-picks the token for the Authorization
|
|
265
287
|
// header; we hand it a pre-resolved override so it doesn't
|
|
@@ -296,12 +318,14 @@ class BaseCommand extends core_1.Command {
|
|
|
296
318
|
// repair that reason implies — "Not signed in. Run `skrr login`" was true
|
|
297
319
|
// and pointed at the wrong fix (OSK-12149). Rate limiting and a mismatched
|
|
298
320
|
// baseURL keep their own one-line hints below.
|
|
299
|
-
if (!this.resolvedCredential.token) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
321
|
+
if (!this.resolvedCredential.token || brokeredFirst) {
|
|
322
|
+
// The guest's early broker above already asked; its answer stands.
|
|
323
|
+
const autoResult = earlyAutoBroker ??
|
|
324
|
+
(await (0, daemonBroker_1.maybeAutoBroker)({
|
|
325
|
+
cliConfig: this.cliConfig,
|
|
326
|
+
bareMode: this.bareMode,
|
|
327
|
+
commandId: this.id,
|
|
328
|
+
}));
|
|
305
329
|
if (autoResult.triggered && autoResult.outcome) {
|
|
306
330
|
if (autoResult.outcome.ok) {
|
|
307
331
|
// Persist the cliId now so a sibling skrr process spawned
|
|
@@ -449,6 +473,8 @@ class BaseCommand extends core_1.Command {
|
|
|
449
473
|
// Only canonical ids short-circuit. Human-readable refs are accepted by
|
|
450
474
|
// some read endpoints, but space-scoped mutation endpoints require the
|
|
451
475
|
// UUID, so refs go through the same exact-match resolution as titles.
|
|
476
|
+
if ((0, internal_id_1.looksLikeInternalObjectId)(query))
|
|
477
|
+
return this.resolveObjectIdShapedSpace(query);
|
|
452
478
|
if (!query || (0, space_resolver_1.looksLikeCanonicalSpaceId)(query))
|
|
453
479
|
return query;
|
|
454
480
|
if ((0, failed_lookup_1.isFailedLookupValue)(query)) {
|
|
@@ -493,6 +519,43 @@ class BaseCommand extends core_1.Command {
|
|
|
493
519
|
},
|
|
494
520
|
});
|
|
495
521
|
}
|
|
522
|
+
/**
|
|
523
|
+
* A 24-hex value: almost always the internal `_id` copied from `--json`,
|
|
524
|
+
* which prints it first (OSK-12205). No space route accepts it, so passing it
|
|
525
|
+
* through produced a not-found or a raw 404 that never said to use `id`.
|
|
526
|
+
*
|
|
527
|
+
* One listing tells the cases apart: a space whose `id` really is this value
|
|
528
|
+
* passes through unchanged; one whose `_id` is this value fails naming the
|
|
529
|
+
* exact `id` to use. A listing that fails, or finds neither, is not proof of
|
|
530
|
+
* anything, so it keeps the old pass-through and lets the route answer.
|
|
531
|
+
*/
|
|
532
|
+
async resolveObjectIdShapedSpace(query) {
|
|
533
|
+
let rows = [];
|
|
534
|
+
try {
|
|
535
|
+
const response = (await data_provider_1.dataService.listSpaces({ limit: 200, includeArchived: true }));
|
|
536
|
+
rows = response?.data ?? [];
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
return query;
|
|
540
|
+
}
|
|
541
|
+
if (rows.some((row) => row.id === query))
|
|
542
|
+
return query;
|
|
543
|
+
const byInternalId = rows.find((row) => String(row._id ?? '') === query);
|
|
544
|
+
if (!byInternalId)
|
|
545
|
+
return query;
|
|
546
|
+
this.failWithCliError({
|
|
547
|
+
message: (0, internal_id_1.describeInternalIdMisuse)({
|
|
548
|
+
value: query,
|
|
549
|
+
noun: 'space',
|
|
550
|
+
canonicalId: typeof byInternalId.id === 'string' ? byInternalId.id : null,
|
|
551
|
+
bin: this.config.bin,
|
|
552
|
+
}),
|
|
553
|
+
code: 'SPACE_INTERNAL_ID',
|
|
554
|
+
exit: 2,
|
|
555
|
+
retryable: false,
|
|
556
|
+
details: { context: { query, id: byInternalId.id } },
|
|
557
|
+
});
|
|
558
|
+
}
|
|
496
559
|
async resolveProjectReference(value, spaceFlag, opts = {}) {
|
|
497
560
|
const spaceRequired = opts.spaceRequired !== false;
|
|
498
561
|
const { projectRef, spaceRef } = (0, projects_1.splitQualifiedProjectRef)(value, spaceFlag);
|
|
@@ -557,6 +620,17 @@ class BaseCommand extends core_1.Command {
|
|
|
557
620
|
const query = value.trim();
|
|
558
621
|
if (!query)
|
|
559
622
|
return query;
|
|
623
|
+
// A task's `id` is a UUID, so a 24-hex value is the internal `_id` that
|
|
624
|
+
// `--json` prints first. The title search it used to reach can only say
|
|
625
|
+
// "no task matching", which never names the field to use (OSK-12205).
|
|
626
|
+
if ((0, internal_id_1.looksLikeInternalObjectId)(query)) {
|
|
627
|
+
this.failWithCliError({
|
|
628
|
+
message: (0, internal_id_1.describeInternalIdMisuse)({ value: query, noun: 'task', bin: this.config.bin }),
|
|
629
|
+
code: 'TASK_INTERNAL_ID',
|
|
630
|
+
exit: 2,
|
|
631
|
+
retryable: false,
|
|
632
|
+
});
|
|
633
|
+
}
|
|
560
634
|
// Before the search, not after it: `null` from a failed `jq -r` must never
|
|
561
635
|
// reach the title tiers, and it should not cost a request to say so.
|
|
562
636
|
if ((0, failed_lookup_1.isFailedLookupValue)(query)) {
|
|
@@ -287,9 +287,11 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
287
287
|
description: 'Permission mode for THIS turn only, on a local-harness agent. It beats the agent’s and ' +
|
|
288
288
|
'the harness’s stored defaults for this turn and changes nothing else — pass it again ' +
|
|
289
289
|
'on each turn that needs it (`agents update --default-permission-mode` changes every ' +
|
|
290
|
-
'conversation of the agent). A mode the harness cannot honour runs as the
|
|
291
|
-
'and run.started says which
|
|
292
|
-
'
|
|
290
|
+
'conversation of the agent). A mode the harness cannot honour runs as the closest ' +
|
|
291
|
+
'STRICTER mode it can, and run.started says which; when the only mode it can run would ' +
|
|
292
|
+
'let more run without asking, the turn is refused before it starts (run.failed, ' +
|
|
293
|
+
'PERMISSION_MODE_UNSUPPORTED_BY_HARNESS). --approve-tools is separate: it answers the ' +
|
|
294
|
+
'approval prompts a mode asks for.',
|
|
293
295
|
options: ['default', 'acceptEdits', 'bypassPermissions', 'plan'],
|
|
294
296
|
}),
|
|
295
297
|
'local-skill': core_1.Flags.string({
|
|
@@ -9,6 +9,7 @@ const core_1 = require("@oclif/core");
|
|
|
9
9
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
10
10
|
const agent_visual_refs_1 = require("../../lib/agent-visual-refs");
|
|
11
11
|
const runtime_tool_support_1 = require("../../lib/runtime-tool-support");
|
|
12
|
+
const agent_model_check_1 = require("../../lib/agent-model-check");
|
|
12
13
|
const base_command_1 = require("../../base-command");
|
|
13
14
|
const execution_target_1 = require("../../lib/execution-target");
|
|
14
15
|
const prompt_1 = require("../../lib/prompt");
|
|
@@ -407,6 +408,12 @@ class AgentsCreate extends base_command_1.BaseCommand {
|
|
|
407
408
|
this.warn(unappliedPayloadKeysWarning(unapplied));
|
|
408
409
|
}
|
|
409
410
|
await (0, runtime_tool_support_1.warnPluginsNotLoaded)(payload.tools, (message) => this.warn(message));
|
|
411
|
+
await (0, agent_model_check_1.warnIfModelNotListed)({
|
|
412
|
+
model: payload.model,
|
|
413
|
+
agent: created,
|
|
414
|
+
bin: this.config.bin,
|
|
415
|
+
warn: (message) => this.warn(message),
|
|
416
|
+
});
|
|
410
417
|
if (flags.json) {
|
|
411
418
|
this.log(JSON.stringify((0, web_url_1.withAgentUrl)(this.cliConfig.baseURL, created), null, 2));
|
|
412
419
|
return;
|
|
@@ -54,8 +54,19 @@ function agentPermissionLines(agent, harness) {
|
|
|
54
54
|
return [`Permission: ${stored} — enforced by the ${harness} harness`];
|
|
55
55
|
}
|
|
56
56
|
const lines = [];
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const from = PERMISSION_SOURCE_LABELS[effective.source] ?? effective.source;
|
|
58
|
+
if (typeof effective.refusedMode === 'string' && effective.refusedMode) {
|
|
59
|
+
// OSK-12316: a mode the harness could only honour by WIDENING it is refused
|
|
60
|
+
// at dispatch (OSK-12154); it does not "run as" anything.
|
|
61
|
+
const required = typeof effective.requiredMode === 'string' && effective.requiredMode
|
|
62
|
+
? effective.requiredMode
|
|
63
|
+
: null;
|
|
64
|
+
lines.push(`Permission: ${effective.refusedMode} (from ${from}) — turns are REFUSED: the ${harness} harness cannot run it without allowing more`);
|
|
65
|
+
if (required) {
|
|
66
|
+
lines.push(`${pad}Set ${required} to run (skrr agents update <agent> --default-permission-mode ${required}), or pass --permission-mode for a turn.`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (typeof effective.mode === 'string' && effective.mode) {
|
|
59
70
|
lines.push(`Permission: ${effective.mode} (from ${from}) — enforced by the ${harness} harness`);
|
|
60
71
|
}
|
|
61
72
|
else {
|
|
@@ -10,6 +10,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
|
|
|
10
10
|
const agent_visual_refs_1 = require("../../lib/agent-visual-refs");
|
|
11
11
|
const create_1 = require("./create");
|
|
12
12
|
const runtime_tool_support_1 = require("../../lib/runtime-tool-support");
|
|
13
|
+
const agent_model_check_1 = require("../../lib/agent-model-check");
|
|
13
14
|
const base_command_1 = require("../../base-command");
|
|
14
15
|
const execution_target_1 = require("../../lib/execution-target");
|
|
15
16
|
const prompt_1 = require("../../lib/prompt");
|
|
@@ -221,6 +222,12 @@ class AgentsUpdate extends base_command_1.BaseCommand {
|
|
|
221
222
|
for (const warning of agentUpdateWarnings(payload, updated))
|
|
222
223
|
this.warn(warning);
|
|
223
224
|
await (0, runtime_tool_support_1.warnPluginsNotLoaded)(payload.tools, (message) => this.warn(message));
|
|
225
|
+
await (0, agent_model_check_1.warnIfModelNotListed)({
|
|
226
|
+
model: payload.model,
|
|
227
|
+
agent: updated,
|
|
228
|
+
bin: this.config.bin,
|
|
229
|
+
warn: (message) => this.warn(message),
|
|
230
|
+
});
|
|
224
231
|
if (flags.json) {
|
|
225
232
|
this.log(JSON.stringify((0, web_url_1.withAgentUrl)(this.cliConfig.baseURL, updated), null, 2));
|
|
226
233
|
return;
|
|
@@ -8,6 +8,8 @@ import { BaseCommand } from '../../base-command';
|
|
|
8
8
|
* sends, and testing for them alone is what made the success branch dead code.
|
|
9
9
|
*/
|
|
10
10
|
export declare function isPingSuccess(status: string | undefined): boolean;
|
|
11
|
+
/** The server declared a completed probe failure, not a still-pending poll. */
|
|
12
|
+
export declare function describePingFailure(status: string, error?: string | null): string;
|
|
11
13
|
export default class HarnessesPing extends BaseCommand {
|
|
12
14
|
static description: string;
|
|
13
15
|
static examples: string[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isPingSuccess = isPingSuccess;
|
|
4
|
+
exports.describePingFailure = describePingFailure;
|
|
4
5
|
const core_1 = require("@oclif/core");
|
|
5
6
|
const base_command_1 = require("../../base-command");
|
|
6
7
|
const harnesses_1 = require("../../lib/harnesses");
|
|
@@ -31,6 +32,14 @@ const POLL_INTERVAL_MS = 300;
|
|
|
31
32
|
function isPingSuccess(status) {
|
|
32
33
|
return status === 'completed' || status === 'ok' || status === 'success';
|
|
33
34
|
}
|
|
35
|
+
/** The server declared a completed probe failure, not a still-pending poll. */
|
|
36
|
+
function describePingFailure(status, error) {
|
|
37
|
+
const detail = typeof error === 'string' && error.trim() ? `: ${error.trim()}` : '.';
|
|
38
|
+
if (status === 'timeout') {
|
|
39
|
+
return `Ping timed out${detail} The daemon is registered but may be offline or not responding.`;
|
|
40
|
+
}
|
|
41
|
+
return `Ping ${status}${detail} The daemon is registered but may be offline or not responding.`;
|
|
42
|
+
}
|
|
34
43
|
class HarnessesPing extends base_command_1.BaseCommand {
|
|
35
44
|
static description = 'Ping a harness and wait for its daemon to answer (local only)';
|
|
36
45
|
static examples = [
|
|
@@ -90,7 +99,7 @@ class HarnessesPing extends base_command_1.BaseCommand {
|
|
|
90
99
|
await this.logReadiness(args.harness);
|
|
91
100
|
}
|
|
92
101
|
else {
|
|
93
|
-
this.
|
|
102
|
+
this.error(describePingFailure(result.status, result.error), { exit: 1 });
|
|
94
103
|
}
|
|
95
104
|
return;
|
|
96
105
|
}
|
package/dist/commands/login.js
CHANGED
|
@@ -234,6 +234,25 @@ class Login extends base_command_1.BaseCommand {
|
|
|
234
234
|
if (handoff.status === 'delivered') {
|
|
235
235
|
this.log(`The local daemon was signed in too — ${handoff.detail}.`);
|
|
236
236
|
}
|
|
237
|
+
else if (handoff.reason === 'platform-managed') {
|
|
238
|
+
// A Dedicated guest: its daemon is the platform's, so there is
|
|
239
|
+
// nothing to install and nothing to hand over (OSK-12296).
|
|
240
|
+
this.log("This machine's daemon is managed by the platform; there is nothing to set up here.");
|
|
241
|
+
}
|
|
242
|
+
else if (handoff.reason === 'foreign-root') {
|
|
243
|
+
// Not a failure and not a hint to re-login anything: the daemon is
|
|
244
|
+
// the default root's, and this is a second root (OSK-12189).
|
|
245
|
+
this.log(`The local daemon was left as it was — ${handoff.detail}.`);
|
|
246
|
+
}
|
|
247
|
+
else if (handoff.reason === 'mint-refused') {
|
|
248
|
+
// The server refused before the daemon was touched, so its own
|
|
249
|
+
// credential is exactly what it was. Saying "not signed in" here sent
|
|
250
|
+
// people to re-login a healthy daemon (OSK-12189).
|
|
251
|
+
this.log(`This login was not handed to the local daemon (${handoff.detail}). ` +
|
|
252
|
+
`The daemon keeps whatever sign-in it already had — check it with ` +
|
|
253
|
+
`\`${this.config.bin} daemon status\`, and run \`${this.config.bin} daemon login\` ` +
|
|
254
|
+
`only if that says it is signed out.`);
|
|
255
|
+
}
|
|
237
256
|
else if (handoff.status === 'failed') {
|
|
238
257
|
this.log(`The local daemon was not signed in (${handoff.detail}). ` +
|
|
239
258
|
`Run \`${this.config.bin} daemon login\` to finish.`);
|
|
@@ -377,7 +396,7 @@ class Login extends base_command_1.BaseCommand {
|
|
|
377
396
|
refreshExpiresAt: result.refreshExpiresAt
|
|
378
397
|
? new Date(result.refreshExpiresAt).getTime()
|
|
379
398
|
: undefined,
|
|
380
|
-
}, { serverOrigin: this.cliConfig.baseURL, clearReauth: true });
|
|
399
|
+
}, { serverOrigin: this.cliConfig.baseURL, clearReauth: true, provenance: 'user_login' });
|
|
381
400
|
this.log('');
|
|
382
401
|
this.log(`Signed in as ${result.user.email} via recovery code on ` +
|
|
383
402
|
`${(0, config_1.describeDeployment)(this.cliConfig.baseURL)}.`);
|
package/dist/commands/logout.js
CHANGED
|
@@ -257,6 +257,14 @@ class Logout extends base_command_1.BaseCommand {
|
|
|
257
257
|
: 'Signed out from all devices.'
|
|
258
258
|
: 'Signed out.';
|
|
259
259
|
this.log(finalMessage);
|
|
260
|
+
// On a brokered Dedicated guest the sign-in just removed was the person's
|
|
261
|
+
// OWN (an explicit `skrr login`); the machine's platform-managed access is
|
|
262
|
+
// untouched and skrr keeps working through it. Say both, so "Signed out."
|
|
263
|
+
// is not read as "this machine can no longer reach skrr" (OSK-12043).
|
|
264
|
+
if ((0, daemonBroker_1.findBrokeredHandoffDescriptor)()) {
|
|
265
|
+
this.log('Removed your own sign-in from this machine. ' +
|
|
266
|
+
"skrr keeps working here through the platform's managed hand-off.");
|
|
267
|
+
}
|
|
260
268
|
if (sweptLegacy.length > 0) {
|
|
261
269
|
this.log(`Also removed ${sweptLegacy.length} credential${sweptLegacy.length === 1 ? '' : 's'} left by the previous product identity:`);
|
|
262
270
|
for (const service of sweptLegacy)
|
package/dist/commands/pair.js
CHANGED
|
@@ -273,7 +273,7 @@ function persistPairCredentials(plaintext, serverOrigin) {
|
|
|
273
273
|
(0, auth_storage_1.writeToBackend)({
|
|
274
274
|
token: plaintext.token,
|
|
275
275
|
...(plaintext.refreshToken ? { refreshToken: plaintext.refreshToken } : {}),
|
|
276
|
-
}, { serverOrigin, clearReauth: true });
|
|
276
|
+
}, { serverOrigin, clearReauth: true, provenance: 'user_login' });
|
|
277
277
|
if (plaintext.dataEncryptionKey) {
|
|
278
278
|
// The unified `writeToBackend` API only carries token+refresh
|
|
279
279
|
// today. For now, persist the DEK as a sibling field on the next
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const api_fetch_1 = require("../../lib/api-fetch");
|
|
6
|
+
const http_error_1 = require("../../lib/http-error");
|
|
7
|
+
const workflow_state_hint_1 = require("../../lib/workflow-state-hint");
|
|
6
8
|
const session_task_endpoints_1 = require("../../lib/session-task-endpoints");
|
|
7
9
|
const base_command_1 = require("../../base-command");
|
|
8
10
|
const task_closure_1 = require("../../lib/task-closure");
|
|
@@ -129,6 +131,15 @@ class TasksMove extends base_command_1.BaseCommand {
|
|
|
129
131
|
}
|
|
130
132
|
}
|
|
131
133
|
catch (err) {
|
|
134
|
+
const unknownState = (0, workflow_state_hint_1.describeUnknownWorkflowState)((0, http_error_1.httpBodyOf)(err), this.config.bin);
|
|
135
|
+
if (unknownState) {
|
|
136
|
+
this.failWithCliError({
|
|
137
|
+
message: unknownState,
|
|
138
|
+
code: 'WORKFLOW_STATE_UNKNOWN',
|
|
139
|
+
exit: 2,
|
|
140
|
+
retryable: false,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
132
143
|
this.handleApiError(err);
|
|
133
144
|
}
|
|
134
145
|
if (flags.json) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface ModelsReport {
|
|
2
|
+
provider?: string | null;
|
|
3
|
+
source?: string;
|
|
4
|
+
sourceReason?: string;
|
|
5
|
+
models?: Array<{
|
|
6
|
+
value?: string;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
/** The harness the saved agent runs on first, as the write response names it. */
|
|
10
|
+
export declare function primaryHarnessId(agent: unknown): string | null;
|
|
11
|
+
export declare function describeUnlistedModel(model: string | undefined, report: ModelsReport | undefined, harnessId: string, bin: string): string | null;
|
|
12
|
+
/** Best effort: a check that cannot run says nothing rather than failing the save. */
|
|
13
|
+
export declare function warnIfModelNotListed(opts: {
|
|
14
|
+
model: string | undefined;
|
|
15
|
+
agent: unknown;
|
|
16
|
+
bin: string;
|
|
17
|
+
warn: (message: string) => void;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.primaryHarnessId = primaryHarnessId;
|
|
4
|
+
exports.describeUnlistedModel = describeUnlistedModel;
|
|
5
|
+
exports.warnIfModelNotListed = warnIfModelNotListed;
|
|
6
|
+
const harnesses_1 = require("./harnesses");
|
|
7
|
+
/**
|
|
8
|
+
* A model id the agent's harness does not list, said at save time (OSK-12205).
|
|
9
|
+
*
|
|
10
|
+
* `agents create/update --model <id>` stored any well-formed id without a
|
|
11
|
+
* word; the first anyone heard of a typo was a failed turn, whose remedy then
|
|
12
|
+
* sent a local agent to "publish the agent". The server only validates the
|
|
13
|
+
* id's SHAPE on write — asking the daemon for its live list on every PATCH
|
|
14
|
+
* would put a machine round-trip on the write path — so the check lives here,
|
|
15
|
+
* after the write, and never fails the command.
|
|
16
|
+
*
|
|
17
|
+
* Only a list that speaks for the harness can say "not there": `live` (the
|
|
18
|
+
* installed harness enumerated it) or `managed` (the admission policy). A
|
|
19
|
+
* `curated` list is our suggestion, so a miss against it is reported as
|
|
20
|
+
* unverified, with the reason the live list was unavailable.
|
|
21
|
+
*/
|
|
22
|
+
/** Not model names: "no selection", which runs the harness default. */
|
|
23
|
+
const SENTINELS = new Set(['', 'default', 'auto']);
|
|
24
|
+
/** The harness the saved agent runs on first, as the write response names it. */
|
|
25
|
+
function primaryHarnessId(agent) {
|
|
26
|
+
const record = (agent ?? {});
|
|
27
|
+
if (typeof record.harnessId === 'string' && record.harnessId)
|
|
28
|
+
return record.harnessId;
|
|
29
|
+
const policy = record.executionPolicy;
|
|
30
|
+
const target = (policy?.targets ?? []).find((t) => t?.kind === 'harness' && t.enabled !== false && typeof t.harnessId === 'string');
|
|
31
|
+
return target?.harnessId ?? null;
|
|
32
|
+
}
|
|
33
|
+
function describeUnlistedModel(model, report, harnessId, bin) {
|
|
34
|
+
const id = typeof model === 'string' ? model.trim() : '';
|
|
35
|
+
if (SENTINELS.has(id.toLowerCase()))
|
|
36
|
+
return null;
|
|
37
|
+
const listed = (report?.models ?? []).some((entry) => entry?.value === id);
|
|
38
|
+
if (listed)
|
|
39
|
+
return null;
|
|
40
|
+
const provider = report?.provider || 'this';
|
|
41
|
+
const remedy = `\`${bin} harnesses models ${harnessId}\` lists what it can run.`;
|
|
42
|
+
if (report?.source === 'live' || report?.source === 'managed') {
|
|
43
|
+
return (`Model '${id}' is not in the ${provider} harness's ${report.source} model list ` +
|
|
44
|
+
`(${harnessId}). It was saved, but a turn fails when the harness refuses it. ${remedy}`);
|
|
45
|
+
}
|
|
46
|
+
const why = report?.sourceReason ? ` (${report.sourceReason})` : '';
|
|
47
|
+
return (`Model '${id}' is not in the suggested list for the ${provider} harness, and the ` +
|
|
48
|
+
`harness's live list was unavailable${why}, so it could not be checked. It was saved. ` +
|
|
49
|
+
remedy);
|
|
50
|
+
}
|
|
51
|
+
/** Best effort: a check that cannot run says nothing rather than failing the save. */
|
|
52
|
+
async function warnIfModelNotListed(opts) {
|
|
53
|
+
const id = typeof opts.model === 'string' ? opts.model.trim() : '';
|
|
54
|
+
if (SENTINELS.has(id.toLowerCase()))
|
|
55
|
+
return;
|
|
56
|
+
const harnessId = primaryHarnessId(opts.agent);
|
|
57
|
+
if (!harnessId)
|
|
58
|
+
return;
|
|
59
|
+
try {
|
|
60
|
+
const report = (await (0, harnesses_1.getHarnessModels)(harnessId));
|
|
61
|
+
const message = describeUnlistedModel(id, report, harnessId, opts.bin);
|
|
62
|
+
if (message)
|
|
63
|
+
opts.warn(message);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
/* the save stands; the harness list is a hint */
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -365,6 +365,18 @@ export declare class AgenticStreamClient {
|
|
|
365
365
|
private readonly toolNamesByCall;
|
|
366
366
|
/** `permission-ask` requestId → tool name, to name its resolution (OSK-12113). */
|
|
367
367
|
private readonly toolNamesByPermission;
|
|
368
|
+
/**
|
|
369
|
+
* Permission prompts on the terminal still waiting for an answer, by
|
|
370
|
+
* toolUseId, so a server `permission:cancel` can close the one it names
|
|
371
|
+
* (OSK-12197) instead of leaving `[y/N]` open after the run stopped asking.
|
|
372
|
+
*/
|
|
373
|
+
private readonly openPermissionPrompts;
|
|
374
|
+
/**
|
|
375
|
+
* Question prompts on the terminal still waiting for an answer, by
|
|
376
|
+
* questionId, so a server `question:cancel` can close the one it names
|
|
377
|
+
* (OSK-12208) the same way `permission:cancel` closes a permission prompt.
|
|
378
|
+
*/
|
|
379
|
+
private readonly openQuestionPrompts;
|
|
368
380
|
/** Text already written to the terminal, for suffix-only rendering. */
|
|
369
381
|
private renderedText;
|
|
370
382
|
private idleTimer;
|
|
@@ -443,6 +455,18 @@ export declare class AgenticStreamClient {
|
|
|
443
455
|
private installHandlers;
|
|
444
456
|
private handlePermission;
|
|
445
457
|
private handleQuestion;
|
|
458
|
+
/**
|
|
459
|
+
* The server closed a permission request (OSK-12197): it expired unanswered,
|
|
460
|
+
* the pre-execution step gave up, or another surface answered it. Say so,
|
|
461
|
+
* and close the terminal prompt if this client is still showing it.
|
|
462
|
+
*/
|
|
463
|
+
private handlePermissionCancel;
|
|
464
|
+
/**
|
|
465
|
+
* The server closed a question (OSK-12208): nobody answered it in time, or
|
|
466
|
+
* the turn ended first. Say so, and close the terminal prompt if this
|
|
467
|
+
* client is still showing it — the question twin of handlePermissionCancel.
|
|
468
|
+
*/
|
|
469
|
+
private handleQuestionCancel;
|
|
446
470
|
private ask;
|
|
447
471
|
/**
|
|
448
472
|
* True the first time this (kind, identity) pair is seen; false afterwards.
|