@skrr-ai/cli 0.1.51 → 0.1.52
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 +7 -0
- package/dist/base-command.js +38 -8
- package/dist/commands/agents/create.js +8 -4
- package/dist/commands/agents/update.d.ts +23 -0
- package/dist/commands/agents/update.js +54 -9
- package/dist/commands/commitments/next-tick.js +9 -1
- package/dist/commands/context.js +2 -1
- package/dist/commands/daemon/config.js +16 -1
- package/dist/commands/doctor.js +8 -1
- package/dist/commands/login.d.ts +2 -1
- package/dist/commands/login.js +26 -7
- package/dist/commands/logout.js +3 -0
- package/dist/commands/machines/dedicated/show.js +25 -2
- package/dist/commands/tasks/create.d.ts +5 -0
- package/dist/commands/tasks/create.js +26 -6
- package/dist/commands/tasks/start.d.ts +14 -0
- package/dist/commands/tasks/start.js +31 -1
- package/dist/commands/tasks/update.js +8 -1
- package/dist/commands/whoami.js +5 -3
- package/dist/lib/agent-config.d.ts +2 -0
- package/dist/lib/agent-config.js +14 -10
- package/dist/lib/agentic-stream.d.ts +20 -0
- package/dist/lib/agentic-stream.js +39 -5
- package/dist/lib/auth-failure.js +4 -1
- package/dist/lib/auth-storage.d.ts +24 -0
- package/dist/lib/auth-storage.js +342 -11
- package/dist/lib/cli-id.d.ts +22 -0
- package/dist/lib/cli-id.js +56 -0
- package/dist/lib/cli-identity.d.ts +49 -0
- package/dist/lib/cli-identity.js +159 -0
- package/dist/lib/commitments.d.ts +4 -0
- package/dist/lib/commitments.js +12 -1
- package/dist/lib/config.d.ts +17 -0
- package/dist/lib/config.js +15 -3
- package/dist/lib/daemonBroker.d.ts +26 -4
- package/dist/lib/daemonBroker.js +82 -10
- package/dist/lib/daemonBrokerRefusal.d.ts +51 -0
- package/dist/lib/daemonBrokerRefusal.js +127 -0
- package/dist/lib/dedicated-machines.d.ts +33 -2
- package/dist/lib/dedicated-machines.js +50 -1
- package/dist/lib/delegated-cli.js +16 -0
- package/dist/lib/first-party-harness-broker.js +6 -4
- package/dist/lib/keychain.d.ts +39 -0
- package/dist/lib/keychain.js +228 -39
- package/dist/lib/label-ref.d.ts +34 -0
- package/dist/lib/label-ref.js +68 -1
- package/dist/lib/login.js +24 -12
- package/dist/lib/oauthLogin.js +2 -8
- package/dist/lib/refresh.js +6 -2
- package/dist/lib/tasks.js +33 -8
- package/dist/node_modules/@skrr-ai/data-provider/index.js +3138 -3100
- package/oclif.manifest.json +36364 -36355
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.taskTagLabelWarning = taskTagLabelWarning;
|
|
3
4
|
exports.droppedEstimateWarning = droppedEstimateWarning;
|
|
4
5
|
exports.taskCreateMayHaveCommitted = taskCreateMayHaveCommitted;
|
|
5
6
|
exports.taskMatchesCreateIdentity = taskMatchesCreateIdentity;
|
|
@@ -36,6 +37,16 @@ const web_url_1 = require("../../lib/web-url");
|
|
|
36
37
|
* Same shape as `warnDroppedDefaultAssignees` below, which does this for a
|
|
37
38
|
* silently-dropped assignee (OSK-4857).
|
|
38
39
|
*/
|
|
40
|
+
/**
|
|
41
|
+
* `tagLabelWarning` for a task: the task-label vocabulary of the ambient
|
|
42
|
+
* workspace. Shared by `tasks create` and `tasks update`.
|
|
43
|
+
*/
|
|
44
|
+
function taskTagLabelWarning(tags, attachedLabelIds) {
|
|
45
|
+
return (0, label_ref_1.tagLabelWarning)(tags, attachedLabelIds, {
|
|
46
|
+
applicableTo: 'task',
|
|
47
|
+
...(process.env.OVERSKY_WORKSPACE_ID ? { workspaceId: process.env.OVERSKY_WORKSPACE_ID } : {}),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
39
50
|
function droppedEstimateWarning(requested, written, spaceId) {
|
|
40
51
|
if (requested === undefined || requested === null)
|
|
41
52
|
return null;
|
|
@@ -137,7 +148,8 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
137
148
|
description: 'Stable caller-supplied idempotency key for create/upsert workflows',
|
|
138
149
|
}),
|
|
139
150
|
'default-agent': core_1.Flags.string({
|
|
140
|
-
description: 'Override the automatic assignee with an Agent
|
|
151
|
+
description: 'Override the automatic assignee with an Agent (it becomes the assignee that runs the task; ' +
|
|
152
|
+
'--assignee-agent names one directly). Use "auto" to inherit the space lead ' +
|
|
141
153
|
'agent. Without this, a task falls to the agent this session is bound to ' +
|
|
142
154
|
'(OVERSKY_SESSION_AGENT_ID), to you when --execution-mode is human, or to nobody. ' +
|
|
143
155
|
'Cannot be combined with --execution-mode human.',
|
|
@@ -161,8 +173,9 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
161
173
|
description: 'Commitment ID that owns this task. Use for work created directly from a commitment without a linked goal.',
|
|
162
174
|
}),
|
|
163
175
|
project: core_1.Flags.string({
|
|
164
|
-
description: '
|
|
165
|
-
|
|
176
|
+
description: 'Project to attach the task to: its `id` (uuid; a pasted `_id` is accepted and rewritten ' +
|
|
177
|
+
'to the `id`), or its ref (OSK-P-5) with --space. Without --space the task inherits ' +
|
|
178
|
+
"the project's space.",
|
|
166
179
|
}),
|
|
167
180
|
cycle: core_1.Flags.string({
|
|
168
181
|
description: 'Cycle ID, or "current" / "next" within the task Space',
|
|
@@ -174,7 +187,8 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
174
187
|
description: 'Source message ID (requires --source-conversation or daemon session context)',
|
|
175
188
|
}),
|
|
176
189
|
'assignee-agent': core_1.Flags.string({
|
|
177
|
-
description: 'Assignee agent
|
|
190
|
+
description: 'Assignee agent: the agent that DOES the work when the task starts (the first listed runs). ' +
|
|
191
|
+
'Id or name, resolved within --space (repeatable)',
|
|
178
192
|
multiple: true,
|
|
179
193
|
}),
|
|
180
194
|
'assignee-user': core_1.Flags.string({
|
|
@@ -182,7 +196,8 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
182
196
|
multiple: true,
|
|
183
197
|
}),
|
|
184
198
|
'dri-agent': core_1.Flags.string({
|
|
185
|
-
description: '
|
|
199
|
+
description: 'Accountable agent (DRI), separate from assignees. `tasks start` runs an assignee, ' +
|
|
200
|
+
'not the DRI — use --assignee-agent for the agent that does the work',
|
|
186
201
|
}),
|
|
187
202
|
'dri-user': core_1.Flags.string({
|
|
188
203
|
description: 'Explicit accountable human DRI (separate from assignees)',
|
|
@@ -219,7 +234,7 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
219
234
|
'ancestor it inherits from); with none set, the server drops the value. Set one with ' +
|
|
220
235
|
'`spaces update <space> --estimate-scale <scale> --estimate-migration <clear|migrate>`.',
|
|
221
236
|
}),
|
|
222
|
-
tag: core_1.Flags.string({ description:
|
|
237
|
+
tag: core_1.Flags.string({ description: label_ref_1.TAG_FLAG_HELP, multiple: true }),
|
|
223
238
|
'label-id': core_1.Flags.string({
|
|
224
239
|
aliases: ['label'],
|
|
225
240
|
description: 'Attach a task label (repeatable) — id, or a name from `skrr labels list --applicable-to task`. ' +
|
|
@@ -517,6 +532,11 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
517
532
|
const estimateWarning = droppedEstimateWarning(flags.estimate, created, payload.spaceId);
|
|
518
533
|
if (estimateWarning)
|
|
519
534
|
this.warn(estimateWarning);
|
|
535
|
+
// A `--tag` spelled like a declared label is almost always a label someone
|
|
536
|
+
// meant to attach (OSK-12053). Say so; never convert it implicitly.
|
|
537
|
+
const tagWarning = await taskTagLabelWarning(flags.tag, resolvedLabelIds);
|
|
538
|
+
if (tagWarning)
|
|
539
|
+
this.warn(tagWarning);
|
|
520
540
|
if (flags.json) {
|
|
521
541
|
// The raw task row alone cannot tell a script the assignee it implicitly
|
|
522
542
|
// asked for was dropped (`assigneeAgentIds: []`) from "nobody was ever
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command';
|
|
2
2
|
export default class TasksStart extends BaseCommand {
|
|
3
3
|
static description: string;
|
|
4
|
+
static aliases: string[];
|
|
4
5
|
static examples: string[];
|
|
5
6
|
static args: {
|
|
6
7
|
id: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
@@ -13,3 +14,16 @@ export default class TasksStart extends BaseCommand {
|
|
|
13
14
|
};
|
|
14
15
|
run(): Promise<void>;
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* What to run next, printed after a start (OSK-12128). A start used to print
|
|
19
|
+
* two ids and stop, and the way to watch it was found by accident. Every
|
|
20
|
+
* command named here exists; `tasks-start.follow-up.spec.ts` pins that.
|
|
21
|
+
*
|
|
22
|
+
* The last line announces the files a local run adds to its working directory:
|
|
23
|
+
* the daemon writes a per-run instruction file (AGENTS.md for Codex, CLAUDE.md
|
|
24
|
+
* for a worktree session, plus any server-sent context file) when nothing is
|
|
25
|
+
* at that name, and removes what it wrote afterwards unless it was changed
|
|
26
|
+
* (`daemon/src/session-context-file.ts`). Unannounced, those were the first two
|
|
27
|
+
* files in a directory the task said to leave alone.
|
|
28
|
+
*/
|
|
29
|
+
export declare function startFollowUpLines(bin: string, taskRef: string): string[];
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.startFollowUpLines = startFollowUpLines;
|
|
3
4
|
const core_1 = require("@oclif/core");
|
|
4
5
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
6
|
const base_command_1 = require("../../base-command");
|
|
6
7
|
const task_resolver_1 = require("../../lib/task-resolver");
|
|
7
8
|
const git_task_1 = require("../../lib/git-task");
|
|
8
9
|
class TasksStart extends base_command_1.BaseCommand {
|
|
9
|
-
static description = 'Start execution of a task
|
|
10
|
+
static description = 'Start execution of a task: its assignee agent runs it in a new conversation + session. ' +
|
|
11
|
+
'Alias: tasks execute';
|
|
12
|
+
// `tasks execute` is the verb a first-time user reaches for, and it was
|
|
13
|
+
// "not a command" (OSK-12128). One command, two names.
|
|
14
|
+
static aliases = ['tasks:execute'];
|
|
10
15
|
static examples = [
|
|
11
16
|
'<%= config.bin %> tasks start <task-id>',
|
|
12
17
|
'<%= config.bin %> tasks start',
|
|
@@ -109,6 +114,31 @@ class TasksStart extends base_command_1.BaseCommand {
|
|
|
109
114
|
this.log(`${branch.created ? 'Created' : 'Checked out'} branch: ${branch.branch}`);
|
|
110
115
|
this.log(`Conversation: ${result.conversationId}`);
|
|
111
116
|
this.log(`Session: ${result.sessionId}`);
|
|
117
|
+
for (const line of startFollowUpLines(this.config.bin, taskIdentity.id))
|
|
118
|
+
this.log(line);
|
|
112
119
|
}
|
|
113
120
|
}
|
|
114
121
|
exports.default = TasksStart;
|
|
122
|
+
/**
|
|
123
|
+
* What to run next, printed after a start (OSK-12128). A start used to print
|
|
124
|
+
* two ids and stop, and the way to watch it was found by accident. Every
|
|
125
|
+
* command named here exists; `tasks-start.follow-up.spec.ts` pins that.
|
|
126
|
+
*
|
|
127
|
+
* The last line announces the files a local run adds to its working directory:
|
|
128
|
+
* the daemon writes a per-run instruction file (AGENTS.md for Codex, CLAUDE.md
|
|
129
|
+
* for a worktree session, plus any server-sent context file) when nothing is
|
|
130
|
+
* at that name, and removes what it wrote afterwards unless it was changed
|
|
131
|
+
* (`daemon/src/session-context-file.ts`). Unannounced, those were the first two
|
|
132
|
+
* files in a directory the task said to leave alone.
|
|
133
|
+
*/
|
|
134
|
+
function startFollowUpLines(bin, taskRef) {
|
|
135
|
+
return [
|
|
136
|
+
'',
|
|
137
|
+
'Follow it:',
|
|
138
|
+
` ${bin} tasks watch ${taskRef} status until it finishes or fails`,
|
|
139
|
+
` ${bin} tasks transcript ${taskRef} what the agent has said so far`,
|
|
140
|
+
` ${bin} tasks approvals list ${taskRef} approval requests waiting on you`,
|
|
141
|
+
'On a local machine, the run may add its instruction file (AGENTS.md / CLAUDE.md) to the',
|
|
142
|
+
'working directory while it runs; files the run wrote are removed afterwards unless changed.',
|
|
143
|
+
];
|
|
144
|
+
}
|
|
@@ -9,6 +9,7 @@ const tasks_1 = require("../../lib/tasks");
|
|
|
9
9
|
const task_resolver_1 = require("../../lib/task-resolver");
|
|
10
10
|
const tasks_2 = require("../../lib/tasks");
|
|
11
11
|
const create_1 = require("./create");
|
|
12
|
+
const label_ref_1 = require("../../lib/label-ref");
|
|
12
13
|
class TasksUpdate extends base_command_1.BaseCommand {
|
|
13
14
|
static description = 'Update an existing task (partial patch). Status and PROJECT changes go through ' +
|
|
14
15
|
'`tasks move` — see `--goal` / `--space` below.';
|
|
@@ -156,7 +157,10 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
156
157
|
description: 'Remove a task from the list this one blocks (repeatable)',
|
|
157
158
|
multiple: true,
|
|
158
159
|
}),
|
|
159
|
-
tag: core_1.Flags.string({
|
|
160
|
+
tag: core_1.Flags.string({
|
|
161
|
+
description: `Replace tags (repeatable, whole-list). ${label_ref_1.TAG_FLAG_HELP}`,
|
|
162
|
+
multiple: true,
|
|
163
|
+
}),
|
|
160
164
|
'label-id': core_1.Flags.string({
|
|
161
165
|
aliases: ['label'],
|
|
162
166
|
description: 'Replace the task labels with these ids (repeatable, whole-list). ' +
|
|
@@ -444,6 +448,9 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
444
448
|
const estimateWarning = (0, create_1.droppedEstimateWarning)(flags.estimate, updated, updated?.spaceId);
|
|
445
449
|
if (estimateWarning)
|
|
446
450
|
this.warn(estimateWarning);
|
|
451
|
+
const tagWarning = await (0, create_1.taskTagLabelWarning)(flags.tag, flags['label-id']);
|
|
452
|
+
if (tagWarning)
|
|
453
|
+
this.warn(tagWarning);
|
|
447
454
|
if (flags.json) {
|
|
448
455
|
this.log(JSON.stringify((0, web_url_1.withTaskUrl)(this.cliConfig.baseURL, updated), null, 2));
|
|
449
456
|
return;
|
package/dist/commands/whoami.js
CHANGED
|
@@ -10,6 +10,7 @@ const auth_storage_1 = require("../lib/auth-storage");
|
|
|
10
10
|
const keychain_1 = require("../lib/keychain");
|
|
11
11
|
const cred_envelope_1 = require("../lib/cred-envelope");
|
|
12
12
|
const config_1 = require("../lib/config");
|
|
13
|
+
const cli_id_1 = require("../lib/cli-id");
|
|
13
14
|
/**
|
|
14
15
|
* `skrr whoami`
|
|
15
16
|
*
|
|
@@ -101,7 +102,7 @@ class Whoami extends base_command_1.BaseCommand {
|
|
|
101
102
|
daemonId,
|
|
102
103
|
server: this.cliConfig.baseURL,
|
|
103
104
|
profile: (0, keychain_1.getActiveProfile)(),
|
|
104
|
-
cliId: this.cliConfig
|
|
105
|
+
cliId: (0, cli_id_1.resolveCliId)(this.cliConfig) ?? null,
|
|
105
106
|
token: {
|
|
106
107
|
source: this.credentialSourceLabel(),
|
|
107
108
|
kind: this.resolvedCredential.kind,
|
|
@@ -135,8 +136,9 @@ class Whoami extends base_command_1.BaseCommand {
|
|
|
135
136
|
// where the login message sends you to find out where you are (OSK-301).
|
|
136
137
|
this.log(`Server: ${(0, config_1.describeDeployment)(this.cliConfig.baseURL)}`);
|
|
137
138
|
this.log(`Profile: ${(0, keychain_1.getActiveProfile)()}`);
|
|
138
|
-
|
|
139
|
-
|
|
139
|
+
const cliId = (0, cli_id_1.resolveCliId)(this.cliConfig);
|
|
140
|
+
if (cliId) {
|
|
141
|
+
this.log(`CLI ID: ${cliId}`);
|
|
140
142
|
}
|
|
141
143
|
this.log(`Token source: ${this.credentialSourceLabel() ?? '(none)'}`);
|
|
142
144
|
this.log(`Token kind: ${this.resolvedCredential.kind}`);
|
|
@@ -2,6 +2,8 @@ export declare const COMMITMENT_RESPONSIBILITY_LEVELS: readonly ["reasoning_only
|
|
|
2
2
|
export declare const REASONING_TOOL_ESCAPES: readonly ["bash", "browser"];
|
|
3
3
|
/** Warning printed by the three retired Commitment permission flags (OSK-11975). */
|
|
4
4
|
export declare const COMMITMENT_PERMISSION_FLAG_RETIRED = "This setting no longer has any effect: Commitment runs use the agent's own permissions.";
|
|
5
|
+
/** The three retired flags themselves: accepted at parse, warned on, never sent. */
|
|
6
|
+
export declare const RETIRED_COMMITMENT_PERMISSION_FLAGS: readonly ["reasoning-tool-escape", "commitment-responsibility", "commitment-responsibility-ceiling"];
|
|
5
7
|
export declare const RELEASE_INSTALL_CHANNELS: readonly ["latest", "manual"];
|
|
6
8
|
/**
|
|
7
9
|
* `09:00-18:00` or `09:00-18:00@America/Los_Angeles`, or a JSON object.
|
package/dist/lib/agent-config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RELEASE_INSTALL_CHANNELS = exports.COMMITMENT_PERMISSION_FLAG_RETIRED = exports.REASONING_TOOL_ESCAPES = exports.COMMITMENT_RESPONSIBILITY_LEVELS = void 0;
|
|
3
|
+
exports.RELEASE_INSTALL_CHANNELS = exports.RETIRED_COMMITMENT_PERMISSION_FLAGS = exports.COMMITMENT_PERMISSION_FLAG_RETIRED = exports.REASONING_TOOL_ESCAPES = exports.COMMITMENT_RESPONSIBILITY_LEVELS = void 0;
|
|
4
4
|
exports.parseWorkingHoursFlag = parseWorkingHoursFlag;
|
|
5
5
|
exports.mergeAgentConfigFlags = mergeAgentConfigFlags;
|
|
6
6
|
const prompt_1 = require("./prompt");
|
|
@@ -9,6 +9,12 @@ exports.COMMITMENT_RESPONSIBILITY_LEVELS = ['reasoning_only', 'full_ownership'];
|
|
|
9
9
|
exports.REASONING_TOOL_ESCAPES = ['bash', 'browser'];
|
|
10
10
|
/** Warning printed by the three retired Commitment permission flags (OSK-11975). */
|
|
11
11
|
exports.COMMITMENT_PERMISSION_FLAG_RETIRED = "This setting no longer has any effect: Commitment runs use the agent's own permissions.";
|
|
12
|
+
/** The three retired flags themselves: accepted at parse, warned on, never sent. */
|
|
13
|
+
exports.RETIRED_COMMITMENT_PERMISSION_FLAGS = [
|
|
14
|
+
'reasoning-tool-escape',
|
|
15
|
+
'commitment-responsibility',
|
|
16
|
+
'commitment-responsibility-ceiling',
|
|
17
|
+
];
|
|
12
18
|
exports.RELEASE_INSTALL_CHANNELS = ['latest', 'manual'];
|
|
13
19
|
/**
|
|
14
20
|
* `09:00-18:00` or `09:00-18:00@America/Los_Angeles`, or a JSON object.
|
|
@@ -102,15 +108,13 @@ function mergeAgentConfigFlags(body, flags) {
|
|
|
102
108
|
if (flags['todo-working-hours'] !== undefined) {
|
|
103
109
|
parsed.todoWorkingHours = parseWorkingHoursFlag(String(flags['todo-working-hours']));
|
|
104
110
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
parsed.commitmentResponsibilityCeiling = flags['commitment-responsibility-ceiling'];
|
|
113
|
-
}
|
|
111
|
+
// `--reasoning-tool-escape`, `--commitment-responsibility` and
|
|
112
|
+
// `--commitment-responsibility-ceiling` are retired (OSK-11975) and are
|
|
113
|
+
// deliberately NOT forwarded. The server still accepts and echoes the stored
|
|
114
|
+
// fields, so sending them wrote a value nothing reads and handed it back as
|
|
115
|
+
// if it were live — and "a ceiling a commitment may not exceed" is a safety
|
|
116
|
+
// claim a person then believes (OSK-12068). The flags stay parseable so old
|
|
117
|
+
// scripts do not break; oclif prints COMMITMENT_PERMISSION_FLAG_RETIRED.
|
|
114
118
|
if (flags['tool-resources'] !== undefined) {
|
|
115
119
|
parsed.tool_resources = JSON.parse(String(flags['tool-resources']));
|
|
116
120
|
}
|
|
@@ -346,6 +346,11 @@ export declare class AgenticStreamClient {
|
|
|
346
346
|
* repeat count varies run to run and cannot be inferred by a consumer.
|
|
347
347
|
*/
|
|
348
348
|
private readonly seenLifecycle;
|
|
349
|
+
/**
|
|
350
|
+
* Whether this client's transport has dropped since it first connected. Until
|
|
351
|
+
* it has, every `session:join` it sends declares a FIRST join (OSK-12124).
|
|
352
|
+
*/
|
|
353
|
+
private transportDropped;
|
|
349
354
|
/** `call` → tool name, so a daemon `tool-call-end` can be named (OSK-7730). */
|
|
350
355
|
private readonly toolNamesByCall;
|
|
351
356
|
/** `permission-ask` requestId → tool name, to name its resolution (OSK-12113). */
|
|
@@ -373,6 +378,21 @@ export declare class AgenticStreamClient {
|
|
|
373
378
|
settledResult(): AgenticStreamResult | null;
|
|
374
379
|
constructor(options: AgenticStreamOptions);
|
|
375
380
|
run(): Promise<AgenticStreamResult>;
|
|
381
|
+
/**
|
|
382
|
+
* The `session:join` payload.
|
|
383
|
+
*
|
|
384
|
+
* This client learns its session id from the HTTP acceptance and only then
|
|
385
|
+
* connects, while a daemon turn publishes its `session:init` at once. The
|
|
386
|
+
* server drops buffered frames older than the socket's connect time — right
|
|
387
|
+
* for a RE-connect, which already rendered them, and wrong here, where they
|
|
388
|
+
* were never seen: `run.started` (the dispatched mode and its source) never
|
|
389
|
+
* reached a daemon turn's stream (OSK-12124). `firstJoin` says so, and the
|
|
390
|
+
* server then replays the pre-connect lifecycle frames. Both joins of the
|
|
391
|
+
* first connection carry it, because the server's replay drains the shared
|
|
392
|
+
* buffer and either join may be the one that reaches it first; the stream's
|
|
393
|
+
* own lifecycle dedupe absorbs the overlap. After a drop it is not sent.
|
|
394
|
+
*/
|
|
395
|
+
private sessionJoinPayload;
|
|
376
396
|
/**
|
|
377
397
|
* (Re)start the silence watchdog that asks the durable record what happened.
|
|
378
398
|
*
|
|
@@ -280,9 +280,14 @@ function describePermissionDecider(ev) {
|
|
|
280
280
|
? `the ${mode} permission mode (nobody was asked)`
|
|
281
281
|
: "the session's permission mode (nobody was asked)";
|
|
282
282
|
case 'policy':
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
283
|
+
if (ev.by === 'server')
|
|
284
|
+
return 'the unattended-run policy (nobody could be asked)';
|
|
285
|
+
// A client that denied on its own because it could not ask anyone — the
|
|
286
|
+
// skrr CLI without --approve-tools, say (OSK-12113).
|
|
287
|
+
if (typeof ev.surface === 'string' && SURFACE_LABELS[ev.surface]) {
|
|
288
|
+
return `${SURFACE_LABELS[ev.surface]}, which had nobody to ask (nobody was asked)`;
|
|
289
|
+
}
|
|
290
|
+
return 'a runtime policy (nobody was asked)';
|
|
286
291
|
case 'session-grant':
|
|
287
292
|
return 'an earlier "allow for this session"';
|
|
288
293
|
case 'person': {
|
|
@@ -520,6 +525,11 @@ class AgenticStreamClient {
|
|
|
520
525
|
* repeat count varies run to run and cannot be inferred by a consumer.
|
|
521
526
|
*/
|
|
522
527
|
seenLifecycle = new Set();
|
|
528
|
+
/**
|
|
529
|
+
* Whether this client's transport has dropped since it first connected. Until
|
|
530
|
+
* it has, every `session:join` it sends declares a FIRST join (OSK-12124).
|
|
531
|
+
*/
|
|
532
|
+
transportDropped = false;
|
|
523
533
|
/** `call` → tool name, so a daemon `tool-call-end` can be named (OSK-7730). */
|
|
524
534
|
toolNamesByCall = new Map();
|
|
525
535
|
/** `permission-ask` requestId → tool name, to name its resolution (OSK-12113). */
|
|
@@ -581,11 +591,30 @@ class AgenticStreamClient {
|
|
|
581
591
|
// the result instead of awaiting an attach nobody is still attempting.
|
|
582
592
|
if (this.settled || this.closed || !this.socket)
|
|
583
593
|
return result;
|
|
584
|
-
this.socket.emit('session:join',
|
|
594
|
+
this.socket.emit('session:join', this.sessionJoinPayload());
|
|
585
595
|
this.emit({ type: 'session.joined', sessionId: this.options.sessionId });
|
|
586
596
|
this.armIdleProbe();
|
|
587
597
|
return result;
|
|
588
598
|
}
|
|
599
|
+
/**
|
|
600
|
+
* The `session:join` payload.
|
|
601
|
+
*
|
|
602
|
+
* This client learns its session id from the HTTP acceptance and only then
|
|
603
|
+
* connects, while a daemon turn publishes its `session:init` at once. The
|
|
604
|
+
* server drops buffered frames older than the socket's connect time — right
|
|
605
|
+
* for a RE-connect, which already rendered them, and wrong here, where they
|
|
606
|
+
* were never seen: `run.started` (the dispatched mode and its source) never
|
|
607
|
+
* reached a daemon turn's stream (OSK-12124). `firstJoin` says so, and the
|
|
608
|
+
* server then replays the pre-connect lifecycle frames. Both joins of the
|
|
609
|
+
* first connection carry it, because the server's replay drains the shared
|
|
610
|
+
* buffer and either join may be the one that reaches it first; the stream's
|
|
611
|
+
* own lifecycle dedupe absorbs the overlap. After a drop it is not sent.
|
|
612
|
+
*/
|
|
613
|
+
sessionJoinPayload() {
|
|
614
|
+
return this.transportDropped
|
|
615
|
+
? { sessionId: this.options.sessionId }
|
|
616
|
+
: { sessionId: this.options.sessionId, firstJoin: true };
|
|
617
|
+
}
|
|
589
618
|
/**
|
|
590
619
|
* (Re)start the silence watchdog that asks the durable record what happened.
|
|
591
620
|
*
|
|
@@ -726,10 +755,11 @@ class AgenticStreamClient {
|
|
|
726
755
|
const socket = this.socket;
|
|
727
756
|
const match = (event) => !event.sessionId || event.sessionId === this.options.sessionId;
|
|
728
757
|
socket.on('connect', () => {
|
|
729
|
-
socket.emit('session:join',
|
|
758
|
+
socket.emit('session:join', this.sessionJoinPayload());
|
|
730
759
|
this.emit({ type: 'transport.connected', sessionId: this.options.sessionId });
|
|
731
760
|
});
|
|
732
761
|
socket.on('disconnect', (reason) => {
|
|
762
|
+
this.transportDropped = true;
|
|
733
763
|
if (this.settled)
|
|
734
764
|
return;
|
|
735
765
|
this.emit({ type: 'transport.disconnected', sessionId: this.options.sessionId, reason });
|
|
@@ -913,6 +943,10 @@ class AgenticStreamClient {
|
|
|
913
943
|
toolUseId: event.toolUseId,
|
|
914
944
|
toolName: event.toolName,
|
|
915
945
|
reason: 'CLI was not started with --approve-tools',
|
|
946
|
+
// Nobody was asked: this client could not put the prompt to anyone.
|
|
947
|
+
// The server records it as a policy decision, not a person's
|
|
948
|
+
// (OSK-12113) — it used to read "a person on the skrr CLI".
|
|
949
|
+
automatic: true,
|
|
916
950
|
});
|
|
917
951
|
}
|
|
918
952
|
this.emit({ ...event, type: 'permission.denied', reason: 'noninteractive_or_not_enabled' });
|
package/dist/lib/auth-failure.js
CHANGED
|
@@ -33,7 +33,10 @@ function describeRefreshRefusal(refusal, opts) {
|
|
|
33
33
|
// configuration on this machine (a different HOME or config dir sharing the
|
|
34
34
|
// same login Keychain) can write this profile's credential under its own ID.
|
|
35
35
|
// Observed: a token minted for `cli_devin_dogfood` sitting in the default
|
|
36
|
-
// profile's slot.
|
|
36
|
+
// profile's slot. Since OSK-12135 the slot is namespaced by config root and a
|
|
37
|
+
// read refuses a bundle bound to another cliId before presenting it, so this
|
|
38
|
+
// is now reached only when the server's binding disagrees with a token that
|
|
39
|
+
// carries no readable `did`.
|
|
37
40
|
if (/bound to a different daemon/i.test(message)) {
|
|
38
41
|
return {
|
|
39
42
|
code,
|
|
@@ -6,14 +6,33 @@ export interface AuthBundle {
|
|
|
6
6
|
refreshExpiresAt?: number;
|
|
7
7
|
/** Normalized API origin that issued this access/refresh pair. */
|
|
8
8
|
serverOrigin?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The config that wrote this bundle, as stored. Filled on write from the
|
|
11
|
+
* current config; callers never need to set it.
|
|
12
|
+
*/
|
|
13
|
+
owner?: CredentialOwner;
|
|
14
|
+
}
|
|
15
|
+
/** Which skrr config a credential belongs to. */
|
|
16
|
+
export interface CredentialOwner {
|
|
17
|
+
/** Resolved config root (`~/.skrr`, or the `SKRR_CONFIG_DIR` override). */
|
|
18
|
+
configRoot?: string;
|
|
19
|
+
/** The CLI identity the server bound the refresh family to. */
|
|
20
|
+
cliId?: string;
|
|
9
21
|
}
|
|
10
22
|
export type CredentialOriginStatus = 'none' | 'unchecked' | 'matched' | 'unbound' | 'mismatch';
|
|
23
|
+
/** `foreign`: the stored bundle belongs to another skrr config and was not used. */
|
|
24
|
+
export type CredentialOwnerStatus = 'foreign';
|
|
11
25
|
export interface ReadResult {
|
|
12
26
|
bundle: AuthBundle | null;
|
|
13
27
|
backend: AuthBackend;
|
|
14
28
|
originStatus?: CredentialOriginStatus;
|
|
15
29
|
storedServerOrigin?: string;
|
|
16
30
|
expectedServerOrigin?: string;
|
|
31
|
+
ownerStatus?: CredentialOwnerStatus;
|
|
32
|
+
/** For `foreign`: who the stored bundle belongs to. */
|
|
33
|
+
storedOwner?: CredentialOwner;
|
|
34
|
+
/** For `foreign`: who this process is. */
|
|
35
|
+
expectedOwner?: CredentialOwner;
|
|
17
36
|
}
|
|
18
37
|
export interface WriteOptions {
|
|
19
38
|
/** Bind/overwrite the bundle's issuing origin. Invalid origins are rejected. */
|
|
@@ -47,6 +66,11 @@ export declare function deleteFileBackend(): void;
|
|
|
47
66
|
* function returns whatever is persisted.
|
|
48
67
|
*/
|
|
49
68
|
export declare function readFromBackend(expectedServerOrigin?: string): ReadResult;
|
|
69
|
+
/**
|
|
70
|
+
* One sentence naming whose credential was refused and why, for the surfaces
|
|
71
|
+
* that explain an absent sign-in (BaseCommand, `skrr doctor`).
|
|
72
|
+
*/
|
|
73
|
+
export declare function describeForeignCredential(result: ReadResult): string | null;
|
|
50
74
|
export declare function writeToBackend(bundle: AuthBundle, options?: WriteOptions): AuthBackend;
|
|
51
75
|
/**
|
|
52
76
|
* Clear both backends. Called on `skrr logout` (and internally during
|