@skrr-ai/cli 0.1.121 → 0.1.123
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/actions/publish.js +4 -0
- package/dist/commands/agent/message/send.js +6 -1
- package/dist/commands/agents/chat.d.ts +7 -0
- package/dist/commands/agents/chat.js +32 -6
- package/dist/commands/followups/act.js +3 -0
- package/dist/commands/followups/checkin.js +3 -0
- package/dist/commands/followups/remind.js +3 -0
- package/dist/commands/followups/show.js +1 -1
- package/dist/commands/followups/watch.js +3 -0
- package/dist/commands/store/gallery-admissions/approve.d.ts +16 -0
- package/dist/commands/store/gallery-admissions/approve.js +36 -0
- package/dist/commands/store/gallery-admissions/list.d.ts +9 -0
- package/dist/commands/store/gallery-admissions/list.js +49 -0
- package/dist/commands/store/gallery-admissions/preview.d.ts +15 -0
- package/dist/commands/store/gallery-admissions/preview.js +48 -0
- package/dist/commands/store/gallery-admissions/refuse.d.ts +15 -0
- package/dist/commands/store/gallery-admissions/refuse.js +35 -0
- package/dist/commands/store/gallery-admissions/show.d.ts +12 -0
- package/dist/commands/store/gallery-admissions/show.js +55 -0
- package/dist/commands/tasks/list.js +8 -0
- package/dist/generated/agent-guides.json +11 -11
- package/dist/lib/agentic-stream.d.ts +46 -0
- package/dist/lib/agentic-stream.js +135 -14
- package/dist/lib/auth-failure.js +12 -0
- package/dist/lib/command-miss.js +1 -0
- package/dist/lib/exec-runtime-binary.d.ts +9 -0
- package/dist/lib/exec-runtime-binary.js +15 -1
- package/dist/lib/followups.d.ts +13 -0
- package/dist/lib/followups.js +8 -0
- package/dist/lib/profile-machine-identity-guard.js +2 -1
- package/dist/lib/runtime-start-verification.js +6 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.js +58 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +60 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +8 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/index.d.ts +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/index.js +7 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/linux.js +3 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/macos.js +3 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/masterKekService.d.ts +29 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/masterKekService.js +37 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.js +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refresh.js +12 -6
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/telemetryRedaction.d.ts +3 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/telemetryRedaction.js +41 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/types.d.ts +7 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.js +109 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +78 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/index.d.ts +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/index.js +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/linux.js +3 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/macos.js +3 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/masterKekService.d.ts +29 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/masterKekService.js +38 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.js +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refresh.js +31 -6
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/telemetryRedaction.d.ts +3 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/telemetryRedaction.js +40 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/types.d.ts +7 -1
- package/dist/node_modules/@skrr-ai/auth-core/package.json +10 -0
- package/dist/node_modules/@skrr-ai/data-provider/index.js +7359 -7344
- package/oclif.manifest.json +478 -3
- package/package.json +1 -1
|
@@ -691,7 +691,7 @@
|
|
|
691
691
|
"id": "delivery-statuses",
|
|
692
692
|
"title": "Delivery statuses",
|
|
693
693
|
"readWhen": "a send answered and you need to know what its status means",
|
|
694
|
-
"tokens":
|
|
694
|
+
"tokens": 726,
|
|
695
695
|
"aliases": [],
|
|
696
696
|
"explains": [
|
|
697
697
|
"agent_not_authorized_for_session",
|
|
@@ -709,7 +709,7 @@
|
|
|
709
709
|
]
|
|
710
710
|
}
|
|
711
711
|
],
|
|
712
|
-
"hash": "
|
|
712
|
+
"hash": "da48eab97c0c8b0102894a1256266cd67df3611313caec27ffff1ef2c2ec87ae",
|
|
713
713
|
"tokens": 544
|
|
714
714
|
},
|
|
715
715
|
"routedIndex": {
|
|
@@ -2027,7 +2027,7 @@
|
|
|
2027
2027
|
"routed": false,
|
|
2028
2028
|
"aliases": [],
|
|
2029
2029
|
"body": "## Concept model\n\nAn **Agent Update** is a deliberate owner-facing message. It is conversation, not\nrecord: what durably changed belongs to the work's own records, and the message\npoints at it.\n\n## Agent Updates\n\nProactively message the owner whenever it would improve their ability to steer the work, understand\na meaningful change, or stay confidently involved. Adapt to the task, uncertainty and stakes, the\nuser's engagement, what you have already communicated, and any per-agent style direction. Do not\nfollow a fixed schedule or manufacture status chatter.\n\n- Choose the semantic kind: `milestone` for a meaningful completed stage, `status` for a\n useful non-blocking checkpoint, `decision` for a concrete choice, `blocker` when user action is needed to\n continue, and one `result` for the terminal outcome (including failure).\n- Declare the attention intent with `--attention ambient|notify|interrupt`: `ambient` stays in\n the durable timeline by default, `notify` is worth seeing soon, and `interrupt` is reserved for\n work that is blocked or genuinely time-sensitive. The system and the user choose delivery channels and native\n presentation; attention never directly selects an Android channel, iOS interruption level, sound, or critical\n alert, and never bypasses notification preferences, quiet hours, Focus, or rate limits.\n- Say what is newly true and what it changes for the work. Include the next step, decision, or\n uncertainty when it would help the user; do not force every message into a fixed template.\n- Write like a capable teammate giving a busy owner a real, in-the-moment briefing — not like a\n system log, a customer-service script, or a task template. Open on the thing itself, never on a\n subject line (\"Status on the refactor:\", \"Quick update:\"). In the user's language, lead with the\n fresh fact, decision, or risk; then make clear why it matters and what happens next. Where the\n evidence is available, ground the update in one or two useful specifics: the affected thing, the\n evidence, the timing, or the trade-off.\n Use plain, varied sentences and only the confidence the evidence earns. Keep them short: where a\n dash would staple two clauses into one, use two sentences instead. Be warm and direct, but\n never manufacture familiarity, emotion, certainty, or colour that is not true. Avoid AI-shaped\n filler such as \"I am pleased to report\", \"completed / in progress / next step\" recaps,\n \"I will continue to monitor\", \"feel free to let me know\", ceremonial thanks, and vague claims\n such as \"the optimization is complete\". Say the thing instead: \"The migration is in; the\n slowest query fell from 2.8s to 420ms. I am running the full regression now and will flag any\n breakage before merge.\" Treat that as a standard of specificity, not a rigid script. Do not make\n a message longer merely to sound human.\n- Task context and card presentation are separate. A managed session's task context is derived\n server-side, but a message shows no Task Card unless you explicitly choose `--attach-task-card`\n (or `presentation.taskCard: true`). Use a card when it materially helps the user open or follow the\n live task; omit it for ordinary conversational updates.\n- In a hand-started session, use `skrr agent message context --agent <id>` when recent\n updates, read state, or a user's reply would help your judgment. It supplies observations, not a\n schedule or a command to send.\n- In a skrr-managed session, do not pass task, run, or session identity: skrr derives it.\n In a hand-started session, add `--task <task-id>` only when you know the correct task; it is\n declared context.\n- Prefer one message for one thought. For a conversational owner DM — such as a pitch, invitation,\n question, or progress update — when a draft exceeds roughly 320 characters or contains two independent thoughts,\n normally use a 2–4 message human-scale sequence with `--part \"<next thought>\"` and one explicit semantic\n `--idempotency-key`. Do not fragment a single thought merely to send more messages, and never split text\n mechanically: choose the semantic boundaries yourself. If one part does not reach the user, the remaining parts\n are deliberately not sent — the user would otherwise read your thoughts out of order.\n- An idempotency key names ONE update for this agent permanently, so make it specific to this run\n (for example `<task-id>:verify-complete`). Reuse it only to retry that same update: reused with\n the same text it is dropped as a duplicate, and with different text it is rejected.\n- A non-`delivered` status (`held`, `approval_required`, `duplicate`, `dropped`) is a policy\n outcome, not an error. It is never a reason to bypass this command with a direct DM or Slack\n call — this is the only sanctioned agent-to-user path.\n\n## Authority\n\n| Actor | May do | Only a person may do |\n| --- | --- | --- |\n| Agent | Decide to message; declare kind, attention intent, task context and a task card | Choose the delivery channel or native presentation; release a held or approval-gated intent |\n| Person | Everything an Agent may do | — |\n\n## Live state\n\n- `skrr agent message context --agent <id>` — recent updates, read state and owner engagement, as observations for the decision above.\n\n## Related guides\n\n- `tasks` — the records an update points at, and the durable version of what changed.\n- `agents` — which Agent a session speaks as; its `identity` section owns the agent-context refusals.",
|
|
2030
|
-
"hash": "
|
|
2030
|
+
"hash": "2ca0bfacfd5ac8edcfe5e36ca344d8698f02874e2786646c4f03bea28ca623d9",
|
|
2031
2031
|
"tokens": 1359,
|
|
2032
2032
|
"sections": [
|
|
2033
2033
|
{
|
|
@@ -2073,7 +2073,7 @@
|
|
|
2073
2073
|
"id": "delivery-statuses",
|
|
2074
2074
|
"title": "Delivery statuses",
|
|
2075
2075
|
"readWhen": "a send answered and you need to know what its status means",
|
|
2076
|
-
"tokens":
|
|
2076
|
+
"tokens": 726,
|
|
2077
2077
|
"aliases": [],
|
|
2078
2078
|
"explains": [
|
|
2079
2079
|
"agent_not_authorized_for_session",
|
|
@@ -8053,7 +8053,7 @@
|
|
|
8053
8053
|
"id": "delivery-statuses",
|
|
8054
8054
|
"title": "Delivery statuses",
|
|
8055
8055
|
"readWhen": "a send answered and you need to know what its status means",
|
|
8056
|
-
"tokens":
|
|
8056
|
+
"tokens": 726,
|
|
8057
8057
|
"aliases": [],
|
|
8058
8058
|
"explains": [
|
|
8059
8059
|
"agent_not_authorized_for_session",
|
|
@@ -8130,7 +8130,7 @@
|
|
|
8130
8130
|
"id": "delivery-statuses",
|
|
8131
8131
|
"title": "Delivery statuses",
|
|
8132
8132
|
"readWhen": "a send answered and you need to know what its status means",
|
|
8133
|
-
"tokens":
|
|
8133
|
+
"tokens": 726,
|
|
8134
8134
|
"aliases": [],
|
|
8135
8135
|
"explains": [
|
|
8136
8136
|
"agent_not_authorized_for_session",
|
|
@@ -8206,7 +8206,7 @@
|
|
|
8206
8206
|
"id": "delivery-statuses",
|
|
8207
8207
|
"title": "Delivery statuses",
|
|
8208
8208
|
"readWhen": "a send answered and you need to know what its status means",
|
|
8209
|
-
"tokens":
|
|
8209
|
+
"tokens": 726,
|
|
8210
8210
|
"aliases": [],
|
|
8211
8211
|
"explains": [
|
|
8212
8212
|
"agent_not_authorized_for_session",
|
|
@@ -8244,9 +8244,9 @@
|
|
|
8244
8244
|
"delivery_mode_ask_first",
|
|
8245
8245
|
"delivery_policy_unavailable"
|
|
8246
8246
|
],
|
|
8247
|
-
"body": "Read this when a send answered and you need to know what its status means.\n\n| Status | Meaning |\n| --- | --- |\n| `submitted` | recorded, not yet dispatched — still owned by the platform |\n| `held` | policy withheld it for now; it may still be delivered — `reason` says why |\n| `re_evaluation` | being re-checked against policy — still owned by the platform |\n| `approval_required` | waiting on the person’s approval; `initiativeId` names what they act on |\n| `authorized` | cleared for dispatch — still owned by the platform |\n| `dispatching` | being delivered right now |\n| `delivered` | reached the user |\n| `transport_failed` | delivery errored; `retryable` and `retryAfter` say whether to try again |\n| `dropped` | will not be delivered — read `reason` and change the request before resending |\n| `expired` | outlived its window before delivery |\n| `cancelled` | withdrawn before delivery |\n| `duplicate` | the idempotency key already carried this identical update — the earlier send stands |\n| `accepted` | an earlier send already delivered this update |\n| `failed` | the transport could not deliver; `retryable` says whether a retry can help |\n| `queued_offline` | held by the local daemon until it can reach the platform — still in flight |\n| `refused` | a guard declined the send; `reason` names it |\n| `unknown` | the transport could not certify the outcome |\n\nA `held`, `approval_required` or `dropped` result carries `reason`; an approval\ncarries `initiativeId` for the person to act on; a transport failure carries\n`retryable` and `retryAfter`. The exit code is `0` while the platform still owns\nthe outcome — `3` is retryable non-delivery, `4` terminal.\n\n## Refusals\n\n| Code | Cause | Fix |\n| --- | --- | --- |\n| `agent_not_authorized_for_session` | The send names an agent the session is not bound to | Drop `--agent`, or send as the agent the session runs |\n| `session_claim_required` | No session or claim authorizes the send | Run inside the managed session, or sign in and send as the person |\n| `context_task_not_authorized_for_session` | The declared `--task` is not this session's | Omit `--task`; a managed session derives it |\n| `task_context_forbidden` | The declared task is not accessible
|
|
8248
|
-
"hash": "
|
|
8249
|
-
"tokens":
|
|
8247
|
+
"body": "Read this when a send answered and you need to know what its status means.\n\n| Status | Meaning |\n| --- | --- |\n| `submitted` | recorded, not yet dispatched — still owned by the platform |\n| `held` | policy withheld it for now; it may still be delivered — `reason` says why |\n| `re_evaluation` | being re-checked against policy — still owned by the platform |\n| `approval_required` | waiting on the person’s approval; `initiativeId` names what they act on |\n| `authorized` | cleared for dispatch — still owned by the platform |\n| `dispatching` | being delivered right now |\n| `delivered` | reached the user |\n| `transport_failed` | delivery errored; `retryable` and `retryAfter` say whether to try again |\n| `dropped` | will not be delivered — read `reason` and change the request before resending |\n| `expired` | outlived its window before delivery |\n| `cancelled` | withdrawn before delivery |\n| `duplicate` | the idempotency key already carried this identical update — the earlier send stands |\n| `accepted` | an earlier send already delivered this update |\n| `failed` | the transport could not deliver; `retryable` says whether a retry can help |\n| `queued_offline` | held by the local daemon until it can reach the platform — still in flight |\n| `refused` | a guard declined the send; `reason` names it |\n| `unknown` | the transport could not certify the outcome |\n\nA `held`, `approval_required` or `dropped` result carries `reason`; an approval\ncarries `initiativeId` for the person to act on; a transport failure carries\n`retryable` and `retryAfter`. The exit code is `0` while the platform still owns\nthe outcome — `3` is retryable non-delivery, `4` terminal.\n\n## Refusals\n\n| Code | Cause | Fix |\n| --- | --- | --- |\n| `agent_not_authorized_for_session` | The send names an agent the session is not bound to | Drop `--agent`, or send as the agent the session runs |\n| `session_claim_required` | No session or claim authorizes the send | Run inside the managed session, or sign in and send as the person |\n| `context_task_not_authorized_for_session` | The declared `--task` is not this session's | Omit `--task`; a managed session derives it |\n| `task_context_forbidden` | The declared task was not found or is not accessible in this workspace | Check the task and workspace, or omit `--task` |\n| `main_chat_disabled` | The owner's main chat is off | Tell the person; do not route around it |\n| `destination_not_allowed` | Workspace policy excludes this destination | Send to an allowed destination, or ask the person to change policy |\n| `delivery_mode_off` | The owner's delivery mode delivers nothing | Do not resend; say it in the conversation instead |\n| `delivery_mode_suggestions_only` | Policy keeps the update as a suggestion | Leave it — the person sees it in-product |\n| `delivery_mode_ask_first` | Policy wants the person's approval first | Wait for the approval; do not resend |\n| `delivery_policy_unavailable` | Policy could not be read, so the send failed closed | Retry once; if it repeats, tell the person |",
|
|
8248
|
+
"hash": "ccd29be3e464db42898f221deca9c8f831c1929d0370d05a0c333def57172a3d",
|
|
8249
|
+
"tokens": 726,
|
|
8250
8250
|
"sections": [
|
|
8251
8251
|
{
|
|
8252
8252
|
"id": "writing",
|
|
@@ -8291,7 +8291,7 @@
|
|
|
8291
8291
|
"id": "delivery-statuses",
|
|
8292
8292
|
"title": "Delivery statuses",
|
|
8293
8293
|
"readWhen": "a send answered and you need to know what its status means",
|
|
8294
|
-
"tokens":
|
|
8294
|
+
"tokens": 726,
|
|
8295
8295
|
"aliases": [],
|
|
8296
8296
|
"explains": [
|
|
8297
8297
|
"agent_not_authorized_for_session",
|
|
@@ -11,11 +11,31 @@ import { type PublicProjection, type AssistantTextJoiner } from '@skrr-ai/data-p
|
|
|
11
11
|
* — consent nobody gave, on a form nobody saw (OSK-10274).
|
|
12
12
|
*/
|
|
13
13
|
export declare const NON_INTERACTIVE_QUESTION_DISMISSAL: string;
|
|
14
|
+
/**
|
|
15
|
+
* A terminal that has printed the header and nothing else for several seconds looks hung. The
|
|
16
|
+
* first line comes after this long with no output at all, and then again at this interval
|
|
17
|
+
* (OSK-20427). Typical waits are 4 to 6 seconds before the first words, so a quick reply
|
|
18
|
+
* prints nothing extra.
|
|
19
|
+
*/
|
|
20
|
+
export declare const WAITING_NOTICE_AFTER_MS = 2500;
|
|
21
|
+
export declare const WAITING_NOTICE_EVERY_MS = 10000;
|
|
14
22
|
export type AgenticStreamEvent = {
|
|
15
23
|
type: string;
|
|
16
24
|
sessionId?: string;
|
|
17
25
|
[key: string]: unknown;
|
|
18
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* The sentence a failed run's terminal event gives for why it failed.
|
|
29
|
+
*
|
|
30
|
+
* The server's failure funnel (`handleSessionFailure`) sends `errors: [<sentence>]`
|
|
31
|
+
* and a short `reason`, never a `message`; other terminal events carry `message`,
|
|
32
|
+
* and a few an `error` string. Every reader used to look at `message` alone, so a
|
|
33
|
+
* run the platform refused ("no valid release package", "insufficient balance", a
|
|
34
|
+
* runtime that was not there) ended as `Error: Agent run failed` with the reason
|
|
35
|
+
* sitting unread in the same event (OSK-20410). One reader, in one order: the
|
|
36
|
+
* explicit `message`, then `error`, then the first entry of `errors`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function terminalFailureMessage(event: Record<string, unknown> | null | undefined): string | undefined;
|
|
19
39
|
export type AgenticStreamResult = {
|
|
20
40
|
sessionId: string;
|
|
21
41
|
responseMessageId?: string;
|
|
@@ -161,6 +181,12 @@ export type AgenticStreamOptions = {
|
|
|
161
181
|
/** Action-required notices when stdout is reserved for a final result. */
|
|
162
182
|
statusOutput?: NodeJS.WritableStream;
|
|
163
183
|
jsonl?: boolean;
|
|
184
|
+
/**
|
|
185
|
+
* With `jsonl`, a `text.delta` carries only the characters it adds (`delta`), not the whole
|
|
186
|
+
* answer so far, and not the envelopes `session.output` already carries. A rewrite that is not
|
|
187
|
+
* an append arrives as `replace`, the new full text. `run.result` still holds the whole answer.
|
|
188
|
+
*/
|
|
189
|
+
incrementalText?: boolean;
|
|
164
190
|
approveTools?: boolean;
|
|
165
191
|
input?: NodeJS.ReadableStream;
|
|
166
192
|
output?: NodeJS.WritableStream;
|
|
@@ -579,6 +605,12 @@ export declare class AgenticStreamClient {
|
|
|
579
605
|
private readonly openQuestionPrompts;
|
|
580
606
|
/** Text already written to the terminal, for suffix-only rendering. */
|
|
581
607
|
private renderedText;
|
|
608
|
+
/** The "still waiting" line for a terminal run with no output yet (OSK-20427). */
|
|
609
|
+
private waitingTimer;
|
|
610
|
+
private waitingStartedAt;
|
|
611
|
+
private outputSeen;
|
|
612
|
+
/** The cumulative text of the last `text.delta` written as JSON, for `delta`/`replace`. */
|
|
613
|
+
private lastJsonlText;
|
|
582
614
|
private idleTimer;
|
|
583
615
|
/** Releases a connect wait still in flight; `close()` calls it. */
|
|
584
616
|
private abandonConnectWait;
|
|
@@ -777,6 +809,20 @@ export declare class AgenticStreamClient {
|
|
|
777
809
|
* passed through, preserving prior behaviour.
|
|
778
810
|
*/
|
|
779
811
|
private emitToolEvent;
|
|
812
|
+
/**
|
|
813
|
+
* Say so when a terminal run has been quiet since it started. Human output only: a script
|
|
814
|
+
* reading --jsonl has the events, and --quiet asked for nothing but the answer.
|
|
815
|
+
*/
|
|
816
|
+
private trackWaiting;
|
|
817
|
+
private stopWaiting;
|
|
818
|
+
/**
|
|
819
|
+
* A `text.delta` is named for a delta and carries the whole answer so far (`text`), so a long
|
|
820
|
+
* answer written as JSON lines grows with the square of its length: 207 events for a 2.4 KB
|
|
821
|
+
* answer came to about 250 KB (OSK-20428). The contract stays, since scripts take the last one;
|
|
822
|
+
* `delta` is added to it, and `incrementalText` turns the cumulative text and the duplicated
|
|
823
|
+
* envelopes off for a consumer that wants only what is new.
|
|
824
|
+
*/
|
|
825
|
+
private jsonlView;
|
|
780
826
|
private emit;
|
|
781
827
|
/**
|
|
782
828
|
* Human-mode lines for the hand-over wait. Returns true when the event is
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AgenticStreamClient = exports.USER_CONNECTION_CAP_CODE = exports.CODEX_DECLINED_TOOL_OUTPUTS = exports.TYPE_AHEAD_DISCARD_MS = exports.HANDOVER_POLL_INTERVAL_MS = exports.DAEMON_HANDOVER_REASON = exports.WORKER_REPLACED_CODE = exports.NON_INTERACTIVE_QUESTION_DISMISSAL = void 0;
|
|
3
|
+
exports.AgenticStreamClient = exports.USER_CONNECTION_CAP_CODE = exports.CODEX_DECLINED_TOOL_OUTPUTS = exports.TYPE_AHEAD_DISCARD_MS = exports.HANDOVER_POLL_INTERVAL_MS = exports.DAEMON_HANDOVER_REASON = exports.WORKER_REPLACED_CODE = exports.WAITING_NOTICE_EVERY_MS = exports.WAITING_NOTICE_AFTER_MS = exports.NON_INTERACTIVE_QUESTION_DISMISSAL = void 0;
|
|
4
|
+
exports.terminalFailureMessage = terminalFailureMessage;
|
|
4
5
|
exports.isDaemonHandoverFailure = isDaemonHandoverFailure;
|
|
5
6
|
exports.formatWaitDuration = formatWaitDuration;
|
|
6
7
|
exports.writeJsonLine = writeJsonLine;
|
|
@@ -46,6 +47,47 @@ const dpop_auth_1 = require("./dpop-auth");
|
|
|
46
47
|
*/
|
|
47
48
|
exports.NON_INTERACTIVE_QUESTION_DISMISSAL = 'No answer: this turn was started non-interactively (no terminal), so nobody can reply to ' +
|
|
48
49
|
'questions. Proceed with your best judgement, or finish by stating exactly what you need.';
|
|
50
|
+
/**
|
|
51
|
+
* A terminal that has printed the header and nothing else for several seconds looks hung. The
|
|
52
|
+
* first line comes after this long with no output at all, and then again at this interval
|
|
53
|
+
* (OSK-20427). Typical waits are 4 to 6 seconds before the first words, so a quick reply
|
|
54
|
+
* prints nothing extra.
|
|
55
|
+
*/
|
|
56
|
+
exports.WAITING_NOTICE_AFTER_MS = 2500;
|
|
57
|
+
exports.WAITING_NOTICE_EVERY_MS = 10_000;
|
|
58
|
+
/** What counts as the run having said something, so the waiting line stops. */
|
|
59
|
+
const OUTPUT_EVENT_TYPES = new Set([
|
|
60
|
+
'text.delta',
|
|
61
|
+
'tool.started',
|
|
62
|
+
'tool.completed',
|
|
63
|
+
'session.notice',
|
|
64
|
+
'permission.requested',
|
|
65
|
+
'run.completed',
|
|
66
|
+
'run.failed',
|
|
67
|
+
'run.aborted',
|
|
68
|
+
'run.handed_over',
|
|
69
|
+
]);
|
|
70
|
+
/**
|
|
71
|
+
* The sentence a failed run's terminal event gives for why it failed.
|
|
72
|
+
*
|
|
73
|
+
* The server's failure funnel (`handleSessionFailure`) sends `errors: [<sentence>]`
|
|
74
|
+
* and a short `reason`, never a `message`; other terminal events carry `message`,
|
|
75
|
+
* and a few an `error` string. Every reader used to look at `message` alone, so a
|
|
76
|
+
* run the platform refused ("no valid release package", "insufficient balance", a
|
|
77
|
+
* runtime that was not there) ended as `Error: Agent run failed` with the reason
|
|
78
|
+
* sitting unread in the same event (OSK-20410). One reader, in one order: the
|
|
79
|
+
* explicit `message`, then `error`, then the first entry of `errors`.
|
|
80
|
+
*/
|
|
81
|
+
function terminalFailureMessage(event) {
|
|
82
|
+
if (!event)
|
|
83
|
+
return undefined;
|
|
84
|
+
const sentence = (value) => typeof value === 'string' && value.trim() ? value : undefined;
|
|
85
|
+
const entryText = (entry) => sentence(entry) ?? sentence(entry?.message);
|
|
86
|
+
const fromList = Array.isArray(event.errors)
|
|
87
|
+
? event.errors.map(entryText).find((entry) => entry !== undefined)
|
|
88
|
+
: undefined;
|
|
89
|
+
return sentence(event.message) ?? sentence(event.error) ?? fromList;
|
|
90
|
+
}
|
|
49
91
|
/**
|
|
50
92
|
* OSK-13517 / OSK-13543 — the one failure that is not an ending.
|
|
51
93
|
*
|
|
@@ -807,6 +849,12 @@ class AgenticStreamClient {
|
|
|
807
849
|
openQuestionPrompts = new Map();
|
|
808
850
|
/** Text already written to the terminal, for suffix-only rendering. */
|
|
809
851
|
renderedText = '';
|
|
852
|
+
/** The "still waiting" line for a terminal run with no output yet (OSK-20427). */
|
|
853
|
+
waitingTimer = null;
|
|
854
|
+
waitingStartedAt = 0;
|
|
855
|
+
outputSeen = false;
|
|
856
|
+
/** The cumulative text of the last `text.delta` written as JSON, for `delta`/`replace`. */
|
|
857
|
+
lastJsonlText = '';
|
|
810
858
|
idleTimer = null;
|
|
811
859
|
/** Releases a connect wait still in flight; `close()` calls it. */
|
|
812
860
|
abandonConnectWait = null;
|
|
@@ -1012,6 +1060,7 @@ class AgenticStreamClient {
|
|
|
1012
1060
|
if (this.replayTimer)
|
|
1013
1061
|
clearTimeout(this.replayTimer);
|
|
1014
1062
|
this.replayTimer = null;
|
|
1063
|
+
this.stopWaiting();
|
|
1015
1064
|
this.stopHandoverTimers();
|
|
1016
1065
|
this.pendingEnvelopes.clear();
|
|
1017
1066
|
this.browserPermissionRequests.clear();
|
|
@@ -1079,10 +1128,7 @@ class AgenticStreamClient {
|
|
|
1079
1128
|
const deadlineTimer = setTimeout(() => void this.expireHandover(), opts.maxWaitMs);
|
|
1080
1129
|
const pollTimer = setInterval(() => this.requestHandoverRead(), opts.pollIntervalMs ?? exports.HANDOVER_POLL_INTERVAL_MS);
|
|
1081
1130
|
this.handover = { event, startedAt, deadlineTimer, pollTimer, reading: null, readAgain: false };
|
|
1082
|
-
const
|
|
1083
|
-
? event.errors[0]
|
|
1084
|
-
: undefined;
|
|
1085
|
-
const message = typeof event.message === 'string' && event.message ? event.message : firstError;
|
|
1131
|
+
const message = terminalFailureMessage(event);
|
|
1086
1132
|
this.emit({
|
|
1087
1133
|
type: 'run.handed_over',
|
|
1088
1134
|
sessionId: this.options.sessionId,
|
|
@@ -1745,6 +1791,10 @@ class AgenticStreamClient {
|
|
|
1745
1791
|
return;
|
|
1746
1792
|
}
|
|
1747
1793
|
if (!this.options.approveTools || !this.socket) {
|
|
1794
|
+
// This CLI makes the decision and nobody is asked, so it says so FIRST. The bound path used
|
|
1795
|
+
// to return before any event, and the only lines a person saw were the server's "The
|
|
1796
|
+
// response was admitted. Tool execution is not confirmed.", which read like an approval.
|
|
1797
|
+
this.emit({ ...event, type: 'permission.denied', reason: 'noninteractive_or_not_enabled' });
|
|
1748
1798
|
if (event.toolUseId) {
|
|
1749
1799
|
if (await this.sendBoundPermissionDecision(event, false, { reason: 'CLI was not started with --approve-tools', automatic: true }))
|
|
1750
1800
|
return;
|
|
@@ -1759,7 +1809,6 @@ class AgenticStreamClient {
|
|
|
1759
1809
|
automatic: true,
|
|
1760
1810
|
});
|
|
1761
1811
|
}
|
|
1762
|
-
this.emit({ ...event, type: 'permission.denied', reason: 'noninteractive_or_not_enabled' });
|
|
1763
1812
|
return;
|
|
1764
1813
|
}
|
|
1765
1814
|
/**
|
|
@@ -1782,6 +1831,14 @@ class AgenticStreamClient {
|
|
|
1782
1831
|
*/
|
|
1783
1832
|
const interactive = this.options.input !== undefined || Boolean(process.stdin.isTTY);
|
|
1784
1833
|
if (!interactive) {
|
|
1834
|
+
// Announced before it is sent, as the denial is: the bound path used to return first, so
|
|
1835
|
+
// "allowed by --approve-tools" never reached a person who used the flag.
|
|
1836
|
+
this.emit({
|
|
1837
|
+
...event,
|
|
1838
|
+
type: 'permission.approved',
|
|
1839
|
+
auto: true,
|
|
1840
|
+
reason: 'approved by --approve-tools, which was given with no terminal to ask at',
|
|
1841
|
+
});
|
|
1785
1842
|
if (await this.sendBoundPermissionDecision(event, true))
|
|
1786
1843
|
return;
|
|
1787
1844
|
this.socket.emit('permission:approve', {
|
|
@@ -1789,12 +1846,6 @@ class AgenticStreamClient {
|
|
|
1789
1846
|
toolUseId: event.toolUseId,
|
|
1790
1847
|
toolName: event.toolName,
|
|
1791
1848
|
});
|
|
1792
|
-
this.emit({
|
|
1793
|
-
...event,
|
|
1794
|
-
type: 'permission.approved',
|
|
1795
|
-
auto: true,
|
|
1796
|
-
reason: 'approved by --approve-tools, which was given with no terminal to ask at',
|
|
1797
|
-
});
|
|
1798
1849
|
return;
|
|
1799
1850
|
}
|
|
1800
1851
|
const prompt = new AbortController();
|
|
@@ -2084,6 +2135,60 @@ class AgenticStreamClient {
|
|
|
2084
2135
|
return;
|
|
2085
2136
|
this.emit(event);
|
|
2086
2137
|
}
|
|
2138
|
+
/**
|
|
2139
|
+
* Say so when a terminal run has been quiet since it started. Human output only: a script
|
|
2140
|
+
* reading --jsonl has the events, and --quiet asked for nothing but the answer.
|
|
2141
|
+
*/
|
|
2142
|
+
trackWaiting(event, output) {
|
|
2143
|
+
if (OUTPUT_EVENT_TYPES.has(event.type)) {
|
|
2144
|
+
this.outputSeen = true;
|
|
2145
|
+
this.stopWaiting();
|
|
2146
|
+
return;
|
|
2147
|
+
}
|
|
2148
|
+
if (event.type !== 'run.started' || this.outputSeen || this.waitingTimer)
|
|
2149
|
+
return;
|
|
2150
|
+
this.waitingStartedAt = Date.now();
|
|
2151
|
+
const schedule = (first) => {
|
|
2152
|
+
this.waitingTimer = setTimeout(() => {
|
|
2153
|
+
this.waitingTimer = null;
|
|
2154
|
+
if (this.outputSeen || this.closed)
|
|
2155
|
+
return;
|
|
2156
|
+
const seconds = Math.floor((Date.now() - this.waitingStartedAt) / 1000);
|
|
2157
|
+
output.write(first
|
|
2158
|
+
? '[run] waiting for the agent to start answering…\n'
|
|
2159
|
+
: `[run] still waiting for the agent (${seconds} s)…\n`);
|
|
2160
|
+
schedule(false);
|
|
2161
|
+
}, first ? exports.WAITING_NOTICE_AFTER_MS : exports.WAITING_NOTICE_EVERY_MS);
|
|
2162
|
+
// A reminder must never be the reason the process stays alive.
|
|
2163
|
+
this.waitingTimer.unref?.();
|
|
2164
|
+
};
|
|
2165
|
+
schedule(true);
|
|
2166
|
+
}
|
|
2167
|
+
stopWaiting() {
|
|
2168
|
+
if (this.waitingTimer)
|
|
2169
|
+
clearTimeout(this.waitingTimer);
|
|
2170
|
+
this.waitingTimer = null;
|
|
2171
|
+
}
|
|
2172
|
+
/**
|
|
2173
|
+
* A `text.delta` is named for a delta and carries the whole answer so far (`text`), so a long
|
|
2174
|
+
* answer written as JSON lines grows with the square of its length: 207 events for a 2.4 KB
|
|
2175
|
+
* answer came to about 250 KB (OSK-20428). The contract stays, since scripts take the last one;
|
|
2176
|
+
* `delta` is added to it, and `incrementalText` turns the cumulative text and the duplicated
|
|
2177
|
+
* envelopes off for a consumer that wants only what is new.
|
|
2178
|
+
*/
|
|
2179
|
+
jsonlView(event) {
|
|
2180
|
+
if (event.type !== 'text.delta' || typeof event.text !== 'string')
|
|
2181
|
+
return event;
|
|
2182
|
+
const previous = this.lastJsonlText;
|
|
2183
|
+
const text = event.text;
|
|
2184
|
+
this.lastJsonlText = text;
|
|
2185
|
+
const appended = text.startsWith(previous) ? text.slice(previous.length) : undefined;
|
|
2186
|
+
if (!this.options.incrementalText) {
|
|
2187
|
+
return appended === undefined ? event : { ...event, delta: appended };
|
|
2188
|
+
}
|
|
2189
|
+
const { text: _text, envelopes: _envelopes, ...rest } = event;
|
|
2190
|
+
return appended === undefined ? { ...rest, replace: text } : { ...rest, delta: appended };
|
|
2191
|
+
}
|
|
2087
2192
|
emit(event) {
|
|
2088
2193
|
this.lastEvent = event;
|
|
2089
2194
|
this.options.onEvent?.(event);
|
|
@@ -2108,9 +2213,10 @@ class AgenticStreamClient {
|
|
|
2108
2213
|
return;
|
|
2109
2214
|
const output = this.options.output ?? process.stdout;
|
|
2110
2215
|
if (this.options.jsonl) {
|
|
2111
|
-
writeJsonLine(output, event);
|
|
2216
|
+
writeJsonLine(output, this.jsonlView(event));
|
|
2112
2217
|
return;
|
|
2113
2218
|
}
|
|
2219
|
+
this.trackWaiting(event, output);
|
|
2114
2220
|
if (event.type === 'run.started') {
|
|
2115
2221
|
const permission = describeRunPermission(event);
|
|
2116
2222
|
if (permission)
|
|
@@ -2192,6 +2298,12 @@ class AgenticStreamClient {
|
|
|
2192
2298
|
output.write(`\n[question] ${why}\n`);
|
|
2193
2299
|
this.renderedText = this.answerText();
|
|
2194
2300
|
}
|
|
2301
|
+
else if (event.type === 'permission.denied' && event.reason === 'noninteractive_or_not_enabled') {
|
|
2302
|
+
// Said out loud, because nobody was asked and the agent will not say it plainly: this run
|
|
2303
|
+
// had no terminal to ask at and was not given --approve-tools, so the call was refused.
|
|
2304
|
+
output.write(`\n[permission] ${describePermission(event)} denied: nobody could be asked, and this run was not started with --approve-tools\n`);
|
|
2305
|
+
this.renderedText = this.answerText();
|
|
2306
|
+
}
|
|
2195
2307
|
else if (event.type === 'permission.approved' && event.auto === true) {
|
|
2196
2308
|
// Said out loud, because nobody was asked: the flag approved it, and a
|
|
2197
2309
|
// reader of this log should see which tools that covered (OSK-10323).
|
|
@@ -2379,7 +2491,16 @@ class AgenticStreamClient {
|
|
|
2379
2491
|
output.write(append);
|
|
2380
2492
|
output.write('\n');
|
|
2381
2493
|
}
|
|
2382
|
-
|
|
2494
|
+
// A failed run states why under the one key a script reads (`message`), whichever
|
|
2495
|
+
// shape the terminal event used. `lastEvent` below stays the event as received.
|
|
2496
|
+
const failureMessage = status === 'failed' && !(typeof event.message === 'string' && event.message.trim())
|
|
2497
|
+
? terminalFailureMessage(event)
|
|
2498
|
+
: undefined;
|
|
2499
|
+
this.emit({
|
|
2500
|
+
...event,
|
|
2501
|
+
...(failureMessage ? { message: failureMessage } : {}),
|
|
2502
|
+
type: `run.${status}`,
|
|
2503
|
+
});
|
|
2383
2504
|
this.close();
|
|
2384
2505
|
this.terminalResult = {
|
|
2385
2506
|
sessionId: this.options.sessionId,
|
package/dist/lib/auth-failure.js
CHANGED
|
@@ -98,6 +98,18 @@ function describeRefreshRefusal(refusal, opts) {
|
|
|
98
98
|
`server treats that as possible theft and revokes the whole family. ${relogin}`,
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
|
+
// The server wanted a device proof and this machine had no key to sign one
|
|
102
|
+
// with. `skrr login` signs the CLI in on its own and never creates a device
|
|
103
|
+
// key, so naming it here sends the person round the same loop (OSK-20472): the
|
|
104
|
+
// daemon's interactive sign-in is what mints a key and re-pins the server to it.
|
|
105
|
+
if (code === 'DEVICE_KEY_UNAVAILABLE') {
|
|
106
|
+
return {
|
|
107
|
+
code,
|
|
108
|
+
message: `This sign-in could not be renewed${said}. This machine's device key is missing or ` +
|
|
109
|
+
`unreadable, so requests carry no device proof. \`${bin} login\` cannot create one; ` +
|
|
110
|
+
`sign the background service in again with \`${bin} daemon login\`.`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
101
113
|
return {
|
|
102
114
|
code,
|
|
103
115
|
message: `This sign-in could not be renewed${said}. ${relogin}`,
|
package/dist/lib/command-miss.js
CHANGED
|
@@ -105,6 +105,7 @@ function describeTaskCommandRedirect(config, parts, typedWords) {
|
|
|
105
105
|
if (lower[0] === 'tasks' &&
|
|
106
106
|
lower[1] === 'search' &&
|
|
107
107
|
hasTopic(config, 'tasks') &&
|
|
108
|
+
config.findCommand('tasks:search') === undefined &&
|
|
108
109
|
config.findCommand(exports.TASK_COMMAND_MISS_TARGETS.search) !== undefined) {
|
|
109
110
|
const query = typedWords.slice(2).join(' ').trim();
|
|
110
111
|
const target = `${config.bin} tasks list --q ${query ? JSON.stringify(query) : '<query>'}`;
|
|
@@ -65,6 +65,15 @@ export declare function normalizeRuntimeGlobalArgs(args: readonly string[]): str
|
|
|
65
65
|
* part of a copy sweep.
|
|
66
66
|
*/
|
|
67
67
|
export declare function notInstalledMessage(): string;
|
|
68
|
+
/**
|
|
69
|
+
* Marks the `skrrd` process that `skrr daemon …` launches, so the daemon can print its recovery
|
|
70
|
+
* commands in the spelling the person can run: `skrr daemon login`, not `skrrd login`. The
|
|
71
|
+
* daemon lives in `~/.skrr/bin`, which this wrapper finds by itself and nothing puts on PATH.
|
|
72
|
+
* Spelled the same in `daemon/src/auth/login-hint.ts`; `scripts/__tests__/invoked-via-parity.spec.js`
|
|
73
|
+
* holds the two together.
|
|
74
|
+
*/
|
|
75
|
+
export declare const INVOKED_VIA_ENV = "SKRR_INVOKED_VIA";
|
|
76
|
+
export declare const INVOKED_VIA_CLI = "skrr-cli";
|
|
68
77
|
/**
|
|
69
78
|
* Spawn `oversky <args>` with stdio inherited. Resolves with the child's
|
|
70
79
|
* exit code so the caller can `process.exit()` with the same code,
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.RUNTIME_BINARY_NAMES = void 0;
|
|
6
|
+
exports.INVOKED_VIA_CLI = exports.INVOKED_VIA_ENV = exports.RUNTIME_BINARY_NAMES = void 0;
|
|
7
7
|
exports.forwardedFlagsNote = forwardedFlagsNote;
|
|
8
8
|
exports.findRuntimeBinary = findRuntimeBinary;
|
|
9
9
|
exports.isCurrentRuntimeBinary = isCurrentRuntimeBinary;
|
|
@@ -206,6 +206,18 @@ function notInstalledMessage() {
|
|
|
206
206
|
'Once installed, re-run your command.',
|
|
207
207
|
].join('\n');
|
|
208
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Marks the `skrrd` process that `skrr daemon …` launches, so the daemon can print its recovery
|
|
211
|
+
* commands in the spelling the person can run: `skrr daemon login`, not `skrrd login`. The
|
|
212
|
+
* daemon lives in `~/.skrr/bin`, which this wrapper finds by itself and nothing puts on PATH.
|
|
213
|
+
* Spelled the same in `daemon/src/auth/login-hint.ts`; `scripts/__tests__/invoked-via-parity.spec.js`
|
|
214
|
+
* holds the two together.
|
|
215
|
+
*/
|
|
216
|
+
exports.INVOKED_VIA_ENV = 'SKRR_INVOKED_VIA';
|
|
217
|
+
exports.INVOKED_VIA_CLI = 'skrr-cli';
|
|
218
|
+
function runtimeEnv() {
|
|
219
|
+
return { ...process.env, [exports.INVOKED_VIA_ENV]: exports.INVOKED_VIA_CLI };
|
|
220
|
+
}
|
|
209
221
|
/**
|
|
210
222
|
* Spawn `oversky <args>` with stdio inherited. Resolves with the child's
|
|
211
223
|
* exit code so the caller can `process.exit()` with the same code,
|
|
@@ -230,6 +242,7 @@ function execRuntimeBinary(args, opts = {}) {
|
|
|
230
242
|
const isWinShim = process.platform === 'win32' && /\.(cmd|bat)$/i.test(bin);
|
|
231
243
|
const child = (0, node_child_process_1.spawn)(bin, normalizeRuntimeGlobalArgs(args), {
|
|
232
244
|
stdio: 'inherit',
|
|
245
|
+
env: runtimeEnv(),
|
|
233
246
|
...(isWinShim ? { shell: true } : {}),
|
|
234
247
|
});
|
|
235
248
|
child.on('error', reject);
|
|
@@ -261,6 +274,7 @@ function execRuntimeBinaryCapture(args, opts = {}) {
|
|
|
261
274
|
const isWinShim = process.platform === 'win32' && /\.(cmd|bat)$/i.test(bin);
|
|
262
275
|
const child = (0, node_child_process_1.spawn)(bin, normalizeRuntimeGlobalArgs(args), {
|
|
263
276
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
277
|
+
env: runtimeEnv(),
|
|
264
278
|
...(isWinShim ? { shell: true } : {}),
|
|
265
279
|
});
|
|
266
280
|
let stdout = '';
|
package/dist/lib/followups.d.ts
CHANGED
|
@@ -49,6 +49,8 @@ export interface FollowUpRow {
|
|
|
49
49
|
kind?: string;
|
|
50
50
|
form?: string | null;
|
|
51
51
|
producer?: string;
|
|
52
|
+
/** The producer in words (`describeFollowUpProducer`), so no surface prints a bare enum. */
|
|
53
|
+
producerLabel?: string;
|
|
52
54
|
why?: string;
|
|
53
55
|
/** A reminder's own words. */
|
|
54
56
|
content?: string | null;
|
|
@@ -86,7 +88,18 @@ export interface FollowUpCreateResponse {
|
|
|
86
88
|
proposal?: boolean;
|
|
87
89
|
budget?: Json;
|
|
88
90
|
fallbackTriggerId?: string | null;
|
|
91
|
+
/**
|
|
92
|
+
* Set when the credential could not carry the producer the request claimed: the
|
|
93
|
+
* server recorded `recorded` instead (never a higher one) and says why.
|
|
94
|
+
*/
|
|
95
|
+
producerDowngraded?: {
|
|
96
|
+
requested: string;
|
|
97
|
+
recorded: string;
|
|
98
|
+
note: string;
|
|
99
|
+
};
|
|
89
100
|
}
|
|
101
|
+
/** One line telling a person the follow-up was recorded with weaker provenance than claimed. */
|
|
102
|
+
export declare function producerDowngradeNotice(response: FollowUpCreateResponse): string | undefined;
|
|
90
103
|
export interface FollowUpObservability {
|
|
91
104
|
generatedAt?: string;
|
|
92
105
|
agents?: Array<{
|
package/dist/lib/followups.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.followupsApi = exports.FOLLOWUP_SUBJECT_FLAG_HELP = exports.FOLLOWUP_SUB
|
|
|
4
4
|
exports.whenFlagProblem = whenFlagProblem;
|
|
5
5
|
exports.settledLine = settledLine;
|
|
6
6
|
exports.recipeWatchBoundsRefusal = recipeWatchBoundsRefusal;
|
|
7
|
+
exports.producerDowngradeNotice = producerDowngradeNotice;
|
|
7
8
|
exports.resolveFollowUpAgent = resolveFollowUpAgent;
|
|
8
9
|
exports.parseSubjectRef = parseSubjectRef;
|
|
9
10
|
exports.followupSummaryLine = followupSummaryLine;
|
|
@@ -127,6 +128,13 @@ function recipeWatchBoundsRefusal(waitKind, passed) {
|
|
|
127
128
|
: 'A recipe watch ends through its --wait.',
|
|
128
129
|
].join(' ');
|
|
129
130
|
}
|
|
131
|
+
/** One line telling a person the follow-up was recorded with weaker provenance than claimed. */
|
|
132
|
+
function producerDowngradeNotice(response) {
|
|
133
|
+
const downgraded = response.producerDowngraded;
|
|
134
|
+
if (!downgraded)
|
|
135
|
+
return undefined;
|
|
136
|
+
return `Note: recorded as ${downgraded.recorded}, not ${downgraded.requested}. ${downgraded.note}`;
|
|
137
|
+
}
|
|
130
138
|
/**
|
|
131
139
|
* A trigger subject becomes a durable concurrency key, so it cannot be prose.
|
|
132
140
|
* Keep this grammar aligned with TriggerEngine's subject validation and the
|
|
@@ -5,6 +5,7 @@ exports.describeLegacySharedProfile = describeLegacySharedProfile;
|
|
|
5
5
|
const machine_uuid_1 = require("@skrr-ai/auth-core/machine-uuid");
|
|
6
6
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
7
7
|
const keychain_1 = require("./keychain");
|
|
8
|
+
const daemon_command_hint_1 = require("./daemon-command-hint");
|
|
8
9
|
/** An old profile may still share the default machine row and its device pin. */
|
|
9
10
|
function legacyProfileSharesDefaultMachine() {
|
|
10
11
|
const profile = (0, keychain_1.getActiveProfile)();
|
|
@@ -47,6 +48,6 @@ function describeLegacySharedProfile(profile) {
|
|
|
47
48
|
`computer the same machine identity, and it still has it. ${why}Signing in here would replace the default ` +
|
|
48
49
|
"profile's device-key pin and sign the default daemon out. Nothing was changed.",
|
|
49
50
|
`Use a new profile name instead: skrr login --profile ${next}`,
|
|
50
|
-
`(For the background service alone:
|
|
51
|
+
`(For the background service alone: ${(0, daemon_command_hint_1.daemonCommandHint)('login', { profile: next })})`,
|
|
51
52
|
];
|
|
52
53
|
}
|
|
@@ -49,7 +49,12 @@ function parseRuntimeStartStatus(value, fallbackProfile) {
|
|
|
49
49
|
return {
|
|
50
50
|
profile: asString(profile?.name) ?? fallbackProfile,
|
|
51
51
|
serverUrl: asString(root.serverUrl),
|
|
52
|
-
|
|
52
|
+
// `root.cwd` is where THIS `status` ran from (the person's shell), not where
|
|
53
|
+
// the daemon runs. The recovery command binds a login to its `--cwd`, and
|
|
54
|
+
// `skrrd status` itself prints `effectiveCwd` (live daemon, then installed
|
|
55
|
+
// service, then the stored binding) for exactly that. A runtime binary that
|
|
56
|
+
// predates `effectiveCwd` only has `cwd`.
|
|
57
|
+
cwd: asString(root.effectiveCwd) ?? asString(root.cwd),
|
|
53
58
|
reauth: {
|
|
54
59
|
needed: reauth?.needed === true,
|
|
55
60
|
reason: asString(reauth?.reason),
|