@skrr-ai/cli 0.1.104 → 0.1.106
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/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 +73 -3
- package/dist/commands/daemon/logs.js +304 -17
- 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/retry-schedule.js +11 -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 +28 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/telemetryRedaction.js +50 -5
- 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 +28 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/telemetryRedaction.js +50 -5
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +7261 -7056
- package/docs/v1/operations.md +1 -1
- package/oclif.manifest.json +116 -72
- package/package.json +1 -1
package/dist/lib/commitments.js
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.COMMITMENT_CLI_EVALUATORS = exports.COMMITMENT_CREATE_KEYS = exports.COMMITMENT_REMEDIATION_ACTIONS = exports.COMMITMENT_FEEDBACK_KINDS = exports.commitmentApi = exports.commitmentPackApi = exports.MAX_COMMITMENT_INTERVAL_MS = exports.MIN_COMMITMENT_INTERVAL_MS = exports.COMMITMENT_EXECUTION_ISOLATION_MODES = exports.COMMITMENT_AUTHORABLE_CADENCE_MODES = exports.COMMITMENT_KIND_VALUES = exports.COMMITMENT_STATUSES = exports.COMMITMENT_CADENCE_MAX_TIMES = exports.COMMITMENT_CADENCE_TIME_PATTERN = exports.COMMITMENT_CADENCE_MODES = exports.COMMITMENT_TEMPLATE_IDS = exports.COMMITMENT_WATCHED_SOURCES = void 0;
|
|
4
4
|
exports.permissionLines = permissionLines;
|
|
5
|
+
exports.scheduleIssueLines = scheduleIssueLines;
|
|
5
6
|
exports.retiredFieldsLine = retiredFieldsLine;
|
|
6
7
|
exports.readCommitmentAgentDirective = readCommitmentAgentDirective;
|
|
7
8
|
exports.normalizeCreateBody = normalizeCreateBody;
|
|
8
9
|
exports.parseCommitmentConcurrencyLimit = parseCommitmentConcurrencyLimit;
|
|
9
10
|
exports.commitmentConcurrencyEnvelopePatch = commitmentConcurrencyEnvelopePatch;
|
|
11
|
+
exports.describeEvidenceGate = describeEvidenceGate;
|
|
10
12
|
exports.describeCommitmentConcurrency = describeCommitmentConcurrency;
|
|
11
13
|
exports.parseIntervalToMs = parseIntervalToMs;
|
|
12
14
|
exports.normalizeWatchedSourceDeclarations = normalizeWatchedSourceDeclarations;
|
|
@@ -14,10 +16,14 @@ exports.buildWatchedSources = buildWatchedSources;
|
|
|
14
16
|
exports.describeEvidenceHealth = describeEvidenceHealth;
|
|
15
17
|
exports.describeIntervalMs = describeIntervalMs;
|
|
16
18
|
exports.buildSuccessCriteria = buildSuccessCriteria;
|
|
19
|
+
exports.pendingCadenceRequestLines = pendingCadenceRequestLines;
|
|
17
20
|
exports.cadenceLabel = cadenceLabel;
|
|
18
21
|
exports.renderCommitmentList = renderCommitmentList;
|
|
19
22
|
exports.extractStandstillFromCheck = extractStandstillFromCheck;
|
|
20
23
|
exports.describeExecutorBlocked = describeExecutorBlocked;
|
|
24
|
+
exports.describeOperation = describeOperation;
|
|
25
|
+
exports.describeOperationFix = describeOperationFix;
|
|
26
|
+
exports.describeStoppedBy = describeStoppedBy;
|
|
21
27
|
exports.withLiveAssessment = withLiveAssessment;
|
|
22
28
|
exports.renderCommitmentSummary = renderCommitmentSummary;
|
|
23
29
|
exports.describeWakeDecision = describeWakeDecision;
|
|
@@ -29,6 +35,7 @@ exports.formatCommitmentCycles = formatCommitmentCycles;
|
|
|
29
35
|
exports.formatCommitmentExplanation = formatCommitmentExplanation;
|
|
30
36
|
exports.renderListPageHint = renderListPageHint;
|
|
31
37
|
exports.describeObservationArtifacts = describeObservationArtifacts;
|
|
38
|
+
exports.observationLegend = observationLegend;
|
|
32
39
|
exports.renderCommitmentObservations = renderCommitmentObservations;
|
|
33
40
|
exports.renderCommitmentHypotheses = renderCommitmentHypotheses;
|
|
34
41
|
exports.renderCommitmentLineage = renderCommitmentLineage;
|
|
@@ -100,6 +107,21 @@ function permissionLines(permissions) {
|
|
|
100
107
|
}
|
|
101
108
|
return lines;
|
|
102
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* The lines `show` prints for a stored schedule setting the engine cannot honour
|
|
112
|
+
* (OSK-15452): the cadence line above them reads as honoured while the daily times
|
|
113
|
+
* run on UTC. The sentence and the remedy are the server's; this only lays them out.
|
|
114
|
+
* An older server reports none, which prints nothing rather than a guess.
|
|
115
|
+
*/
|
|
116
|
+
function scheduleIssueLines(issues) {
|
|
117
|
+
const lines = [];
|
|
118
|
+
for (const issue of issues ?? []) {
|
|
119
|
+
lines.push(`Warning: ${issue.message}`);
|
|
120
|
+
if (issue.fix)
|
|
121
|
+
lines.push(` Fix: ${issue.fix}`);
|
|
122
|
+
}
|
|
123
|
+
return lines;
|
|
124
|
+
}
|
|
103
125
|
/**
|
|
104
126
|
* One line naming fields that are still in the JSON but decide nothing any
|
|
105
127
|
* more. Deprecation, not removal: `--json` keeps them for scripts.
|
|
@@ -449,6 +471,17 @@ function commitmentConcurrencyEnvelopePatch({ rawValue, reason, autonomous = pro
|
|
|
449
471
|
...(normalizedReason ? { maxConcurrentRunsReason: normalizedReason } : {}),
|
|
450
472
|
};
|
|
451
473
|
}
|
|
474
|
+
/**
|
|
475
|
+
* What `policy.evidenceGate.requireMaterialEvidenceForAutonomousRun` does to this Commitment's
|
|
476
|
+
* scheduled runs, in a line `show` can print (OSK-15417). It existed only in `show --json`: nothing
|
|
477
|
+
* in help, docs or any text view said whether a run on an unchanged day would be held back.
|
|
478
|
+
*/
|
|
479
|
+
function describeEvidenceGate(commitment) {
|
|
480
|
+
const gate = commitment.policy?.evidenceGate;
|
|
481
|
+
return gate?.requireMaterialEvidenceForAutonomousRun === true
|
|
482
|
+
? 'an autonomous run is held back unless the check found material evidence (a scheduled run on an unchanged day does not start)'
|
|
483
|
+
: 'off — an autonomous run is not held back for lack of material evidence';
|
|
484
|
+
}
|
|
452
485
|
function describeCommitmentConcurrency(commitment) {
|
|
453
486
|
const envelope = commitment.policy?.executionBudget;
|
|
454
487
|
const limit = envelope?.maxConcurrentRuns;
|
|
@@ -657,6 +690,12 @@ function describeEvidenceHealth(health, criteriaCount) {
|
|
|
657
690
|
if (state === 'degraded_observability') {
|
|
658
691
|
return `degraded_observability${detail} — ${describeDegradedObservability(reasons)}`;
|
|
659
692
|
}
|
|
693
|
+
if (state === 'awaiting_first_evidence') {
|
|
694
|
+
// Not stale and not degraded: nothing has been reported yet. The server
|
|
695
|
+
// only says this while the first run has neither reported nor finished;
|
|
696
|
+
// after that, seeing nothing is `degraded_observability` again (OSK-16211).
|
|
697
|
+
return `awaiting_first_evidence${detail} — no evidence yet: the first run is still collecting it`;
|
|
698
|
+
}
|
|
660
699
|
return `${state}${detail}`;
|
|
661
700
|
}
|
|
662
701
|
/**
|
|
@@ -680,6 +719,9 @@ function describeDegradedObservability(reasons) {
|
|
|
680
719
|
if (has('evidence_stale')) {
|
|
681
720
|
return "the freshest evidence is older than this commitment's freshness expectation";
|
|
682
721
|
}
|
|
722
|
+
if (has('evidence_awaiting_first_report')) {
|
|
723
|
+
return 'no evidence yet: the first run has not reported';
|
|
724
|
+
}
|
|
683
725
|
return 'the last check could not see everything it needs to judge this commitment';
|
|
684
726
|
}
|
|
685
727
|
/** Render an interval back as a compact human string. */
|
|
@@ -745,6 +787,29 @@ function buildSuccessCriteria(labels, opts = {}) {
|
|
|
745
787
|
...(evaluator === 'source_state' && source ? { config: { source } } : {}),
|
|
746
788
|
}));
|
|
747
789
|
}
|
|
790
|
+
/**
|
|
791
|
+
* The cadence changes an Agent has proposed and nobody has decided. `cadence-requests decide` needs a request
|
|
792
|
+
* id, and `show` listed none, so a person could not find the id to pass (OSK-15422).
|
|
793
|
+
*/
|
|
794
|
+
function pendingCadenceRequestLines(commitment) {
|
|
795
|
+
const state = commitment?.state;
|
|
796
|
+
const requests = Array.isArray(state?.pendingCadenceRequests) ? state.pendingCadenceRequests : [];
|
|
797
|
+
const pending = requests.filter((item) => !!item &&
|
|
798
|
+
typeof item === 'object' &&
|
|
799
|
+
item.kind === 'cadence_change' &&
|
|
800
|
+
item.status === 'pending');
|
|
801
|
+
if (!pending.length)
|
|
802
|
+
return [];
|
|
803
|
+
return [
|
|
804
|
+
'Pending cadence requests (decide with `commitments cadence-requests decide <id> <request-id> --approve|--reject`):',
|
|
805
|
+
...pending.map((item) => {
|
|
806
|
+
const from = item.basis?.intervalMs;
|
|
807
|
+
const to = item.requestedIntervalMs;
|
|
808
|
+
const reason = typeof item.reason === 'string' && item.reason ? ` — ${item.reason}` : '';
|
|
809
|
+
return ` ${String(item.id)} ${describeIntervalMs(from)} → ${describeIntervalMs(to)}${reason}`;
|
|
810
|
+
}),
|
|
811
|
+
];
|
|
812
|
+
}
|
|
748
813
|
function cadenceLabel(commitment) {
|
|
749
814
|
const cadence = commitment.cadence;
|
|
750
815
|
if (!cadence || !cadence.mode || cadence.mode === 'manual')
|
|
@@ -859,6 +924,71 @@ function describeExecutorBlocked(commitment) {
|
|
|
859
924
|
: reason || 'blocked';
|
|
860
925
|
return `${label}${until ? ` until ${until}` : ''}`;
|
|
861
926
|
}
|
|
927
|
+
/**
|
|
928
|
+
* The Operation line's wording for a Commitment that is not running because of its lifecycle.
|
|
929
|
+
*
|
|
930
|
+
* The assessment reports `waiting` with reason `lifecycle_<status>` for a Commitment a person
|
|
931
|
+
* paused, and `show` printed that verbatim ("waiting — lifecycle_paused"): the word "waiting" and a
|
|
932
|
+
* raw code, indistinguishable from one waiting on a machine or an approval (OSK-15423). Any other
|
|
933
|
+
* operation and reason is printed as it always was.
|
|
934
|
+
*/
|
|
935
|
+
function describeOperation(operation, reason) {
|
|
936
|
+
const lifecycle = typeof reason === 'string' ? /^lifecycle_([a-z_]+)$/.exec(reason) : null;
|
|
937
|
+
if (!lifecycle)
|
|
938
|
+
return `${operation ?? 'blocked'}${reason ? ` — ${reason}` : ''}`;
|
|
939
|
+
const status = lifecycle[1];
|
|
940
|
+
const meaning = {
|
|
941
|
+
paused: 'paused, it will not run until it is resumed',
|
|
942
|
+
draft: 'a draft, it will not run until it is activated',
|
|
943
|
+
archived: 'archived, it will not run',
|
|
944
|
+
completed: 'completed, it will not run',
|
|
945
|
+
cancelled: 'cancelled, it will not run',
|
|
946
|
+
};
|
|
947
|
+
return `not running — ${meaning[status] ?? status}`;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* What to do about an operation that is not running, from the operating state's
|
|
951
|
+
* first actionable blocker (`withLiveAssessment` carries it onto the view).
|
|
952
|
+
*
|
|
953
|
+
* `show` printed `Operation: blocked — typed_external_blocker` and nothing else,
|
|
954
|
+
* so the one line a reader acts on was a code; the remediation the server
|
|
955
|
+
* already composed sat only in `--json` and `doctor` (OSK-16212). Nothing is
|
|
956
|
+
* printed while the operation is ready or working, where a fix would contradict
|
|
957
|
+
* the line above it.
|
|
958
|
+
*/
|
|
959
|
+
function describeOperationFix(commitment, operation) {
|
|
960
|
+
if (operation === 'ready' || operation === 'working')
|
|
961
|
+
return null;
|
|
962
|
+
const blocker = commitment.firstActionableBlocker;
|
|
963
|
+
const remediation = blocker && typeof blocker.remediation === 'string' ? blocker.remediation.trim() : '';
|
|
964
|
+
if (!remediation)
|
|
965
|
+
return null;
|
|
966
|
+
const who = typeof blocker?.ownerLabel === 'string' && blocker.ownerLabel
|
|
967
|
+
? blocker.ownerLabel
|
|
968
|
+
: typeof blocker?.owner === 'string' && blocker.owner
|
|
969
|
+
? blocker.owner
|
|
970
|
+
: '';
|
|
971
|
+
return `${remediation}${who ? ` (${who})` : ''}`;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Who stopped a Commitment, and when and why, in one line. The server stores it as `stoppedBy`;
|
|
975
|
+
* `show` printed neither (OSK-15423), so a Commitment you paused did not say you had.
|
|
976
|
+
*/
|
|
977
|
+
function describeStoppedBy(commitment) {
|
|
978
|
+
const stoppedBy = commitment.stoppedBy;
|
|
979
|
+
if (!stoppedBy || typeof stoppedBy !== 'object')
|
|
980
|
+
return null;
|
|
981
|
+
const who = stoppedBy.kind === 'user'
|
|
982
|
+
? 'a person'
|
|
983
|
+
: stoppedBy.kind === 'agent'
|
|
984
|
+
? 'the Agent'
|
|
985
|
+
: stoppedBy.kind
|
|
986
|
+
? 'the platform'
|
|
987
|
+
: 'someone';
|
|
988
|
+
const status = stoppedBy.status || commitment.status || 'stopped';
|
|
989
|
+
return (`${status} by ${who}${stoppedBy.at ? ` on ${stoppedBy.at}` : ''}` +
|
|
990
|
+
`${stoppedBy.reason ? `: ${stoppedBy.reason}` : ''}`);
|
|
991
|
+
}
|
|
862
992
|
/**
|
|
863
993
|
* The commitment `show` should render: its stored contract, with the
|
|
864
994
|
* assessment replaced by the one the operating state recomputed for this read
|
|
@@ -869,9 +999,30 @@ function describeExecutorBlocked(commitment) {
|
|
|
869
999
|
*/
|
|
870
1000
|
function withLiveAssessment(commitment, operatingState) {
|
|
871
1001
|
const live = operatingState?.assessment;
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1002
|
+
// The remediation for a blocked operation lives on the operating state's
|
|
1003
|
+
// first actionable blocker; the header printed only the code (OSK-16212).
|
|
1004
|
+
const blocker = operatingState?.firstActionableBlocker &&
|
|
1005
|
+
typeof operatingState.firstActionableBlocker === 'object'
|
|
1006
|
+
? { firstActionableBlocker: operatingState.firstActionableBlocker }
|
|
1007
|
+
: {};
|
|
1008
|
+
// The operating state also knows the executor's machine is stopped (`waiting_for_machine`),
|
|
1009
|
+
// which the stored contract never carries; `show` printed neither (OSK-15423).
|
|
1010
|
+
const executorBlocked = !commitment.executorBlocked &&
|
|
1011
|
+
operatingState?.executorBlocked &&
|
|
1012
|
+
typeof operatingState.executorBlocked === 'object'
|
|
1013
|
+
? { executorBlocked: operatingState.executorBlocked }
|
|
1014
|
+
: {};
|
|
1015
|
+
if (!live || typeof live !== 'object') {
|
|
1016
|
+
return Object.keys(executorBlocked).length + Object.keys(blocker).length > 0
|
|
1017
|
+
? { ...commitment, ...executorBlocked, ...blocker }
|
|
1018
|
+
: commitment;
|
|
1019
|
+
}
|
|
1020
|
+
return {
|
|
1021
|
+
...commitment,
|
|
1022
|
+
...executorBlocked,
|
|
1023
|
+
...blocker,
|
|
1024
|
+
assessment: live,
|
|
1025
|
+
};
|
|
875
1026
|
}
|
|
876
1027
|
function renderCommitmentSummary(commitment, log,
|
|
877
1028
|
/**
|
|
@@ -879,19 +1030,34 @@ function renderCommitmentSummary(commitment, log,
|
|
|
879
1030
|
* complete / detach / reports render this same summary and hold only the
|
|
880
1031
|
* contract — they should not be made to fetch a check to print one line.
|
|
881
1032
|
*/
|
|
882
|
-
latestCheck
|
|
1033
|
+
latestCheck,
|
|
1034
|
+
/**
|
|
1035
|
+
* Stored schedule settings the engine cannot honour (OSK-15452), printed directly under the
|
|
1036
|
+
* cadence they affect rather than after the free-text summary at the bottom. Optional for the
|
|
1037
|
+
* same reason as `latestCheck`: only `show` reads them from the server.
|
|
1038
|
+
*/
|
|
1039
|
+
scheduleIssues) {
|
|
883
1040
|
log(`ID: ${commitment.id ?? '-'}`);
|
|
884
1041
|
log(`Title: ${commitment.title ?? '-'}`);
|
|
885
1042
|
log(`Kind: ${commitment.kind ?? 'achieve'}`);
|
|
886
1043
|
log(`Status: ${commitment.status ?? '-'}`);
|
|
1044
|
+
const stopped = describeStoppedBy(commitment);
|
|
1045
|
+
if (commitment.status !== 'active' && stopped)
|
|
1046
|
+
log(`Stopped: ${stopped}`);
|
|
887
1047
|
log(`Agent: ${commitment.agentId ?? '-'}`);
|
|
888
1048
|
// A commitment whose executor cannot run anywhere should say so on the
|
|
889
1049
|
// header, not as a silent skip series — "executor cannot run until <date> —
|
|
890
1050
|
// reassign or add a fallback target" (design doc P4).
|
|
891
1051
|
const executorBlocked = describeExecutorBlocked(commitment);
|
|
892
1052
|
if (executorBlocked) {
|
|
893
|
-
|
|
894
|
-
|
|
1053
|
+
// A stopped machine is started for the work: that is waiting, not blocked, and reassigning the
|
|
1054
|
+
// Commitment is not its remedy (OSK-15423).
|
|
1055
|
+
const machineWait = commitment.executorBlocked?.reason ===
|
|
1056
|
+
'waiting_for_machine';
|
|
1057
|
+
log(`Executor: ${machineWait ? 'waiting' : 'blocked'} — ${executorBlocked}`);
|
|
1058
|
+
if (!machineWait) {
|
|
1059
|
+
log(` Fix: reassign with \`skrr commitments handover ${commitment.id ?? '<id>'} --to <agent>\` or add a fallback target`);
|
|
1060
|
+
}
|
|
895
1061
|
}
|
|
896
1062
|
if (commitment.goalId)
|
|
897
1063
|
log(`Goal: ${commitment.goalId}`);
|
|
@@ -902,8 +1068,11 @@ latestCheck) {
|
|
|
902
1068
|
if (commitment.target?.deadlineAt)
|
|
903
1069
|
log(`Deadline: ${commitment.target.deadlineAt}`);
|
|
904
1070
|
log(`Cadence: ${cadenceLabel(commitment)}`);
|
|
1071
|
+
for (const line of scheduleIssueLines(scheduleIssues))
|
|
1072
|
+
log(line);
|
|
905
1073
|
log(`Delivery: ${(0, commitment_product_1.deliveryLabel)(commitment.delivery?.mode)}${commitment.delivery?.repo ? ` · ${commitment.delivery.repo}` : ''}${commitment.delivery?.baseBranch ? ` → ${commitment.delivery.baseBranch}` : ''}`);
|
|
906
1074
|
log(`Concurrency: ${describeCommitmentConcurrency(commitment)}`);
|
|
1075
|
+
log(`Evidence gate: ${describeEvidenceGate(commitment)}`);
|
|
907
1076
|
const criteria = commitment.target?.successCriteria || [];
|
|
908
1077
|
const evidenceHealth = latestCheck?.inputs?.evidenceHealth;
|
|
909
1078
|
const evidenceLine = describeEvidenceHealth(evidenceHealth, criteria.length);
|
|
@@ -912,7 +1081,10 @@ latestCheck) {
|
|
|
912
1081
|
: undefined;
|
|
913
1082
|
log(`Assessment: ${assessment?.label ?? 'Unknown'}`);
|
|
914
1083
|
log(`Evidence: ${assessment?.evidence ?? 'unavailable'}`);
|
|
915
|
-
log(`Operation: ${assessment?.operation
|
|
1084
|
+
log(`Operation: ${describeOperation(assessment?.operation, assessment?.operationReason)}`);
|
|
1085
|
+
const fix = describeOperationFix(commitment, assessment?.operation);
|
|
1086
|
+
if (fix)
|
|
1087
|
+
log(` Fix: ${fix}`);
|
|
916
1088
|
if (commitment.state?.lastCheckedAt)
|
|
917
1089
|
log(`Checked: ${commitment.state.lastCheckedAt}`);
|
|
918
1090
|
if (typeof commitment.state?.consecutiveUnresolvedChecks === 'number') {
|
|
@@ -1553,6 +1725,32 @@ function describeObservationArtifacts(artifacts) {
|
|
|
1553
1725
|
.map((name) => `${counts.get(name)} ${name}`)
|
|
1554
1726
|
.join(', ');
|
|
1555
1727
|
}
|
|
1728
|
+
/** The VALUE column's width in `renderCommitmentObservations`, which `observationLegend` checks against. */
|
|
1729
|
+
const OBSERVATION_VALUE_WIDTH = 38;
|
|
1730
|
+
/**
|
|
1731
|
+
* What the observations table's terse columns mean, for the rows actually on screen (OSK-15432).
|
|
1732
|
+
*
|
|
1733
|
+
* The table shortens VALUE to fit with `…`, so a reading cut by the screen looked the same as one
|
|
1734
|
+
* the platform truncated; `undeclared` / `DECLARED no` and CLAMPED were defined nowhere. Only the
|
|
1735
|
+
* terms present in THESE rows are explained, so an ordinary table adds nothing.
|
|
1736
|
+
*/
|
|
1737
|
+
function observationLegend(data) {
|
|
1738
|
+
const lines = [];
|
|
1739
|
+
const shortened = data.some((item) => (0, format_1.formatCell)(firstValue(item, ['value', 'observedValue', 'latestValue'])).length >
|
|
1740
|
+
OBSERVATION_VALUE_WIDTH);
|
|
1741
|
+
if (shortened) {
|
|
1742
|
+
lines.push('VALUE is shortened to fit this table (…); the stored value is complete in --json.');
|
|
1743
|
+
}
|
|
1744
|
+
if (data.some((item) => item.declared === false)) {
|
|
1745
|
+
lines.push('DECLARED no / REASON undeclared: the key is not declared on this Commitment’s agent_run ' +
|
|
1746
|
+
'source, so the reading is recorded but can never open a loop.');
|
|
1747
|
+
}
|
|
1748
|
+
if (data.some((item) => item.observedAtClamped === true)) {
|
|
1749
|
+
lines.push('CLAMPED yes: the observed time the run claimed was moved into the accepted window; ' +
|
|
1750
|
+
'CLAIMED shows what it said.');
|
|
1751
|
+
}
|
|
1752
|
+
return lines;
|
|
1753
|
+
}
|
|
1556
1754
|
function renderCommitmentObservations(response, log, options = {}) {
|
|
1557
1755
|
const data = records(response.data);
|
|
1558
1756
|
renderRows(data.map((item) => ({
|
|
@@ -1590,7 +1788,7 @@ function renderCommitmentObservations(response, log, options = {}) {
|
|
|
1590
1788
|
{ key: 'model', header: 'MODEL', maxWidth: 40 },
|
|
1591
1789
|
{ key: 'modelSource', header: 'MODEL SOURCE', maxWidth: 18 },
|
|
1592
1790
|
{ key: 'key', header: 'KEY', maxWidth: 30 },
|
|
1593
|
-
{ key: 'value', header: 'VALUE', maxWidth:
|
|
1791
|
+
{ key: 'value', header: 'VALUE', maxWidth: OBSERVATION_VALUE_WIDTH },
|
|
1594
1792
|
{ key: 'changed', header: 'CHANGED', maxWidth: 8 },
|
|
1595
1793
|
{ key: 'reason', header: 'REASON', maxWidth: 20 },
|
|
1596
1794
|
{ key: 'reference', header: 'REFERENCE', maxWidth: 80 },
|
|
@@ -1599,6 +1797,8 @@ function renderCommitmentObservations(response, log, options = {}) {
|
|
|
1599
1797
|
{ key: 'confidence', header: 'CONF', maxWidth: 6 },
|
|
1600
1798
|
{ key: 'artifacts', header: 'ARTIFACTS', maxWidth: 36 },
|
|
1601
1799
|
], options.emptyMessage || 'No observations recorded.', log);
|
|
1800
|
+
for (const line of observationLegend(data))
|
|
1801
|
+
log(line);
|
|
1602
1802
|
}
|
|
1603
1803
|
/** Human view for causal hypotheses. */
|
|
1604
1804
|
function renderCommitmentHypotheses(response, log, options = {}) {
|
|
@@ -50,6 +50,7 @@ const fs = __importStar(require("node:fs"));
|
|
|
50
50
|
const os = __importStar(require("node:os"));
|
|
51
51
|
const path = __importStar(require("node:path"));
|
|
52
52
|
const auth_core_1 = require("@skrr-ai/auth-core");
|
|
53
|
+
const cli_handoff_wire_1 = require("@skrr-ai/auth-core/cli-handoff-wire");
|
|
53
54
|
const loopback_http_1 = require("@skrr-ai/auth-core/loopback-http");
|
|
54
55
|
function isDelegatedCliSession(env = process.env) {
|
|
55
56
|
return Boolean(env.OVERSKY_AUTONOMOUS_SESSION === '1' &&
|
|
@@ -237,9 +238,17 @@ class DelegatedCliUnavailableError extends Error {
|
|
|
237
238
|
this.upstreamCode = fields.upstreamCode;
|
|
238
239
|
this.upstreamReason = fields.upstreamReason;
|
|
239
240
|
this.retryAt = fields.retryAt;
|
|
241
|
+
// OSK-16216 — an older daemon names no remedy. The fallback is the SAME
|
|
242
|
+
// shared sentence a current daemon sends, with nothing filled in: this
|
|
243
|
+
// process cannot tell whether the desktop app or a `skrrd install` runs
|
|
244
|
+
// the daemon, and `skrr daemon login` alone is wrong for the first.
|
|
240
245
|
this.remedy =
|
|
241
246
|
fields.remedy ??
|
|
242
|
-
(fields.retryable
|
|
247
|
+
(fields.retryable
|
|
248
|
+
? undefined
|
|
249
|
+
: (0, cli_handoff_wire_1.daemonSignInRemedy)({
|
|
250
|
+
repinsDeviceKey: fields.upstreamCode === 'DEVICE_PROOF_PIN_MISMATCH',
|
|
251
|
+
}));
|
|
243
252
|
}
|
|
244
253
|
}
|
|
245
254
|
exports.DelegatedCliUnavailableError = DelegatedCliUnavailableError;
|
|
@@ -321,7 +330,9 @@ async function fetchDelegatedCliCredential(env = process.env, options = {}) {
|
|
|
321
330
|
throw new DelegatedCliUnavailableError(typeof body?.message === 'string' && body.message
|
|
322
331
|
? `the daemon's delegated CLI broker could not mint a credential: ${body.message}`
|
|
323
332
|
: `the skrr server refused the daemon's request for this session's credential (${named}).`, {
|
|
324
|
-
...(typeof body?.upstreamStatus === 'number'
|
|
333
|
+
...(typeof body?.upstreamStatus === 'number'
|
|
334
|
+
? { upstreamStatus: body.upstreamStatus }
|
|
335
|
+
: {}),
|
|
325
336
|
...(typeof body?.upstreamCode === 'string' ? { upstreamCode: body.upstreamCode } : {}),
|
|
326
337
|
...(typeof body?.upstreamReason === 'string'
|
|
327
338
|
? { upstreamReason: body.upstreamReason }
|
package/dist/lib/followups.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { withQuery } from './triggers';
|
|
2
2
|
type Query = Parameters<typeof withQuery>[1];
|
|
3
3
|
type Json = Record<string, unknown>;
|
|
4
|
+
/**
|
|
5
|
+
* Check a `--in` / `--at` value against the bound the server enforces, before the round trip, and
|
|
6
|
+
* name the FLAG the user typed. The server's message names its own field (`in`, `once.runAt`) and,
|
|
7
|
+
* for a duration, used to give the bound in raw seconds (OSK-15415). Null when it is acceptable.
|
|
8
|
+
*/
|
|
9
|
+
export declare function whenFlagProblem(flag: 'in' | 'at', value: string | undefined): string | null;
|
|
4
10
|
/**
|
|
5
11
|
* The line a settle verb prints. A follow-up that had already ended is said to
|
|
6
12
|
* have ended, with the terminal it actually has, rather than reported as the
|
package/dist/lib/followups.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.followupsApi = exports.FOLLOWUP_SUBJECT_FLAG_HELP = exports.FOLLOWUP_SUBJECT_EXTERNAL_KEY_RE = exports.WATCH_WAIT_KINDS = exports.FOLLOWUP_RESOLVE_OUTCOMES = exports.FOLLOWUP_STATES = exports.FOLLOWUP_KINDS = exports.followUpIdArg = void 0;
|
|
4
|
+
exports.whenFlagProblem = whenFlagProblem;
|
|
4
5
|
exports.settledLine = settledLine;
|
|
5
6
|
exports.recipeWatchBoundsRefusal = recipeWatchBoundsRefusal;
|
|
6
7
|
exports.resolveFollowUpAgent = resolveFollowUpAgent;
|
|
@@ -28,6 +29,17 @@ const triggers_1 = require("./triggers");
|
|
|
28
29
|
const agent_resolver_1 = require("./agent-resolver");
|
|
29
30
|
const MOUNT = '/api/followups';
|
|
30
31
|
const base = (id) => `${MOUNT}/${encodeURIComponent(id)}`;
|
|
32
|
+
/**
|
|
33
|
+
* Check a `--in` / `--at` value against the bound the server enforces, before the round trip, and
|
|
34
|
+
* name the FLAG the user typed. The server's message names its own field (`in`, `once.runAt`) and,
|
|
35
|
+
* for a duration, used to give the bound in raw seconds (OSK-15415). Null when it is acceptable.
|
|
36
|
+
*/
|
|
37
|
+
function whenFlagProblem(flag, value) {
|
|
38
|
+
if (value === undefined)
|
|
39
|
+
return null;
|
|
40
|
+
const refusal = (0, data_provider_1.validateFollowUpWhen)(value, `--${flag}`);
|
|
41
|
+
return refusal ? refusal.message : null;
|
|
42
|
+
}
|
|
31
43
|
/**
|
|
32
44
|
* The line a settle verb prints. A follow-up that had already ended is said to
|
|
33
45
|
* have ended, with the terminal it actually has, rather than reported as the
|
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
* join them with a paragraph break, and non-text parts (tool calls, images,
|
|
12
12
|
* think blocks) are skipped the same way the `--json` response omits them.
|
|
13
13
|
*
|
|
14
|
+
* A widget part (CSS, HTML and JavaScript an app draws in a frame) is a block of
|
|
15
|
+
* its fallback sentence: a terminal cannot draw it, the markup is never printed,
|
|
16
|
+
* and skipping it like the other non-text parts dropped the sentence from every
|
|
17
|
+
* message that also had text. A cut-off widget contributes nothing, as it does in
|
|
18
|
+
* the stored `text`.
|
|
19
|
+
*
|
|
14
20
|
* Falls back to the flattened `text` string when a message carries no
|
|
15
21
|
* structured content — user messages and historical rows never have it.
|
|
16
22
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.messageTextBlocks = messageTextBlocks;
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
4
5
|
/**
|
|
5
6
|
* Flatten a conversation message's assistant `content[]` parts into readable
|
|
6
7
|
* display blocks.
|
|
@@ -14,6 +15,12 @@ exports.messageTextBlocks = messageTextBlocks;
|
|
|
14
15
|
* join them with a paragraph break, and non-text parts (tool calls, images,
|
|
15
16
|
* think blocks) are skipped the same way the `--json` response omits them.
|
|
16
17
|
*
|
|
18
|
+
* A widget part (CSS, HTML and JavaScript an app draws in a frame) is a block of
|
|
19
|
+
* its fallback sentence: a terminal cannot draw it, the markup is never printed,
|
|
20
|
+
* and skipping it like the other non-text parts dropped the sentence from every
|
|
21
|
+
* message that also had text. A cut-off widget contributes nothing, as it does in
|
|
22
|
+
* the stored `text`.
|
|
23
|
+
*
|
|
17
24
|
* Falls back to the flattened `text` string when a message carries no
|
|
18
25
|
* structured content — user messages and historical rows never have it.
|
|
19
26
|
*/
|
|
@@ -43,6 +50,9 @@ function textOfPart(part) {
|
|
|
43
50
|
return '';
|
|
44
51
|
}
|
|
45
52
|
const p = part;
|
|
53
|
+
if (p.type === data_provider_1.UI_WIDGET_PART_TYPE) {
|
|
54
|
+
return widgetFallback(p[data_provider_1.UI_WIDGET_PART_TYPE]);
|
|
55
|
+
}
|
|
46
56
|
if (typeof p.type === 'string' && p.type !== 'text') {
|
|
47
57
|
return '';
|
|
48
58
|
}
|
|
@@ -58,3 +68,17 @@ function textOfPart(part) {
|
|
|
58
68
|
}
|
|
59
69
|
return '';
|
|
60
70
|
}
|
|
71
|
+
/** The sentence that stands for a complete widget part, or `''` (never any of its markup). */
|
|
72
|
+
function widgetFallback(widget) {
|
|
73
|
+
if (!widget || typeof widget !== 'object') {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
const w = widget;
|
|
77
|
+
if (w.complete === false) {
|
|
78
|
+
return '';
|
|
79
|
+
}
|
|
80
|
+
return (0, data_provider_1.uiWidgetFallbackText)({
|
|
81
|
+
title: typeof w.title === 'string' ? w.title : undefined,
|
|
82
|
+
fallbackText: typeof w.fallbackText === 'string' ? w.fallbackText : undefined,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The operating plan's planning loops, named (OSK-15416). The text view printed only
|
|
3
|
+
* "15 existing planning/reflection loops; 0 scheduled work blocks": a count that grew as follow-ups
|
|
4
|
+
* were armed and did not drop when they were cancelled (a cancelled loop stays in the list, disabled),
|
|
5
|
+
* and never said which wake was due when. The server already projects `enabled`, `status` and
|
|
6
|
+
* `nextAt` for each loop; only --json showed them.
|
|
7
|
+
*/
|
|
8
|
+
export declare function planningLoopLines(loops: unknown, todoCount: number): string[];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The operating plan's planning loops, named (OSK-15416). The text view printed only
|
|
4
|
+
* "15 existing planning/reflection loops; 0 scheduled work blocks": a count that grew as follow-ups
|
|
5
|
+
* were armed and did not drop when they were cancelled (a cancelled loop stays in the list, disabled),
|
|
6
|
+
* and never said which wake was due when. The server already projects `enabled`, `status` and
|
|
7
|
+
* `nextAt` for each loop; only --json showed them.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.planningLoopLines = planningLoopLines;
|
|
11
|
+
const SHOWN = 10;
|
|
12
|
+
function planningLoopLines(loops, todoCount) {
|
|
13
|
+
const all = (Array.isArray(loops) ? loops : []);
|
|
14
|
+
const armed = all.filter((loop) => loop.enabled !== false);
|
|
15
|
+
const disabled = all.length - armed.length;
|
|
16
|
+
const lines = [
|
|
17
|
+
'',
|
|
18
|
+
`${armed.length} armed planning/reflection loop${armed.length === 1 ? '' : 's'}` +
|
|
19
|
+
`${disabled > 0 ? ` (${disabled} more disabled or cancelled, not counted)` : ''}; ` +
|
|
20
|
+
`${todoCount} scheduled work block${todoCount === 1 ? '' : 's'}.`,
|
|
21
|
+
];
|
|
22
|
+
// Soonest first; a loop with no due time (an event wait, unknown) after the dated ones.
|
|
23
|
+
const ordered = [...armed].sort((a, b) => (a.nextAt || '9999').localeCompare(b.nextAt || '9999'));
|
|
24
|
+
for (const loop of ordered.slice(0, SHOWN)) {
|
|
25
|
+
lines.push(` ${loop.nextAt ?? '(no due time)'} · ${loop.label ?? 'Scheduled work'} · ` +
|
|
26
|
+
`${loop.kind ?? '-'} · ${loop.status ?? '-'} · ${loop.id ?? '-'}`);
|
|
27
|
+
}
|
|
28
|
+
if (ordered.length > SHOWN) {
|
|
29
|
+
lines.push(` … and ${ordered.length - SHOWN} more armed (all of them in --json).`);
|
|
30
|
+
}
|
|
31
|
+
return lines;
|
|
32
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain-words sentences for two preflight gates whose verdicts used to be a bare `warn` (OSK-15425).
|
|
3
|
+
*
|
|
4
|
+
* "When is silence a problem?" printed only that freshness is advisory, with the reason
|
|
5
|
+
* (`evidence_unconfigured`) left in --json, so a reader could not tell whether silence would ever be
|
|
6
|
+
* flagged. "Where does its access come from?" printed no sentence at all. A credential whose expiry
|
|
7
|
+
* had passed read `stale`.
|
|
8
|
+
*/
|
|
9
|
+
type GateLike = {
|
|
10
|
+
id?: string;
|
|
11
|
+
verdict?: string;
|
|
12
|
+
reason?: string | null;
|
|
13
|
+
detail?: Record<string, unknown> | null;
|
|
14
|
+
};
|
|
15
|
+
/** The refusal as `code: words`, or the bare code when it is not one this knows. */
|
|
16
|
+
export declare function sourceRefusalWords(code: string): string;
|
|
17
|
+
/** Extra lines under a gate, or none. The gate's own note is printed by the caller. */
|
|
18
|
+
export declare function gateExplanationLines(gate: GateLike): string[];
|
|
19
|
+
/**
|
|
20
|
+
* The status word for a capability. A credential whose expiry has passed is `expired`, not the
|
|
21
|
+
* storage word `stale`, which gave no sign that time had run out.
|
|
22
|
+
*/
|
|
23
|
+
export declare function capabilityStatusLabel(capability: {
|
|
24
|
+
status?: string;
|
|
25
|
+
expiresAt?: string | null;
|
|
26
|
+
}, now?: Date): string;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Plain-words sentences for two preflight gates whose verdicts used to be a bare `warn` (OSK-15425).
|
|
4
|
+
*
|
|
5
|
+
* "When is silence a problem?" printed only that freshness is advisory, with the reason
|
|
6
|
+
* (`evidence_unconfigured`) left in --json, so a reader could not tell whether silence would ever be
|
|
7
|
+
* flagged. "Where does its access come from?" printed no sentence at all. A credential whose expiry
|
|
8
|
+
* had passed read `stale`.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.sourceRefusalWords = sourceRefusalWords;
|
|
12
|
+
exports.gateExplanationLines = gateExplanationLines;
|
|
13
|
+
exports.capabilityStatusLabel = capabilityStatusLabel;
|
|
14
|
+
/** What a refused event-source code means, in words. An unknown code is printed as it is. */
|
|
15
|
+
const SOURCE_REFUSAL_WORDS = {
|
|
16
|
+
source_authorization_descriptor_invalid: 'authorization record incomplete (it names no id to check access against)',
|
|
17
|
+
source_binding_detached: 'no longer matches a source attached to this Commitment',
|
|
18
|
+
source_access_revoked: 'access refused',
|
|
19
|
+
source_access_unavailable: 'access could not be checked just now',
|
|
20
|
+
};
|
|
21
|
+
/** The refusal as `code: words`, or the bare code when it is not one this knows. */
|
|
22
|
+
function sourceRefusalWords(code) {
|
|
23
|
+
const words = SOURCE_REFUSAL_WORDS[code];
|
|
24
|
+
return words ? `${code}: ${words}` : code;
|
|
25
|
+
}
|
|
26
|
+
function sourceRefusalLine(row) {
|
|
27
|
+
const id = row.refId
|
|
28
|
+
? `${row.selector ?? 'id'} ${row.refId}`
|
|
29
|
+
: `no ${row.selector ?? 'id'} given`;
|
|
30
|
+
const why = row.reason ? (SOURCE_REFUSAL_WORDS[row.reason] ?? row.reason) : 'access was refused';
|
|
31
|
+
return `Source ${row.source ?? 'unknown'} (${id}): ${why}.`;
|
|
32
|
+
}
|
|
33
|
+
/** Extra lines under a gate, or none. The gate's own note is printed by the caller. */
|
|
34
|
+
function gateExplanationLines(gate) {
|
|
35
|
+
if (gate.verdict === 'pass')
|
|
36
|
+
return [];
|
|
37
|
+
if (gate.id === 'freshness') {
|
|
38
|
+
switch (gate.reason) {
|
|
39
|
+
case 'evidence_unconfigured':
|
|
40
|
+
return [
|
|
41
|
+
'No freshness expectation is set, so nothing will flag this Commitment’s evidence as stale or silent.',
|
|
42
|
+
];
|
|
43
|
+
case 'evidence_stale': {
|
|
44
|
+
const criteria = Array.isArray(gate.detail?.criteria)
|
|
45
|
+
? gate.detail.criteria
|
|
46
|
+
: [];
|
|
47
|
+
const observed = criteria
|
|
48
|
+
.filter((item) => item.evidence === 'stale' && item.observedAt)
|
|
49
|
+
.map((item) => String(item.observedAt))
|
|
50
|
+
.sort();
|
|
51
|
+
return [
|
|
52
|
+
observed.length > 0
|
|
53
|
+
? `Its evidence is stale; the oldest reading is from ${observed[0]}.`
|
|
54
|
+
: 'Its evidence is stale.',
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
case 'evidence_partial':
|
|
58
|
+
return ['Only part of its evidence is current.'];
|
|
59
|
+
case 'evidence_unavailable':
|
|
60
|
+
return ['No evidence could be read just now, so freshness cannot be judged.'];
|
|
61
|
+
default:
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// The gate carries the sources it checked; its reason alone is a bare code naming none of them.
|
|
66
|
+
if (gate.id === 'source_access') {
|
|
67
|
+
const rows = Array.isArray(gate.detail?.sources) ? gate.detail.sources : [];
|
|
68
|
+
return rows.filter((row) => row.access && row.access !== 'authorized').map(sourceRefusalLine);
|
|
69
|
+
}
|
|
70
|
+
if (gate.id === 'access' && gate.reason === 'capability_access_unverified') {
|
|
71
|
+
return [
|
|
72
|
+
'No access beyond an identity check is verified as available yet (see “Where access comes from” below).',
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The status word for a capability. A credential whose expiry has passed is `expired`, not the
|
|
79
|
+
* storage word `stale`, which gave no sign that time had run out.
|
|
80
|
+
*/
|
|
81
|
+
function capabilityStatusLabel(capability, now = new Date()) {
|
|
82
|
+
const status = capability.status ?? 'unknown';
|
|
83
|
+
if (status === 'stale' && capability.expiresAt) {
|
|
84
|
+
const expires = Date.parse(capability.expiresAt);
|
|
85
|
+
if (!Number.isNaN(expires) && expires <= now.getTime())
|
|
86
|
+
return 'expired';
|
|
87
|
+
}
|
|
88
|
+
return status;
|
|
89
|
+
}
|
|
@@ -6,7 +6,7 @@ export declare abstract class ProposalDecisionCommand extends BaseCommand {
|
|
|
6
6
|
static decision: ProposalDecision;
|
|
7
7
|
static args: {
|
|
8
8
|
kind: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
9
|
-
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
9
|
+
id: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
10
10
|
};
|
|
11
11
|
static flags: {
|
|
12
12
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|