@skrr-ai/cli 0.1.51 → 0.1.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/base-command.d.ts +7 -0
  2. package/dist/base-command.js +38 -8
  3. package/dist/commands/agents/chat.js +2 -1
  4. package/dist/commands/agents/create.js +21 -8
  5. package/dist/commands/agents/update.d.ts +55 -0
  6. package/dist/commands/agents/update.js +119 -23
  7. package/dist/commands/commitments/next-tick.js +9 -1
  8. package/dist/commands/context.js +2 -1
  9. package/dist/commands/daemon/config.js +16 -1
  10. package/dist/commands/doctor.js +8 -1
  11. package/dist/commands/login.d.ts +2 -1
  12. package/dist/commands/login.js +26 -7
  13. package/dist/commands/logout.js +3 -0
  14. package/dist/commands/machines/dedicated/show.js +25 -2
  15. package/dist/commands/tasks/approvals/list.d.ts +22 -0
  16. package/dist/commands/tasks/approvals/list.js +80 -3
  17. package/dist/commands/tasks/create.d.ts +5 -0
  18. package/dist/commands/tasks/create.js +28 -7
  19. package/dist/commands/tasks/start.d.ts +14 -0
  20. package/dist/commands/tasks/start.js +31 -1
  21. package/dist/commands/tasks/update.js +30 -3
  22. package/dist/commands/whoami.js +5 -3
  23. package/dist/lib/agent-config.d.ts +15 -0
  24. package/dist/lib/agent-config.js +46 -10
  25. package/dist/lib/agentic-stream.d.ts +21 -1
  26. package/dist/lib/agentic-stream.js +40 -6
  27. package/dist/lib/auth-failure.js +4 -1
  28. package/dist/lib/auth-storage.d.ts +24 -0
  29. package/dist/lib/auth-storage.js +348 -13
  30. package/dist/lib/cli-id.d.ts +22 -0
  31. package/dist/lib/cli-id.js +56 -0
  32. package/dist/lib/cli-identity.d.ts +49 -0
  33. package/dist/lib/cli-identity.js +159 -0
  34. package/dist/lib/commitments.d.ts +4 -0
  35. package/dist/lib/commitments.js +12 -1
  36. package/dist/lib/config.d.ts +17 -0
  37. package/dist/lib/config.js +15 -3
  38. package/dist/lib/cred-envelope.d.ts +12 -1
  39. package/dist/lib/cred-envelope.js +20 -3
  40. package/dist/lib/daemonBroker.d.ts +26 -4
  41. package/dist/lib/daemonBroker.js +82 -10
  42. package/dist/lib/daemonBrokerRefusal.d.ts +51 -0
  43. package/dist/lib/daemonBrokerRefusal.js +127 -0
  44. package/dist/lib/dedicated-machines.d.ts +53 -4
  45. package/dist/lib/dedicated-machines.js +95 -5
  46. package/dist/lib/delegated-cli.js +16 -0
  47. package/dist/lib/first-party-harness-broker.js +6 -4
  48. package/dist/lib/keychain.d.ts +39 -0
  49. package/dist/lib/keychain.js +254 -40
  50. package/dist/lib/label-ref.d.ts +34 -0
  51. package/dist/lib/label-ref.js +72 -1
  52. package/dist/lib/login.js +24 -12
  53. package/dist/lib/oauthLogin.js +2 -8
  54. package/dist/lib/refresh.js +6 -2
  55. package/dist/lib/tasks.js +33 -8
  56. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.d.ts +13 -1
  57. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.js +9 -3
  58. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  59. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +4 -3
  60. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.d.ts +13 -1
  61. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.js +8 -3
  62. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  63. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
  64. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  65. package/dist/node_modules/@skrr-ai/data-provider/index.js +3138 -3100
  66. package/oclif.manifest.json +26470 -26449
  67. package/package.json +1 -1
@@ -1,6 +1,9 @@
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
+ exports.retiredCommitmentPermissionFlagMessage = retiredCommitmentPermissionFlagMessage;
5
+ exports.deprecatedFieldsWarnings = deprecatedFieldsWarnings;
6
+ exports.deprecatedFieldNames = deprecatedFieldNames;
4
7
  exports.parseWorkingHoursFlag = parseWorkingHoursFlag;
5
8
  exports.mergeAgentConfigFlags = mergeAgentConfigFlags;
6
9
  const prompt_1 = require("./prompt");
@@ -9,6 +12,41 @@ exports.COMMITMENT_RESPONSIBILITY_LEVELS = ['reasoning_only', 'full_ownership'];
9
12
  exports.REASONING_TOOL_ESCAPES = ['bash', 'browser'];
10
13
  /** Warning printed by the three retired Commitment permission flags (OSK-11975). */
11
14
  exports.COMMITMENT_PERMISSION_FLAG_RETIRED = "This setting no longer has any effect: Commitment runs use the agent's own permissions.";
