@skrr-ai/cli 0.1.103 → 0.1.105
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.js +6 -2
- package/dist/commands/agenda/trigger/list.js +2 -0
- package/dist/commands/agents/chat.js +4 -1
- package/dist/commands/agents/operating-plan.js +5 -2
- package/dist/commands/agents/triggers/create.js +3 -1
- package/dist/commands/agents/triggers/fire.js +2 -0
- package/dist/commands/agents/triggers/replay.js +2 -0
- package/dist/commands/agents/triggers/update.js +12 -4
- package/dist/commands/commitments/action-history.js +7 -2
- package/dist/commands/commitments/cadence-decisions.js +6 -1
- package/dist/commands/commitments/cadence-requests/decide.d.ts +1 -1
- package/dist/commands/commitments/cadence-requests/decide.js +1 -1
- package/dist/commands/commitments/create.js +2 -0
- package/dist/commands/commitments/doctor.js +7 -1
- package/dist/commands/commitments/observations.js +7 -1
- package/dist/commands/commitments/operating-review.d.ts +15 -0
- package/dist/commands/commitments/operating-review.js +23 -2
- package/dist/commands/commitments/preflight.d.ts +12 -0
- package/dist/commands/commitments/preflight.js +23 -9
- package/dist/commands/commitments/reflection.js +6 -1
- package/dist/commands/commitments/show.js +3 -1
- package/dist/commands/commitments/trigger-status.js +2 -22
- package/dist/commands/commitments/wake-capabilities.d.ts +13 -0
- package/dist/commands/commitments/wake-capabilities.js +39 -3
- package/dist/commands/daemon/logs.d.ts +109 -4
- package/dist/commands/daemon/logs.js +397 -43
- package/dist/commands/followups/act.js +13 -1
- package/dist/commands/followups/checkin.js +11 -2
- package/dist/commands/followups/reschedule.js +13 -1
- package/dist/commands/proposals/discard.d.ts +1 -1
- package/dist/commands/proposals/keep.d.ts +1 -1
- package/dist/commands/proposals/list.d.ts +7 -0
- package/dist/commands/proposals/list.js +31 -5
- package/dist/commands/proposals/show.d.ts +2 -1
- package/dist/commands/proposals/show.js +31 -4
- package/dist/commands/proposals/snooze.d.ts +1 -1
- package/dist/commands/requests/create.js +8 -3
- package/dist/commands/spaces/workflows/create.js +3 -3
- package/dist/commands/tasks/complete.js +4 -0
- package/dist/commands/tasks/create.d.ts +2 -0
- package/dist/commands/tasks/create.js +17 -3
- package/dist/commands/tasks/deliverable/add.d.ts +2 -0
- package/dist/commands/tasks/deliverable/add.js +22 -7
- package/dist/commands/tasks/deliverable/list.d.ts +13 -0
- package/dist/commands/tasks/deliverable/list.js +34 -1
- package/dist/commands/tasks/economics-compare.js +7 -1
- package/dist/commands/tasks/expectations.d.ts +10 -0
- package/dist/commands/tasks/expectations.js +31 -3
- package/dist/commands/tasks/list.d.ts +9 -0
- package/dist/commands/tasks/list.js +40 -5
- package/dist/commands/tasks/report.js +1 -1
- package/dist/commands/tasks/result/show.d.ts +7 -0
- package/dist/commands/tasks/result/show.js +32 -1
- package/dist/commands/tasks/result/submit.js +1 -1
- package/dist/commands/tasks/show.d.ts +6 -0
- package/dist/commands/tasks/show.js +23 -0
- package/dist/commands/tasks/update.d.ts +3 -0
- package/dist/commands/tasks/update.js +23 -3
- package/dist/commands/triggers/audit.js +6 -2
- package/dist/commands/triggers/create.js +3 -1
- package/dist/commands/triggers/fire.d.ts +1 -0
- package/dist/commands/triggers/fire.js +16 -1
- package/dist/commands/triggers/preflight.js +6 -5
- package/dist/commands/triggers/replay.js +2 -0
- package/dist/commands/triggers/update.js +12 -4
- package/dist/commands/workspaces/stewardship.js +5 -1
- package/dist/lib/agentic-stream.d.ts +9 -0
- package/dist/lib/agentic-stream.js +38 -18
- package/dist/lib/commitment-event-wakes.d.ts +8 -0
- package/dist/lib/commitment-event-wakes.js +39 -0
- package/dist/lib/commitment-ledgers.d.ts +9 -0
- package/dist/lib/commitment-ledgers.js +126 -0
- package/dist/lib/commitments.d.ts +102 -2
- package/dist/lib/commitments.js +208 -8
- package/dist/lib/delegated-cli.js +13 -2
- package/dist/lib/followups.d.ts +6 -0
- package/dist/lib/followups.js +12 -0
- package/dist/lib/message-content.d.ts +6 -0
- package/dist/lib/message-content.js +24 -0
- package/dist/lib/operating-plan-loops.d.ts +8 -0
- package/dist/lib/operating-plan-loops.js +32 -0
- package/dist/lib/preflight-copy.d.ts +27 -0
- package/dist/lib/preflight-copy.js +89 -0
- package/dist/lib/proposal-decision-command.d.ts +1 -1
- package/dist/lib/proposal-decision-command.js +27 -3
- package/dist/lib/proposals.d.ts +17 -0
- package/dist/lib/proposals.js +41 -0
- package/dist/lib/stewardship-output.js +57 -12
- package/dist/lib/task-economics-line.d.ts +30 -0
- package/dist/lib/task-economics-line.js +49 -0
- package/dist/lib/task-economics.d.ts +15 -0
- package/dist/lib/task-economics.js +73 -8
- package/dist/lib/task-recurrence-outcome.d.ts +19 -0
- package/dist/lib/task-recurrence-outcome.js +30 -0
- package/dist/lib/triggers.d.ts +42 -0
- package/dist/lib/triggers.js +136 -2
- package/dist/lib/widgetTerminalText.d.ts +8 -0
- package/dist/lib/widgetTerminalText.js +116 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +33 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +27 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/telemetryRedaction.d.ts +84 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/telemetryRedaction.js +222 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +33 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +26 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/telemetryRedaction.d.ts +84 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/telemetryRedaction.js +216 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +7628 -7414
- package/docs/v1/operations.md +1 -1
- package/oclif.manifest.json +117 -73
- package/package.json +1 -1
|
@@ -3,6 +3,8 @@ export declare function economicsFromFlags(flags: {
|
|
|
3
3
|
per?: string;
|
|
4
4
|
effort?: string;
|
|
5
5
|
risk?: string;
|
|
6
|
+
confidence?: string;
|
|
7
|
+
rationale?: string;
|
|
6
8
|
}, existing?: unknown): {
|
|
7
9
|
savings?: {
|
|
8
10
|
amount: number;
|
|
@@ -14,3 +16,16 @@ export declare function economicsFromFlags(flags: {
|
|
|
14
16
|
confidence?: "low" | "medium" | "high" | undefined;
|
|
15
17
|
rationale?: string | undefined;
|
|
16
18
|
} | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* One readable line for an estimate: `USD 7/month · 4h effort · medium risk · Human confirmed`.
|
|
21
|
+
* `economics-compare` printed the stored object as one minified JSON line (OSK-15368). A dimension
|
|
22
|
+
* the estimate does not carry is named as not set, never skipped.
|
|
23
|
+
*/
|
|
24
|
+
export declare function describeEstimate(economics: unknown): string;
|
|
25
|
+
/**
|
|
26
|
+
* The number `--rank economics` sorts by, so the order can be read off the screen (OSK-15374):
|
|
27
|
+
* annualized USD savings per effort hour (floored at half an hour), times the risk factor
|
|
28
|
+
* (low 1, medium 0.6, high 0.25). `—` when savings, effort or risk is missing: such a row is not
|
|
29
|
+
* scored and sorts after every row that is.
|
|
30
|
+
*/
|
|
31
|
+
export declare function economicsScoreText(economics: unknown): string;
|
|
@@ -1,22 +1,87 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.economicsFromFlags = economicsFromFlags;
|
|
4
|
+
exports.describeEstimate = describeEstimate;
|
|
5
|
+
exports.economicsScoreText = economicsScoreText;
|
|
4
6
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
7
|
+
/**
|
|
8
|
+
* A flag's amount as a number, or an error that names the flag and the value (OSK-15366). A raw
|
|
9
|
+
* Zod issue array named internal paths (`savings.amount`, `effortHours`) and no flag, so a person
|
|
10
|
+
* who typed `--savings 1,000` was shown a JSON dump of `too_small` and `invalid_type`.
|
|
11
|
+
*/
|
|
12
|
+
function amountFlag(flag, value) {
|
|
13
|
+
const amount = Number(value);
|
|
14
|
+
if (value.trim() === '' || !Number.isFinite(amount) || amount < 0) {
|
|
15
|
+
const hint = /,/.test(value) ? ' Write it without a thousands separator.' : '';
|
|
16
|
+
throw new Error(`${flag} must be a non-negative number (got '${value}').${hint}`);
|
|
17
|
+
}
|
|
18
|
+
return amount;
|
|
19
|
+
}
|
|
20
|
+
/** Zod's internal path, in the flag a person typed. */
|
|
21
|
+
const FLAG_FOR_PATH = {
|
|
22
|
+
'savings.amount': '--savings',
|
|
23
|
+
'savings.per': '--per',
|
|
24
|
+
effortHours: '--effort',
|
|
25
|
+
risk: '--risk',
|
|
26
|
+
confidence: '--confidence',
|
|
27
|
+
rationale: '--rationale',
|
|
28
|
+
};
|
|
5
29
|
function economicsFromFlags(flags, existing) {
|
|
6
|
-
if ([flags.savings, flags.per, flags.effort, flags.risk].every((value) => value === undefined))
|
|
30
|
+
if ([flags.savings, flags.per, flags.effort, flags.risk, flags.confidence, flags.rationale].every((value) => value === undefined))
|
|
7
31
|
return undefined;
|
|
8
32
|
if ((flags.savings === undefined) !== (flags.per === undefined))
|
|
9
33
|
throw new Error('--savings and --per must be supplied together (USD/month, year or once).');
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
throw new Error('Economic amounts must be explicit nonnegative numbers.');
|
|
34
|
+
const savings = flags.savings === undefined ? undefined : amountFlag('--savings', flags.savings);
|
|
35
|
+
const effort = flags.effort === undefined ? undefined : amountFlag('--effort', flags.effort);
|
|
13
36
|
const content = existing && typeof existing === 'object' ? existing : {};
|
|
14
|
-
|
|
37
|
+
const parsed = data_provider_1.ProposalEconomicsInputSchema.safeParse({
|
|
15
38
|
...content,
|
|
16
|
-
...(
|
|
17
|
-
? { savings: { amount:
|
|
39
|
+
...(savings !== undefined
|
|
40
|
+
? { savings: { amount: savings, currency: 'USD', per: flags.per } }
|
|
18
41
|
: {}),
|
|
19
|
-
...(
|
|
42
|
+
...(effort !== undefined ? { effortHours: effort } : {}),
|
|
20
43
|
...(flags.risk !== undefined ? { risk: flags.risk } : {}),
|
|
44
|
+
...(flags.confidence !== undefined ? { confidence: flags.confidence } : {}),
|
|
45
|
+
...(flags.rationale !== undefined ? { rationale: flags.rationale } : {}),
|
|
21
46
|
});
|
|
47
|
+
if (parsed.success)
|
|
48
|
+
return parsed.data;
|
|
49
|
+
// One line per refused field, in the flag when there is one, never the raw issue array.
|
|
50
|
+
throw new Error(parsed.error.issues
|
|
51
|
+
.map((issue) => {
|
|
52
|
+
const path = issue.path.join('.');
|
|
53
|
+
return `${FLAG_FOR_PATH[path] ?? `economics.${path || 'value'}`}: ${issue.message}`;
|
|
54
|
+
})
|
|
55
|
+
.join('\n'));
|
|
56
|
+
}
|
|
57
|
+
const BASIS_WORDS = {
|
|
58
|
+
agent_estimate: 'Agent estimate',
|
|
59
|
+
human_confirmed: 'Human confirmed',
|
|
60
|
+
measured: 'Measured',
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* One readable line for an estimate: `USD 7/month · 4h effort · medium risk · Human confirmed`.
|
|
64
|
+
* `economics-compare` printed the stored object as one minified JSON line (OSK-15368). A dimension
|
|
65
|
+
* the estimate does not carry is named as not set, never skipped.
|
|
66
|
+
*/
|
|
67
|
+
function describeEstimate(economics) {
|
|
68
|
+
const value = (economics && typeof economics === 'object' ? economics : {});
|
|
69
|
+
return [
|
|
70
|
+
value.savings
|
|
71
|
+
? `${value.savings.currency ?? 'USD'} ${value.savings.amount}/${value.savings.per}`
|
|
72
|
+
: 'savings not set',
|
|
73
|
+
value.effortHours !== undefined ? `${value.effortHours}h effort` : 'effort not set',
|
|
74
|
+
value.risk ? `${value.risk} risk` : 'risk not set',
|
|
75
|
+
BASIS_WORDS[value.basis ?? ''] ?? 'basis unknown',
|
|
76
|
+
].join(' · ');
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The number `--rank economics` sorts by, so the order can be read off the screen (OSK-15374):
|
|
80
|
+
* annualized USD savings per effort hour (floored at half an hour), times the risk factor
|
|
81
|
+
* (low 1, medium 0.6, high 0.25). `—` when savings, effort or risk is missing: such a row is not
|
|
82
|
+
* scored and sorts after every row that is.
|
|
83
|
+
*/
|
|
84
|
+
function economicsScoreText(economics) {
|
|
85
|
+
const score = (0, data_provider_1.proposalEconomicsRank)(economics).score;
|
|
86
|
+
return score === null || !Number.isFinite(score) ? '—' : String(Math.round(score));
|
|
22
87
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What completing a recurring occurrence did to its series, as a sentence (OSK-15483).
|
|
3
|
+
*
|
|
4
|
+
* `tasks complete` armed the next occurrence (or deliberately did not) and printed nothing either way, so a
|
|
5
|
+
* person could not tell that the series continued, or that the third of three runs had ended it. The server
|
|
6
|
+
* reports `recurrence` on the completion; this words it.
|
|
7
|
+
*/
|
|
8
|
+
export type RecurrenceOutcome = {
|
|
9
|
+
state?: string;
|
|
10
|
+
reason?: string;
|
|
11
|
+
successor?: {
|
|
12
|
+
id?: string;
|
|
13
|
+
identifier?: string | null;
|
|
14
|
+
scheduledFor?: string | null;
|
|
15
|
+
};
|
|
16
|
+
armFailure?: unknown;
|
|
17
|
+
};
|
|
18
|
+
/** One line, or null when the Task does not recur (or an older server did not say). */
|
|
19
|
+
export declare function recurrenceOutcomeLine(outcome: RecurrenceOutcome | null | undefined): string | null;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.recurrenceOutcomeLine = recurrenceOutcomeLine;
|
|
4
|
+
const ENDED_BECAUSE = {
|
|
5
|
+
max_runs: 'it reached its maximum number of runs',
|
|
6
|
+
end_date: 'its end date has passed',
|
|
7
|
+
no_next_occurrence: 'its pattern has no further occurrence',
|
|
8
|
+
};
|
|
9
|
+
/** One line, or null when the Task does not recur (or an older server did not say). */
|
|
10
|
+
function recurrenceOutcomeLine(outcome) {
|
|
11
|
+
if (!outcome || typeof outcome !== 'object')
|
|
12
|
+
return null;
|
|
13
|
+
switch (outcome.state) {
|
|
14
|
+
case 'armed': {
|
|
15
|
+
const next = outcome.successor?.identifier || outcome.successor?.id || 'the next occurrence';
|
|
16
|
+
const when = outcome.successor?.scheduledFor ? ` for ${outcome.successor.scheduledFor}` : '';
|
|
17
|
+
return `Recurrence: next occurrence ${next} is scheduled${when}.`;
|
|
18
|
+
}
|
|
19
|
+
case 'ended':
|
|
20
|
+
return `Recurrence: the series has ended; ${ENDED_BECAUSE[outcome.reason ?? ''] ?? 'nothing further is due'}.`;
|
|
21
|
+
case 'paused':
|
|
22
|
+
return 'Recurrence: the series is paused, so no next occurrence was scheduled.';
|
|
23
|
+
case 'routine_managed':
|
|
24
|
+
return 'Recurrence: a Routine owns this series, so no next occurrence was scheduled here.';
|
|
25
|
+
case 'not_armed':
|
|
26
|
+
return 'Recurrence: the series is live but no next occurrence was scheduled; check it with `tasks show`.';
|
|
27
|
+
default:
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/lib/triggers.d.ts
CHANGED
|
@@ -295,6 +295,41 @@ export declare function assertKindFlagsApply(kind: unknown, flags: Record<string
|
|
|
295
295
|
* by itself (OSK-15492). Absent when the server reported nothing to say.
|
|
296
296
|
*/
|
|
297
297
|
export declare function describeScheduledFire(result: Record<string, unknown>): string | undefined;
|
|
298
|
+
/**
|
|
299
|
+
* Was the fire admitted? `in_progress` and `awaiting_runtime` are admissions whose terminal arrives
|
|
300
|
+
* later (every agent-turn fire answers `in_progress`), and a repeat of a fire that was admitted is
|
|
301
|
+
* `duplicate` with the original beside it; none of them is a refusal (OSK-3867 made the same mistake
|
|
302
|
+
* about `in_progress` once). Only these are success for `triggers fire --strict`.
|
|
303
|
+
*/
|
|
304
|
+
export declare function fireWasAccepted(result: Record<string, unknown>): boolean;
|
|
305
|
+
/**
|
|
306
|
+
* The lines a fire or replay owes beyond `outcome=… reason=…` (OSK-15494): the sentence the
|
|
307
|
+
* server wrote ("Replayed fire … resolved a step that was not waiting when that fire ran:
|
|
308
|
+
* run …, step …"), or, for a debounced fire, when a retry will be accepted. Empty when the server
|
|
309
|
+
* reported nothing more.
|
|
310
|
+
*/
|
|
311
|
+
export declare function describeFireDetail(result: Record<string, unknown>): string[];
|
|
312
|
+
/**
|
|
313
|
+
* A projected fire as the trigger's own clock reads it. The header said `America/New_York` over
|
|
314
|
+
* values that were UTC instants, so nothing on the line was in the zone it named (OSK-15407). The
|
|
315
|
+
* instant itself stays beside it, exact.
|
|
316
|
+
*/
|
|
317
|
+
export declare function formatFireInZone(iso: string, timeZone: string | undefined): string;
|
|
318
|
+
/**
|
|
319
|
+
* The lines `triggers preflight` prints under "Next fires". A fire the trigger's own working or
|
|
320
|
+
* quiet window would skip is marked, because the gate above is evaluated at the current instant
|
|
321
|
+
* only and a window that excludes every scheduled time otherwise read as healthy (OSK-15407).
|
|
322
|
+
*/
|
|
323
|
+
type PreflightSchedule = NonNullable<TriggerPreflightReport['schedule']>;
|
|
324
|
+
export declare function nextFireLines(schedule: PreflightSchedule | null | undefined): string[];
|
|
325
|
+
/**
|
|
326
|
+
* A destination as `key=value` pairs, without Mongo's storage fields.
|
|
327
|
+
*
|
|
328
|
+
* A nested object that is longer than a short locator (`agentChat` is the Agent's whole chat row,
|
|
329
|
+
* with the preview of its last message) is named by its id, not dumped: the line printed ~700
|
|
330
|
+
* characters of unrelated chat state into a preflight (OSK-16200). `--json` carries all of it.
|
|
331
|
+
*/
|
|
332
|
+
export declare function destinationLine(destination: Record<string, unknown>): string;
|
|
298
333
|
export declare function withQuery(path: string, params: Record<string, string | number | boolean | string[] | undefined>): string;
|
|
299
334
|
/**
|
|
300
335
|
* Build the universal-engine scope block for an agent-owned trigger.
|
|
@@ -349,6 +384,12 @@ export declare class AgentTriggerScopeError extends Error {
|
|
|
349
384
|
*/
|
|
350
385
|
export declare function fetchAgentScopedTrigger(agentId: string, triggerId: string): Promise<TriggerDefinition>;
|
|
351
386
|
export declare function scopeLabel(trigger: TriggerDefinition): string;
|
|
387
|
+
/**
|
|
388
|
+
* When an armed `once` trigger is due: its `once.runAt` (or `once.at`), and only while it has not
|
|
389
|
+
* fired. A follow-up check-in due next month had no `schedule.nextRunAt`, so every table said `-` and
|
|
390
|
+
* `triggers show` said `Armed: yes` with no time (OSK-15416).
|
|
391
|
+
*/
|
|
392
|
+
export declare function onceDueAt(trigger: TriggerDefinition): string | null;
|
|
352
393
|
export declare function nextRunLabel(trigger: TriggerDefinition): string;
|
|
353
394
|
/**
|
|
354
395
|
* Whether a trigger will actually fire — not merely whether someone left it on.
|
|
@@ -511,3 +552,4 @@ export declare const triggerApi: {
|
|
|
511
552
|
setSpaceAssistant: (spaceId: string, body: Record<string, unknown>) => Promise<SpaceAssistantResponse>;
|
|
512
553
|
deleteSpaceAssistant: (spaceId: string) => Promise<Record<string, unknown>>;
|
|
513
554
|
};
|
|
555
|
+
export {};
|
package/dist/lib/triggers.js
CHANGED
|
@@ -25,12 +25,18 @@ exports.mergeObjectField = mergeObjectField;
|
|
|
25
25
|
exports.mergeTriggerKindConfig = mergeTriggerKindConfig;
|
|
26
26
|
exports.assertKindFlagsApply = assertKindFlagsApply;
|
|
27
27
|
exports.describeScheduledFire = describeScheduledFire;
|
|
28
|
+
exports.fireWasAccepted = fireWasAccepted;
|
|
29
|
+
exports.describeFireDetail = describeFireDetail;
|
|
30
|
+
exports.formatFireInZone = formatFireInZone;
|
|
31
|
+
exports.nextFireLines = nextFireLines;
|
|
32
|
+
exports.destinationLine = destinationLine;
|
|
28
33
|
exports.withQuery = withQuery;
|
|
29
34
|
exports.agentScope = agentScope;
|
|
30
35
|
exports.spaceSubscriberScope = spaceSubscriberScope;
|
|
31
36
|
exports.agentRefEntity = agentRefEntity;
|
|
32
37
|
exports.fetchAgentScopedTrigger = fetchAgentScopedTrigger;
|
|
33
38
|
exports.scopeLabel = scopeLabel;
|
|
39
|
+
exports.onceDueAt = onceDueAt;
|
|
34
40
|
exports.nextRunLabel = nextRunLabel;
|
|
35
41
|
exports.armedLabel = armedLabel;
|
|
36
42
|
exports.degradedMarker = degradedMarker;
|
|
@@ -766,6 +772,116 @@ function describeScheduledFire(result) {
|
|
|
766
772
|
}
|
|
767
773
|
return undefined;
|
|
768
774
|
}
|
|
775
|
+
/**
|
|
776
|
+
* Was the fire admitted? `in_progress` and `awaiting_runtime` are admissions whose terminal arrives
|
|
777
|
+
* later (every agent-turn fire answers `in_progress`), and a repeat of a fire that was admitted is
|
|
778
|
+
* `duplicate` with the original beside it; none of them is a refusal (OSK-3867 made the same mistake
|
|
779
|
+
* about `in_progress` once). Only these are success for `triggers fire --strict`.
|
|
780
|
+
*/
|
|
781
|
+
function fireWasAccepted(result) {
|
|
782
|
+
const accepted = data_provider_1.ACCEPTED_AGENT_TRIGGER_FIRE_OUTCOMES;
|
|
783
|
+
if (typeof result.outcome === 'string' && accepted.includes(result.outcome))
|
|
784
|
+
return true;
|
|
785
|
+
return (result.outcome === 'duplicate' &&
|
|
786
|
+
typeof result.originalOutcome === 'string' &&
|
|
787
|
+
accepted.includes(result.originalOutcome));
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* The lines a fire or replay owes beyond `outcome=… reason=…` (OSK-15494): the sentence the
|
|
791
|
+
* server wrote ("Replayed fire … resolved a step that was not waiting when that fire ran:
|
|
792
|
+
* run …, step …"), or, for a debounced fire, when a retry will be accepted. Empty when the server
|
|
793
|
+
* reported nothing more.
|
|
794
|
+
*/
|
|
795
|
+
function describeFireDetail(result) {
|
|
796
|
+
const lines = [];
|
|
797
|
+
if (typeof result.detail === 'string' && result.detail.trim())
|
|
798
|
+
lines.push(result.detail.trim());
|
|
799
|
+
else if (typeof result.retryAt === 'string' && result.retryAt) {
|
|
800
|
+
lines.push(`Try again after ${result.retryAt}.`);
|
|
801
|
+
}
|
|
802
|
+
// A fire option the server does not read is named, because dropping it without a word left a caller
|
|
803
|
+
// unable to tell whether `--from-json` was honoured (OSK-15498).
|
|
804
|
+
if (Array.isArray(result.ignoredOptions) && result.ignoredOptions.length > 0) {
|
|
805
|
+
lines.push(`Ignored fire options (a fire does not read them): ${result.ignoredOptions.map(String).join(', ')}`);
|
|
806
|
+
}
|
|
807
|
+
// Only a fire that resolved a step: an executionRef is a job id, a message id or a
|
|
808
|
+
// notification id for every other executor, and "Resolved:" would be wrong for them.
|
|
809
|
+
if (typeof result.executionRef === 'string' &&
|
|
810
|
+
result.executionRef &&
|
|
811
|
+
result.outcome === 'succeeded' &&
|
|
812
|
+
typeof result.reason === 'string' &&
|
|
813
|
+
result.reason.startsWith('resolved:')) {
|
|
814
|
+
lines.push(`Resolved: ${result.executionRef}`);
|
|
815
|
+
}
|
|
816
|
+
return lines;
|
|
817
|
+
}
|
|
818
|
+
const FIRE_DENIAL_WORDS = {
|
|
819
|
+
outside_working_hours: 'outside working hours',
|
|
820
|
+
inside_quiet_hours: 'inside quiet hours',
|
|
821
|
+
};
|
|
822
|
+
/**
|
|
823
|
+
* A projected fire as the trigger's own clock reads it. The header said `America/New_York` over
|
|
824
|
+
* values that were UTC instants, so nothing on the line was in the zone it named (OSK-15407). The
|
|
825
|
+
* instant itself stays beside it, exact.
|
|
826
|
+
*/
|
|
827
|
+
function formatFireInZone(iso, timeZone) {
|
|
828
|
+
const date = new Date(iso);
|
|
829
|
+
if (!timeZone || Number.isNaN(date.getTime()))
|
|
830
|
+
return iso;
|
|
831
|
+
try {
|
|
832
|
+
const parts = new Intl.DateTimeFormat('en-CA', {
|
|
833
|
+
timeZone,
|
|
834
|
+
year: 'numeric',
|
|
835
|
+
month: '2-digit',
|
|
836
|
+
day: '2-digit',
|
|
837
|
+
hour: '2-digit',
|
|
838
|
+
minute: '2-digit',
|
|
839
|
+
hourCycle: 'h23',
|
|
840
|
+
timeZoneName: 'short',
|
|
841
|
+
}).formatToParts(date);
|
|
842
|
+
const part = (type) => parts.find((entry) => entry.type === type)?.value ?? '';
|
|
843
|
+
return `${part('year')}-${part('month')}-${part('day')} ${part('hour')}:${part('minute')} ${part('timeZoneName')} (${iso})`;
|
|
844
|
+
}
|
|
845
|
+
catch {
|
|
846
|
+
return iso;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
function nextFireLines(schedule) {
|
|
850
|
+
if (!schedule?.nextFires?.length)
|
|
851
|
+
return [];
|
|
852
|
+
const windows = new Map((schedule.nextFireWindows ?? []).map((entry) => [entry.at, entry]));
|
|
853
|
+
const lines = [`Next fires (${schedule.timezone}):`];
|
|
854
|
+
for (const at of schedule.nextFires) {
|
|
855
|
+
const window = windows.get(at);
|
|
856
|
+
const mark = window && !window.runs
|
|
857
|
+
? ` ✗ would be skipped: ${FIRE_DENIAL_WORDS[window.reason ?? ''] ?? window.reason ?? 'window'}`
|
|
858
|
+
: '';
|
|
859
|
+
lines.push(` ${formatFireInZone(at, schedule.timezone)}${mark}`);
|
|
860
|
+
}
|
|
861
|
+
return lines;
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* A destination as `key=value` pairs, without Mongo's storage fields.
|
|
865
|
+
*
|
|
866
|
+
* A nested object that is longer than a short locator (`agentChat` is the Agent's whole chat row,
|
|
867
|
+
* with the preview of its last message) is named by its id, not dumped: the line printed ~700
|
|
868
|
+
* characters of unrelated chat state into a preflight (OSK-16200). `--json` carries all of it.
|
|
869
|
+
*/
|
|
870
|
+
function destinationLine(destination) {
|
|
871
|
+
const fields = Object.entries(destination).filter(([key]) => key !== '_id' && key !== '__v');
|
|
872
|
+
return fields.map(([key, value]) => `${key}=${destinationValue(value)}`).join(' ');
|
|
873
|
+
}
|
|
874
|
+
const DESTINATION_INLINE_MAX = 80;
|
|
875
|
+
function destinationValue(value) {
|
|
876
|
+
if (typeof value !== 'object' || value === null)
|
|
877
|
+
return String(value);
|
|
878
|
+
const json = JSON.stringify(value);
|
|
879
|
+
if (json.length <= DESTINATION_INLINE_MAX)
|
|
880
|
+
return json;
|
|
881
|
+
const record = value;
|
|
882
|
+
const id = [record.agentChatId, record.id, record.conversationId].find((candidate) => typeof candidate === 'string' && candidate);
|
|
883
|
+
return id ? String(id) : `{${Object.keys(record).length} fields; see --json}`;
|
|
884
|
+
}
|
|
769
885
|
function withQuery(path, params) {
|
|
770
886
|
const search = new URLSearchParams();
|
|
771
887
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -849,9 +965,24 @@ function scopeLabel(trigger) {
|
|
|
849
965
|
.join(',');
|
|
850
966
|
return refText ? `${kind}:${refText}` : kind;
|
|
851
967
|
}
|
|
968
|
+
/**
|
|
969
|
+
* When an armed `once` trigger is due: its `once.runAt` (or `once.at`), and only while it has not
|
|
970
|
+
* fired. A follow-up check-in due next month had no `schedule.nextRunAt`, so every table said `-` and
|
|
971
|
+
* `triggers show` said `Armed: yes` with no time (OSK-15416).
|
|
972
|
+
*/
|
|
973
|
+
function onceDueAt(trigger) {
|
|
974
|
+
if (trigger.kind !== 'once' || !trigger.once)
|
|
975
|
+
return null;
|
|
976
|
+
const once = trigger.once;
|
|
977
|
+
if (once.firedAt || trigger.enabled === false)
|
|
978
|
+
return null;
|
|
979
|
+
const due = once.runAt ?? once.at;
|
|
980
|
+
return due ? String(due) : null;
|
|
981
|
+
}
|
|
852
982
|
function nextRunLabel(trigger) {
|
|
853
983
|
const next = trigger.schedule?.nextRunAt ??
|
|
854
984
|
trigger.selfScheduled?.nextRunAt ??
|
|
985
|
+
onceDueAt(trigger) ??
|
|
855
986
|
trigger.audit?.lastFiredAt ??
|
|
856
987
|
'';
|
|
857
988
|
return next ? String(next) : '-';
|
|
@@ -1073,8 +1204,11 @@ function renderTriggerSummary(trigger, log, options = {}) {
|
|
|
1073
1204
|
if (effect?.writeMode)
|
|
1074
1205
|
log(`Writes: ${writeModeLabel(String(effect.writeMode))}`);
|
|
1075
1206
|
const next = nextRunLabel(trigger);
|
|
1076
|
-
if (next !== '-')
|
|
1077
|
-
|
|
1207
|
+
if (next !== '-') {
|
|
1208
|
+
// `nextRunLabel` falls back to the last fire when nothing is scheduled; say which it is.
|
|
1209
|
+
const upcoming = Boolean(trigger.schedule?.nextRunAt ?? trigger.selfScheduled?.nextRunAt ?? onceDueAt(trigger));
|
|
1210
|
+
log(`${upcoming ? 'Next run:' : 'Last run:'}${next.length > 0 ? ` ${next}` : ''}`);
|
|
1211
|
+
}
|
|
1078
1212
|
/*
|
|
1079
1213
|
* What actually happened when it fired.
|
|
1080
1214
|
*
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param text the answer so far, widget blocks included
|
|
3
|
+
* @param final the answer will not grow any more (release held-back text, and report
|
|
4
|
+
* a block still open as cut off)
|
|
5
|
+
*/
|
|
6
|
+
export declare function widgetsAsText(text: string, { final }: {
|
|
7
|
+
final: boolean;
|
|
8
|
+
}): string;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.widgetsAsText = widgetsAsText;
|
|
4
|
+
/**
|
|
5
|
+
* An agent's answer as a TERMINAL can show it: each widget block replaced by its
|
|
6
|
+
* fallback sentence.
|
|
7
|
+
*
|
|
8
|
+
* An agent may answer with a widget: a fenced block of CSS, HTML and JavaScript
|
|
9
|
+
* that the web and mobile apps draw in a sandboxed frame. A terminal cannot draw
|
|
10
|
+
* one, and printing the block is not a faithful rendering of it: it is a program,
|
|
11
|
+
* dumped between two paragraphs of prose, that can run to hundreds of kilobytes.
|
|
12
|
+
* The design record is explicit that the CLI is one of the consumers that "gets the
|
|
13
|
+
* sentence" (docs/architecture/generative-ui-2026-10-13.md section 15.4), the same
|
|
14
|
+
* sentence the server stores as `Message.text` and sends to a channel. The block
|
|
15
|
+
* itself is in the persisted message for the app to draw.
|
|
16
|
+
*
|
|
17
|
+
* WHICH LANES THIS COVERS. The streaming lane (`text.delta`), the final answer
|
|
18
|
+
* (`run.result`, `--no-stream`, `--json`'s `response`) and the comparison against the
|
|
19
|
+
* persisted record all read the ANSWER, and now all read this. The raw lane does
|
|
20
|
+
* not: a `session.output` envelope is the wire, and `--jsonl` keeps mirroring it
|
|
21
|
+
* unchanged for a consumer that wants the block itself (a coding agent that asked a
|
|
22
|
+
* cloud agent for a widget, say). That is where the source is.
|
|
23
|
+
*
|
|
24
|
+
* ONE PARSER. The grammar is read only through the shared incremental parser
|
|
25
|
+
* (`createUiWidgetFenceParser`), the one the web client and the server use, so a
|
|
26
|
+
* block is recognised, bounded and refused identically on every surface; this file
|
|
27
|
+
* never spells the fence. What it adds is the JOIN: how a substituted sentence sits
|
|
28
|
+
* among the prose. That rule is the server's (`GenerativeUi/ingest.js`
|
|
29
|
+
* `createTextDeriver`, which produces `Message.text`) and is mirrored here, because
|
|
30
|
+
* a terminal-side answer that spaced the sentence differently would be a second,
|
|
31
|
+
* quietly different text, and the whitespace-insensitive comparison against the
|
|
32
|
+
* record (`streamShowsRecordedText`) would no longer be the only thing keeping the
|
|
33
|
+
* two honest. The join: a fallback is its own paragraph, preceded by a blank line
|
|
34
|
+
* unless the text so far already ends in one, and followed by a blank line before
|
|
35
|
+
* whatever comes next; the break is written only when something follows, so the
|
|
36
|
+
* result only ever grows by appending while the input grows, which is what a
|
|
37
|
+
* terminal that prints "what is new" needs (`renderableDelta`).
|
|
38
|
+
*
|
|
39
|
+
* `final: false` holds back a trailing line that could still turn into a block
|
|
40
|
+
* opener, so text a terminal has already printed is never retracted.
|
|
41
|
+
*/
|
|
42
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
43
|
+
const PARAGRAPH_BREAK = '\n\n';
|
|
44
|
+
/** The last line of `text` starts with a colon: the shape a held-back opener has. */
|
|
45
|
+
function endsInColonLine(text) {
|
|
46
|
+
const start = Math.max(text.lastIndexOf('\n'), text.lastIndexOf('\r')) + 1;
|
|
47
|
+
return text.charCodeAt(start) === 58;
|
|
48
|
+
}
|
|
49
|
+
/** What must precede a paragraph so a blank line separates it from `tail`. */
|
|
50
|
+
function leadForBlankLine(tail) {
|
|
51
|
+
if (tail.endsWith('\n\n'))
|
|
52
|
+
return '';
|
|
53
|
+
if (tail.endsWith('\n'))
|
|
54
|
+
return '\n';
|
|
55
|
+
return PARAGRAPH_BREAK;
|
|
56
|
+
}
|
|
57
|
+
/** Fence-looking lines in a substituted sentence are pushed off column 0, so nothing re-reads them as a block. */
|
|
58
|
+
const OPENER_AT_LINE_START = new RegExp(`^(:{${data_provider_1.UI_WIDGET_FENCE.minColons},})`, 'gm');
|
|
59
|
+
function fallbackFor(event) {
|
|
60
|
+
if (event.type === 'widget-complete')
|
|
61
|
+
return (0, data_provider_1.uiWidgetFallbackText)(event.payload);
|
|
62
|
+
// A cut-off block contributes nothing: its sentence is not known yet, and adding the
|
|
63
|
+
// title now would make a later, shorter sentence shrink the text.
|
|
64
|
+
if (event.type === 'widget-error' && event.partial && event.reason !== 'truncated') {
|
|
65
|
+
return (0, data_provider_1.uiWidgetFallbackText)(event.partial);
|
|
66
|
+
}
|
|
67
|
+
return '';
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* @param text the answer so far, widget blocks included
|
|
71
|
+
* @param final the answer will not grow any more (release held-back text, and report
|
|
72
|
+
* a block still open as cut off)
|
|
73
|
+
*/
|
|
74
|
+
function widgetsAsText(text, { final }) {
|
|
75
|
+
// Cheap necessary condition for any work at all: a block needs the fence name, and
|
|
76
|
+
// while text is still streaming a trailing colon line may be an opener not yet decided.
|
|
77
|
+
if (text.indexOf(data_provider_1.UI_WIDGET_FENCE.name) === -1 && !(!final && endsInColonLine(text))) {
|
|
78
|
+
return text;
|
|
79
|
+
}
|
|
80
|
+
const parser = (0, data_provider_1.createUiWidgetFenceParser)();
|
|
81
|
+
const events = parser.push(text).slice();
|
|
82
|
+
if (final)
|
|
83
|
+
events.push(...parser.end());
|
|
84
|
+
let out = '';
|
|
85
|
+
let tail = '';
|
|
86
|
+
let started = false;
|
|
87
|
+
let pendingBreak = false;
|
|
88
|
+
const emit = (chunk) => {
|
|
89
|
+
if (chunk === '')
|
|
90
|
+
return;
|
|
91
|
+
started = true;
|
|
92
|
+
tail = (tail + chunk).slice(-2);
|
|
93
|
+
out += chunk;
|
|
94
|
+
};
|
|
95
|
+
for (const event of events) {
|
|
96
|
+
if (event.type === 'text') {
|
|
97
|
+
if (event.text === '')
|
|
98
|
+
continue;
|
|
99
|
+
const lead = pendingBreak ? PARAGRAPH_BREAK : '';
|
|
100
|
+
pendingBreak = false;
|
|
101
|
+
emit(lead + event.text);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const fallback = fallbackFor(event);
|
|
105
|
+
if (!fallback)
|
|
106
|
+
continue;
|
|
107
|
+
let lead = '';
|
|
108
|
+
if (pendingBreak)
|
|
109
|
+
lead = PARAGRAPH_BREAK;
|
|
110
|
+
else if (started)
|
|
111
|
+
lead = leadForBlankLine(tail);
|
|
112
|
+
pendingBreak = true;
|
|
113
|
+
emit(lead + fallback.replace(OPENER_AT_LINE_START, ' $1'));
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
@@ -133,3 +133,36 @@ export declare function parseCliHandoffDpopProofResponse(value: unknown): CliHan
|
|
|
133
133
|
export declare const DELEGATED_AGENT_CLI_ID_PREFIX = "oversky-daemon-agent-cli-";
|
|
134
134
|
export declare const WORKLOAD_CLI_ID_PREFIX = "oversky-daemon-workload-cli-";
|
|
135
135
|
export declare function classifyCliFamilyId(id: unknown): 'agent' | 'workload' | 'person';
|
|
136
|
+
/**
|
|
137
|
+
* Who keeps a daemon running, which decides how it is signed in again.
|
|
138
|
+
* - `desktop`: the Skrr desktop app hosts it (Electron-as-Node under the
|
|
139
|
+
* app's LaunchAgent). The app mints the daemon's credential itself at
|
|
140
|
+
* `POST /api/daemons/token`, with a device proof, on every start of its
|
|
141
|
+
* daemon client (OSK-15990), so reopening the app IS the sign-in. Neither
|
|
142
|
+
* login command repairs it: `skrr daemon login` refuses an Electron-hosted
|
|
143
|
+
* daemon (its program is the app, not a `skrrd`), and a `skrrd` on PATH is
|
|
144
|
+
* usually not installed, or is a different build from the one running.
|
|
145
|
+
* - `service`: `skrrd install` (launchd/systemd/schtasks) or a hand start;
|
|
146
|
+
* `skrrd login` for that profile, server and working directory is the fix.
|
|
147
|
+
*/
|
|
148
|
+
export type DaemonSupervisor = 'desktop' | 'service';
|
|
149
|
+
/**
|
|
150
|
+
* The ONE sentence for "only a new daemon sign-in repairs this hand-off
|
|
151
|
+
* refusal" (OSK-16216). The daemon fills it in with what it knows about
|
|
152
|
+
* itself and sends it as the loopback 503's `remedy`; the CLI uses it, with
|
|
153
|
+
* nothing filled in, when an older daemon sent none. Before this the two were
|
|
154
|
+
* written separately and disagreed — the CLI said `skrr daemon login`, the
|
|
155
|
+
* daemon said `skrrd --profile … login` — and neither was right for a daemon
|
|
156
|
+
* the desktop app runs.
|
|
157
|
+
*
|
|
158
|
+
* Returned without a leading capital or trailing period so callers can put it
|
|
159
|
+
* after "Fix: " or "— ".
|
|
160
|
+
*/
|
|
161
|
+
export declare function daemonSignInRemedy(opts?: {
|
|
162
|
+
/** How the daemon is run; absent when the caller cannot tell. */
|
|
163
|
+
supervisor?: DaemonSupervisor;
|
|
164
|
+
/** The exact `skrrd … login` for this profile, when the daemon knows it. */
|
|
165
|
+
loginCommand?: string;
|
|
166
|
+
/** The refusal was DEVICE_PROOF_PIN_MISMATCH: say that a sign-in re-pins. */
|
|
167
|
+
repinsDeviceKey?: boolean;
|
|
168
|
+
}): string;
|
|
@@ -27,6 +27,7 @@ exports.descriptorOffersDpop = descriptorOffersDpop;
|
|
|
27
27
|
exports.parseCliHandoffTokenResponse = parseCliHandoffTokenResponse;
|
|
28
28
|
exports.parseCliHandoffDpopProofResponse = parseCliHandoffDpopProofResponse;
|
|
29
29
|
exports.classifyCliFamilyId = classifyCliFamilyId;
|
|
30
|
+
exports.daemonSignInRemedy = daemonSignInRemedy;
|
|
30
31
|
/**
|
|
31
32
|
* What a caller may redeem the descriptor secret for.
|
|
32
33
|
* - `refresh_family`: the legacy durable mint (90-day cli-scope family).
|
|
@@ -156,3 +157,29 @@ function classifyCliFamilyId(id) {
|
|
|
156
157
|
return 'workload';
|
|
157
158
|
return 'person';
|
|
158
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* The ONE sentence for "only a new daemon sign-in repairs this hand-off
|
|
162
|
+
* refusal" (OSK-16216). The daemon fills it in with what it knows about
|
|
163
|
+
* itself and sends it as the loopback 503's `remedy`; the CLI uses it, with
|
|
164
|
+
* nothing filled in, when an older daemon sent none. Before this the two were
|
|
165
|
+
* written separately and disagreed — the CLI said `skrr daemon login`, the
|
|
166
|
+
* daemon said `skrrd --profile … login` — and neither was right for a daemon
|
|
167
|
+
* the desktop app runs.
|
|
168
|
+
*
|
|
169
|
+
* Returned without a leading capital or trailing period so callers can put it
|
|
170
|
+
* after "Fix: " or "— ".
|
|
171
|
+
*/
|
|
172
|
+
function daemonSignInRemedy(opts = {}) {
|
|
173
|
+
const repin = opts.repinsDeviceKey === true;
|
|
174
|
+
if (opts.supervisor === 'desktop') {
|
|
175
|
+
return ('quit and reopen the Skrr desktop app on the machine running the daemon (install its update ' +
|
|
176
|
+
`first if one is offered): the app signs its daemon in again on start${repin ? ' and re-pins the device key' : ''}`);
|
|
177
|
+
}
|
|
178
|
+
if (opts.supervisor === 'service') {
|
|
179
|
+
const login = opts.loginCommand ? `\`${opts.loginCommand}\`` : '`skrr daemon login`';
|
|
180
|
+
return `run ${login}${repin ? ' — an interactive login re-pins the device key' : ''}`;
|
|
181
|
+
}
|
|
182
|
+
return ('sign the daemon in again on the machine running it: if the Skrr desktop app runs it, quit ' +
|
|
183
|
+
'and reopen the app; otherwise run `skrr daemon login`' +
|
|
184
|
+
(repin ? ' (either one re-pins the device key)' : ''));
|
|
185
|
+
}
|