15
+ /** The three retired flags themselves: accepted at parse, warned on, never sent. */
16
+ exports.RETIRED_COMMITMENT_PERMISSION_FLAGS = [
17
+ 'reasoning-tool-escape',
18
+ 'commitment-responsibility',
19
+ 'commitment-responsibility-ceiling',
20
+ ];
21
+ /**
22
+ * The deprecation warning ONE retired flag prints. oclif prints a
23
+ * `deprecated.message` verbatim, so a shared message printed the same line
24
+ * once per flag with nothing saying which flag each one was about (OSK-12068).
25
+ */
26
+ function retiredCommitmentPermissionFlagMessage(flag) {
27
+ return `--${flag} is retired and was not sent. ${exports.COMMITMENT_PERMISSION_FLAG_RETIRED}`;
28
+ }
29
+ /**
30
+ * The fields the server ignored and why, from a write's `deprecatedFields`
31
+ * notice, one line each. Empty when the response carries none.
32
+ */
33
+ function deprecatedFieldsWarnings(response) {
34
+ const entries = response?.deprecatedFields;
35
+ if (!Array.isArray(entries))
36
+ return [];
37
+ return entries
38
+ .filter((entry) => Boolean(entry) && typeof entry.field === 'string')
39
+ .map((entry) => `${entry.field}: ${entry.reason || 'ignored by the server'}`);
40
+ }
41
+ /** The payload keys the server reported as ignored, so they are not also called "not kept". */
42
+ function deprecatedFieldNames(response) {
43
+ const entries = response?.deprecatedFields;
44
+ return Array.isArray(entries)
45
+ ? entries
46
+ .map((entry) => entry?.field)
47
+ .filter((field) => typeof field === 'string')
48
+ : [];
49
+ }
12
50
  exports.RELEASE_INSTALL_CHANNELS = ['latest', 'manual'];
13
51
  /**
14
52
  * `09:00-18:00` or `09:00-18:00@America/Los_Angeles`, or a JSON object.
@@ -102,15 +140,13 @@ function mergeAgentConfigFlags(body, flags) {
102
140
  if (flags['todo-working-hours'] !== undefined) {
103
141
  parsed.todoWorkingHours = parseWorkingHoursFlag(String(flags['todo-working-hours']));
104
142
  }
105
- if (flags['reasoning-tool-escape'] !== undefined) {
106
- parsed.reasoningToolEscapes = flags['reasoning-tool-escape'];
107
- }
108
- if (flags['commitment-responsibility'] !== undefined) {
109
- parsed.commitmentResponsibilityDefault = flags['commitment-responsibility'];
110
- }
111
- if (flags['commitment-responsibility-ceiling'] !== undefined) {
112
- parsed.commitmentResponsibilityCeiling = flags['commitment-responsibility-ceiling'];
113
- }
143
+ // `--reasoning-tool-escape`, `--commitment-responsibility` and
144
+ // `--commitment-responsibility-ceiling` are retired (OSK-11975) and are
145
+ // deliberately NOT forwarded. The server still accepts and echoes the stored
146
+ // fields, so sending them wrote a value nothing reads and handed it back as
147
+ // if it were live — and "a ceiling a commitment may not exceed" is a safety
148
+ // claim a person then believes (OSK-12068). The flags stay parseable so old
149
+ // scripts do not break; oclif prints COMMITMENT_PERMISSION_FLAG_RETIRED.
114
150
  if (flags['tool-resources'] !== undefined) {
115
151
  parsed.tool_resources = JSON.parse(String(flags['tool-resources']));
116
152
  }
@@ -299,7 +299,7 @@ export type SeqGap = {
299
299
  * `service` envelopes that carry a `notice` — words for the person, not
300
300
  * diagnostics — projected as `session.notice` events. Today the only notice is
301
301
  * `harness-session-reset`: the local harness could not resume its previous
302
- * session and began a new one, so its local working context is gone.
302
+ * session and began a new one, which does not see earlier turns (OSK-12155).
303
303
  */
304
304
  export declare function projectSessionNotices(envelopes: unknown[]): AgenticStreamEvent[];
305
305
  export declare function detectSeqGap(highestApplied: number, incoming: number): SeqGap | null;
@@ -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
- return ev.by === 'server'
284
- ? 'the unattended-run policy (nobody could be asked)'
285
- : 'a runtime policy (nobody was asked)';
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': {
@@ -421,7 +426,7 @@ function jitteredRetryDelay(retryAfterMs, random = Math.random) {
421
426
  * `service` envelopes that carry a `notice` — words for the person, not
422
427
  * diagnostics — projected as `session.notice` events. Today the only notice is
423
428
  * `harness-session-reset`: the local harness could not resume its previous
424
- * session and began a new one, so its local working context is gone.
429
+ * session and began a new one, which does not see earlier turns (OSK-12155).
425
430
  */
426
431
  function projectSessionNotices(envelopes) {
427
432
  const out = [];
@@ -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', { sessionId: this.options.sessionId });
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', { sessionId: this.options.sessionId });
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' });
@@ -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