@skrr-ai/cli 0.1.128 → 0.1.130
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base-command.d.ts +12 -0
- package/dist/base-command.js +65 -0
- package/dist/commands/agenda/trigger/create.js +9 -1
- package/dist/commands/agenda/trigger/list.d.ts +2 -1
- package/dist/commands/agenda/trigger/list.js +55 -6
- package/dist/commands/agents/create.js +3 -1
- package/dist/commands/agents/reflect.d.ts +6 -0
- package/dist/commands/agents/reflect.js +45 -6
- package/dist/commands/agents/schedule-overview.d.ts +17 -0
- package/dist/commands/agents/schedule-overview.js +33 -9
- package/dist/commands/agents/triggers/policy.js +3 -1
- package/dist/commands/browser/persistent-profile.d.ts +22 -0
- package/dist/commands/browser/persistent-profile.js +46 -0
- package/dist/commands/commitments/create.js +3 -1
- package/dist/commands/commitments/effective-policy.js +6 -0
- package/dist/commands/commitments/explain.js +10 -1
- package/dist/commands/commitments/plan.d.ts +14 -0
- package/dist/commands/commitments/plan.js +32 -0
- package/dist/commands/commitments/preflight.d.ts +1 -1
- package/dist/commands/commitments/preflight.js +12 -4
- package/dist/commands/commitments/show.d.ts +9 -0
- package/dist/commands/commitments/show.js +14 -1
- package/dist/commands/compass/revisions.d.ts +6 -2
- package/dist/commands/compass/revisions.js +13 -5
- package/dist/commands/compass/tactic.d.ts +5 -0
- package/dist/commands/compass/tactic.js +14 -0
- package/dist/commands/followups/plans/create.js +1 -1
- package/dist/commands/harnesses/list.js +1 -1
- package/dist/commands/northstars/create.js +6 -1
- package/dist/commands/northstars/governance/configure.js +1 -1
- package/dist/commands/northstars/intents/decide.js +2 -3
- package/dist/commands/northstars/list.js +1 -1
- package/dist/commands/northstars/review-now.js +5 -0
- package/dist/commands/northstars/reviews.js +15 -3
- package/dist/commands/northstars/show.js +1 -1
- package/dist/commands/spaces/auto-executor/runs.d.ts +9 -1
- package/dist/commands/spaces/auto-executor/runs.js +18 -1
- package/dist/commands/spaces/heartbeats/create.js +7 -5
- package/dist/commands/spaces/heartbeats/status.js +10 -0
- package/dist/commands/spaces/heartbeats/update.js +5 -3
- package/dist/commands/store/artifacts/releases.d.ts +20 -0
- package/dist/commands/store/artifacts/releases.js +85 -0
- package/dist/commands/store/library.d.ts +14 -0
- package/dist/commands/store/library.js +71 -0
- package/dist/commands/tasks/archive.js +6 -0
- package/dist/commands/tasks/list.d.ts +48 -0
- package/dist/commands/tasks/list.js +141 -9
- package/dist/commands/tasks/pause-schedule.js +1 -1
- package/dist/commands/tasks/remind.js +23 -11
- package/dist/commands/tasks/resume-schedule.js +4 -1
- package/dist/commands/tasks/retry-schedule.js +6 -3
- package/dist/commands/tasks/schedule.js +35 -8
- package/dist/commands/tasks/self-schedule.d.ts +56 -0
- package/dist/commands/tasks/self-schedule.js +179 -21
- package/dist/commands/tasks/show.js +4 -0
- package/dist/lib/agent-trigger-listing.d.ts +26 -0
- package/dist/lib/agent-trigger-listing.js +63 -0
- package/dist/lib/agentic-stream.js +138 -41
- package/dist/lib/commitment-ledgers.js +3 -1
- package/dist/lib/commitments.d.ts +24 -0
- package/dist/lib/commitments.js +64 -4
- package/dist/lib/compasses.d.ts +2 -0
- package/dist/lib/iso-instant.d.ts +52 -0
- package/dist/lib/iso-instant.js +162 -0
- package/dist/lib/northstars.d.ts +87 -4
- package/dist/lib/northstars.js +226 -94
- package/dist/lib/proposals.js +6 -2
- package/dist/lib/reflection.d.ts +58 -0
- package/dist/lib/reflection.js +115 -6
- package/dist/lib/store-artifact-releases.d.ts +39 -0
- package/dist/lib/store-artifact-releases.js +12 -0
- package/dist/lib/store-library.d.ts +37 -0
- package/dist/lib/store-library.js +12 -0
- package/dist/lib/task-calendar.d.ts +19 -0
- package/dist/lib/task-calendar.js +72 -0
- package/dist/lib/task-operations.js +2 -0
- package/dist/lib/task-recurrence-outcome.js +3 -1
- package/dist/lib/task-recurrence-summary.d.ts +15 -0
- package/dist/lib/task-recurrence-summary.js +228 -0
- package/dist/lib/task-reminder.d.ts +28 -0
- package/dist/lib/task-reminder.js +50 -0
- package/dist/lib/task-schedule-notes.d.ts +46 -0
- package/dist/lib/task-schedule-notes.js +71 -0
- package/dist/lib/triggers.d.ts +31 -2
- package/dist/lib/triggers.js +38 -18
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonWire.js +11 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +9 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +22 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/executorStopReceipt.d.ts +123 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/executorStopReceipt.js +160 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +19 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonWire.js +11 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +9 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +28 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/executorStopReceipt.d.ts +123 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/executorStopReceipt.js +173 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +3 -1
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +6772 -6675
- package/oclif.manifest.json +282 -40
- package/package.json +1 -1
|
@@ -206,7 +206,10 @@ async function discardTypeAhead(input, windowMs = exports.TYPE_AHEAD_DISCARD_MS)
|
|
|
206
206
|
process.kill(process.pid, 'SIGINT');
|
|
207
207
|
}
|
|
208
208
|
const permissionGeneration = (event) => JSON.stringify([
|
|
209
|
-
event.sessionId,
|
|
209
|
+
event.sessionId,
|
|
210
|
+
event.toolUseId,
|
|
211
|
+
event.permissionBinding?.epoch,
|
|
212
|
+
event.permissionBinding?.requestId,
|
|
210
213
|
]);
|
|
211
214
|
const PERMISSION_DETAIL_MAX = 400;
|
|
212
215
|
/**
|
|
@@ -1523,7 +1526,8 @@ class AgenticStreamClient {
|
|
|
1523
1526
|
return; // a pure re-delivery — drop it whole
|
|
1524
1527
|
const scoped = { ...event, envelopes: fresh };
|
|
1525
1528
|
if (fresh.some((env) => env?.ev?.t === 'text' &&
|
|
1526
|
-
env.ev?.publicProjection ===
|
|
1529
|
+
env.ev?.publicProjection ===
|
|
1530
|
+
'lookback_answer_v1'))
|
|
1527
1531
|
this.publicProjection = 'lookback_answer_v1';
|
|
1528
1532
|
// Note what never arrived before folding this batch in (OSK-7631,
|
|
1529
1533
|
// OSK-13518). Checked against the FRESH set: a re-delivery carries old
|
|
@@ -1687,7 +1691,8 @@ class AgenticStreamClient {
|
|
|
1687
1691
|
socket.on('execution:aborted', (event) => {
|
|
1688
1692
|
if (!match(event))
|
|
1689
1693
|
return;
|
|
1690
|
-
if (typeof event.permissionEpoch === 'string' &&
|
|
1694
|
+
if (typeof event.permissionEpoch === 'string' &&
|
|
1695
|
+
this.permissionEpochs.has(this.options.sessionId) &&
|
|
1691
1696
|
this.permissionEpochs.get(this.options.sessionId) !== event.permissionEpoch)
|
|
1692
1697
|
return;
|
|
1693
1698
|
if (this.handover)
|
|
@@ -1698,7 +1703,8 @@ class AgenticStreamClient {
|
|
|
1698
1703
|
socket.on('execution:cancelled', (event) => {
|
|
1699
1704
|
if (!match(event))
|
|
1700
1705
|
return;
|
|
1701
|
-
if (typeof event.permissionEpoch === 'string' &&
|
|
1706
|
+
if (typeof event.permissionEpoch === 'string' &&
|
|
1707
|
+
this.permissionEpochs.has(this.options.sessionId) &&
|
|
1702
1708
|
this.permissionEpochs.get(this.options.sessionId) !== event.permissionEpoch)
|
|
1703
1709
|
return;
|
|
1704
1710
|
if (this.handover)
|
|
@@ -1731,7 +1737,10 @@ class AgenticStreamClient {
|
|
|
1731
1737
|
return;
|
|
1732
1738
|
if (event.toolUseId) {
|
|
1733
1739
|
this.openPermissionPrompts.get(event.toolUseId)?.abort();
|
|
1734
|
-
this.permissionGenerations.set(event.toolUseId, {
|
|
1740
|
+
this.permissionGenerations.set(event.toolUseId, {
|
|
1741
|
+
key: permissionGeneration(event),
|
|
1742
|
+
bound: !!event.permissionBinding,
|
|
1743
|
+
});
|
|
1735
1744
|
if (event.permissionBinding)
|
|
1736
1745
|
this.permissionEpochs.set(this.options.sessionId, event.permissionBinding.epoch);
|
|
1737
1746
|
}
|
|
@@ -1765,21 +1774,27 @@ class AgenticStreamClient {
|
|
|
1765
1774
|
// Runtime-authored application consent is exclusively an interactive app
|
|
1766
1775
|
// decision. A TTY or --approve-tools is not that authenticated human lane.
|
|
1767
1776
|
// Recognize malformed runtime asks too, so they never fall into generic grants.
|
|
1768
|
-
if (event.purpose === 'runtime-elicitation' ||
|
|
1777
|
+
if (event.purpose === 'runtime-elicitation' ||
|
|
1778
|
+
event.runtimeElicitation !== undefined ||
|
|
1779
|
+
event.toolName === 'ComputerUse') {
|
|
1769
1780
|
let message = 'This Computer Use request cannot be verified. Wait for a new request in the app.';
|
|
1770
1781
|
let requestValid = false;
|
|
1771
1782
|
try {
|
|
1772
1783
|
const runtime = event.runtimeElicitation;
|
|
1773
1784
|
(0, computer_use_1.assertComputerUseElicitationRequest)(runtime);
|
|
1774
|
-
if (event.purpose !== 'runtime-elicitation' ||
|
|
1775
|
-
event.
|
|
1785
|
+
if (event.purpose !== 'runtime-elicitation' ||
|
|
1786
|
+
event.toolName !== 'ComputerUse' ||
|
|
1787
|
+
event.toolUseId !== runtime.requestId ||
|
|
1788
|
+
runtime.context.sessionId !== this.options.sessionId ||
|
|
1776
1789
|
runtime.expiresAt <= Date.now())
|
|
1777
1790
|
throw new Error('Mismatched runtime permission');
|
|
1778
1791
|
const display = (text) => describePermissionTarget({ command: text }) ?? '';
|
|
1779
|
-
message = [
|
|
1792
|
+
message = [
|
|
1793
|
+
display(runtime.message),
|
|
1780
1794
|
`Choices in the app: ${runtime.options.map((option) => display(option.label)).join(' | ')}`,
|
|
1781
1795
|
`Expires: ${new Date(runtime.expiresAt).toISOString()}`,
|
|
1782
|
-
'Answer in the signed-in app. This CLI cannot grant or decline application access.'
|
|
1796
|
+
'Answer in the signed-in app. This CLI cannot grant or decline application access.',
|
|
1797
|
+
].join('\n');
|
|
1783
1798
|
requestValid = true;
|
|
1784
1799
|
}
|
|
1785
1800
|
catch {
|
|
@@ -1791,10 +1806,17 @@ class AgenticStreamClient {
|
|
|
1791
1806
|
if (approvalUrl)
|
|
1792
1807
|
message += `\nOpen ${approvalUrl} and choose Review pending decisions.`;
|
|
1793
1808
|
else
|
|
1794
|
-
message +=
|
|
1809
|
+
message +=
|
|
1810
|
+
'\nOpen this conversation in the signed-in app and choose Review pending decisions.';
|
|
1795
1811
|
if (event.toolUseId)
|
|
1796
1812
|
this.browserPermissionRequests.add(event.toolUseId);
|
|
1797
|
-
this.emit({
|
|
1813
|
+
this.emit({
|
|
1814
|
+
...event,
|
|
1815
|
+
type: 'permission.browser_required',
|
|
1816
|
+
requestValid,
|
|
1817
|
+
approvalUrl,
|
|
1818
|
+
message,
|
|
1819
|
+
});
|
|
1798
1820
|
return;
|
|
1799
1821
|
}
|
|
1800
1822
|
if (!this.options.approveTools || !this.socket) {
|
|
@@ -1803,7 +1825,10 @@ class AgenticStreamClient {
|
|
|
1803
1825
|
// response was admitted. Tool execution is not confirmed.", which read like an approval.
|
|
1804
1826
|
this.emit({ ...event, type: 'permission.denied', reason: 'noninteractive_or_not_enabled' });
|
|
1805
1827
|
if (event.toolUseId) {
|
|
1806
|
-
if (await this.sendBoundPermissionDecision(event, false, {
|
|
1828
|
+
if (await this.sendBoundPermissionDecision(event, false, {
|
|
1829
|
+
reason: 'CLI was not started with --approve-tools',
|
|
1830
|
+
automatic: true,
|
|
1831
|
+
}))
|
|
1807
1832
|
return;
|
|
1808
1833
|
this.socket?.emit('permission:deny', {
|
|
1809
1834
|
sessionId: this.options.sessionId,
|
|
@@ -1877,14 +1902,22 @@ class AgenticStreamClient {
|
|
|
1877
1902
|
this.emit({ ...event, type: approved ? 'permission.approved' : 'permission.denied' });
|
|
1878
1903
|
}
|
|
1879
1904
|
replayPermissionDecision(event) {
|
|
1880
|
-
if (!event.permissionBinding ||
|
|
1881
|
-
event.
|
|
1905
|
+
if (!event.permissionBinding ||
|
|
1906
|
+
event.decisionStatus === undefined ||
|
|
1907
|
+
event.purpose === 'task-command-once' ||
|
|
1908
|
+
event.purpose === 'runtime-elicitation' ||
|
|
1909
|
+
event.runtimeElicitation !== undefined ||
|
|
1910
|
+
event.toolName === 'ComputerUse')
|
|
1882
1911
|
return false;
|
|
1883
1912
|
const generation = permissionGeneration(event);
|
|
1884
1913
|
if (event.toolUseId) {
|
|
1885
1914
|
this.openPermissionPrompts.get(event.toolUseId)?.abort();
|
|
1886
1915
|
this.openPermissionPrompts.delete(event.toolUseId);
|
|
1887
|
-
this.permissionGenerations.set(event.toolUseId, {
|
|
1916
|
+
this.permissionGenerations.set(event.toolUseId, {
|
|
1917
|
+
key: generation,
|
|
1918
|
+
bound: true,
|
|
1919
|
+
readonly: true,
|
|
1920
|
+
});
|
|
1888
1921
|
this.permissionEpochs.set(this.options.sessionId, event.permissionBinding.epoch);
|
|
1889
1922
|
}
|
|
1890
1923
|
this.firstTimeSeen('permission', generation);
|
|
@@ -1892,11 +1925,18 @@ class AgenticStreamClient {
|
|
|
1892
1925
|
const status = event.decisionStatus === 'recorded' ? 'recorded' : 'delivery_unconfirmed';
|
|
1893
1926
|
if (this.firstTimeSeen('permission-receipt', `${generation}:${valid ? status : 'unknown'}`))
|
|
1894
1927
|
this.emit({
|
|
1895
|
-
...event,
|
|
1896
|
-
|
|
1928
|
+
...event,
|
|
1929
|
+
type: status === 'recorded'
|
|
1930
|
+
? 'permission.response_recorded'
|
|
1931
|
+
: 'permission.delivery_unconfirmed',
|
|
1932
|
+
status,
|
|
1933
|
+
previouslyRecorded: valid,
|
|
1934
|
+
executionConfirmed: false,
|
|
1935
|
+
readonly: true,
|
|
1897
1936
|
message: status === 'recorded'
|
|
1898
1937
|
? 'Response recorded. Waiting for execution to admit this response; no tool execution or continuing permission grant is confirmed.'
|
|
1899
|
-
: valid
|
|
1938
|
+
: valid
|
|
1939
|
+
? 'Response recorded; delivery unconfirmed. Check execution or Stop before starting a new turn.'
|
|
1900
1940
|
: 'Your response could not be confirmed. Check execution or Stop before starting a new turn.',
|
|
1901
1941
|
});
|
|
1902
1942
|
return true;
|
|
@@ -1906,7 +1946,9 @@ class AgenticStreamClient {
|
|
|
1906
1946
|
if (!event.permissionBinding)
|
|
1907
1947
|
return false;
|
|
1908
1948
|
const generation = permissionGeneration(event);
|
|
1909
|
-
const current = () => !this.closed &&
|
|
1949
|
+
const current = () => !this.closed &&
|
|
1950
|
+
!!event.toolUseId &&
|
|
1951
|
+
this.permissionGenerations.get(event.toolUseId)?.key === generation &&
|
|
1910
1952
|
!this.permissionGenerations.get(event.toolUseId)?.readonly;
|
|
1911
1953
|
if (!current())
|
|
1912
1954
|
return true;
|
|
@@ -1923,27 +1965,54 @@ class AgenticStreamClient {
|
|
|
1923
1965
|
const timer = setTimeout(() => finish({ type: 'permission.delivery_unconfirmed', message: unknown }), 10_000);
|
|
1924
1966
|
try {
|
|
1925
1967
|
if (!this.socket?.connected) {
|
|
1926
|
-
finish({
|
|
1968
|
+
finish({
|
|
1969
|
+
type: 'permission.response_refused',
|
|
1970
|
+
message: 'The connection is offline. No response was sent.',
|
|
1971
|
+
});
|
|
1927
1972
|
return;
|
|
1928
1973
|
}
|
|
1929
1974
|
this.socket.emit(approved ? 'permission:approve' : 'permission:deny', {
|
|
1930
|
-
sessionId: this.options.sessionId,
|
|
1931
|
-
|
|
1975
|
+
sessionId: this.options.sessionId,
|
|
1976
|
+
toolUseId: event.toolUseId,
|
|
1977
|
+
toolName: event.toolName,
|
|
1978
|
+
permissionBinding: event.permissionBinding,
|
|
1979
|
+
...extra,
|
|
1932
1980
|
}, (receipt) => {
|
|
1933
1981
|
const result = receipt?.result;
|
|
1934
|
-
if (receipt?.ok === false &&
|
|
1935
|
-
|
|
1982
|
+
if (receipt?.ok === false &&
|
|
1983
|
+
['invalid', 'unauthorized'].includes(receipt.error?.code || ''))
|
|
1984
|
+
finish({
|
|
1985
|
+
type: 'permission.response_refused',
|
|
1986
|
+
message: receipt.error?.message ||
|
|
1987
|
+
'The response was refused. No new permission was granted.',
|
|
1988
|
+
});
|
|
1936
1989
|
else if (receipt?.ok === false)
|
|
1937
1990
|
finish({ type: 'permission.delivery_unconfirmed', message: unknown });
|
|
1938
1991
|
else if (!result || result.toolUseId !== event.toolUseId)
|
|
1939
1992
|
finish({ type: 'permission.delivery_unconfirmed', message: unknown });
|
|
1940
1993
|
else if (result.status === 'closed')
|
|
1941
|
-
finish({
|
|
1994
|
+
finish({
|
|
1995
|
+
type: 'permission.response_refused',
|
|
1996
|
+
status: 'closed',
|
|
1997
|
+
message: 'This approval request is closed. No new permission was granted.',
|
|
1998
|
+
});
|
|
1942
1999
|
else if (result.status === 'delivery_unconfirmed')
|
|
1943
|
-
finish({
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
2000
|
+
finish({
|
|
2001
|
+
type: 'permission.delivery_unconfirmed',
|
|
2002
|
+
status: result.status,
|
|
2003
|
+
previouslyRecorded: result.previouslyRecorded === true,
|
|
2004
|
+
message: result.previouslyRecorded === true
|
|
2005
|
+
? 'Response recorded; delivery unconfirmed. Check execution or Stop before starting a new turn.'
|
|
2006
|
+
: unknown,
|
|
2007
|
+
});
|
|
2008
|
+
else if (receipt.ok === true &&
|
|
2009
|
+
result.accepted === true &&
|
|
2010
|
+
['recorded', 'admitted'].includes(result.status || ''))
|
|
2011
|
+
finish({
|
|
2012
|
+
type: 'permission.response_recorded',
|
|
2013
|
+
status: result.status,
|
|
2014
|
+
message: 'Response recorded. This does not confirm tool execution or a continuing permission grant.',
|
|
2015
|
+
});
|
|
1947
2016
|
else
|
|
1948
2017
|
finish({ type: 'permission.delivery_unconfirmed', message: unknown });
|
|
1949
2018
|
});
|
|
@@ -1953,7 +2022,12 @@ class AgenticStreamClient {
|
|
|
1953
2022
|
}
|
|
1954
2023
|
});
|
|
1955
2024
|
if (current())
|
|
1956
|
-
this.emit({
|
|
2025
|
+
this.emit({
|
|
2026
|
+
...event,
|
|
2027
|
+
...result,
|
|
2028
|
+
decision: approved ? 'allow' : 'deny',
|
|
2029
|
+
executionConfirmed: false,
|
|
2030
|
+
});
|
|
1957
2031
|
return true;
|
|
1958
2032
|
}
|
|
1959
2033
|
async handleQuestion(event) {
|
|
@@ -2021,7 +2095,8 @@ class AgenticStreamClient {
|
|
|
2021
2095
|
handlePermissionCancel(event) {
|
|
2022
2096
|
const toolUseId = typeof event.toolUseId === 'string' ? event.toolUseId : undefined;
|
|
2023
2097
|
const generation = toolUseId ? this.permissionGenerations.get(toolUseId) : undefined;
|
|
2024
|
-
if (generation &&
|
|
2098
|
+
if (generation &&
|
|
2099
|
+
(generation.bound || event.permissionBinding) &&
|
|
2025
2100
|
(!event.permissionBinding || generation.key !== permissionGeneration(event)))
|
|
2026
2101
|
return;
|
|
2027
2102
|
const open = toolUseId ? this.openPermissionPrompts.get(toolUseId) : undefined;
|
|
@@ -2059,8 +2134,17 @@ class AgenticStreamClient {
|
|
|
2059
2134
|
if (questionId)
|
|
2060
2135
|
this.openQuestionPrompts.delete(questionId);
|
|
2061
2136
|
open?.abort();
|
|
2137
|
+
// `answered` is the server telling every surface that SOMEONE answered. When
|
|
2138
|
+
// this client's own answer comes back as one there is nothing to report (it
|
|
2139
|
+
// already printed `question.answered`); when another surface answered while
|
|
2140
|
+
// this prompt was open, the prompt was just closed and that is worth saying.
|
|
2141
|
+
if (event.reason === 'answered' && !open)
|
|
2142
|
+
return;
|
|
2062
2143
|
this.emit({
|
|
2063
2144
|
...event,
|
|
2145
|
+
...(event.reason === 'answered' && !event.message
|
|
2146
|
+
? { message: 'This question was answered in another window.' }
|
|
2147
|
+
: {}),
|
|
2064
2148
|
type: 'question.cancelled',
|
|
2065
2149
|
promptClosed: Boolean(open),
|
|
2066
2150
|
});
|
|
@@ -2207,10 +2291,11 @@ class AgenticStreamClient {
|
|
|
2207
2291
|
// result, so there the line goes to stderr (OSK-13543).
|
|
2208
2292
|
if (!this.options.jsonl && this.renderHandoverStatus(event))
|
|
2209
2293
|
return;
|
|
2210
|
-
if (event.type === 'permission.browser_required' &&
|
|
2294
|
+
if (event.type === 'permission.browser_required' &&
|
|
2295
|
+
(this.options.quiet || !this.options.jsonl)) {
|
|
2211
2296
|
const output = this.options.quiet
|
|
2212
|
-
? this.options.statusOutput ?? process.stderr
|
|
2213
|
-
: this.options.output ?? process.stdout;
|
|
2297
|
+
? (this.options.statusOutput ?? process.stderr)
|
|
2298
|
+
: (this.options.output ?? process.stdout);
|
|
2214
2299
|
output.write(`\n[application permission] ${String(event.message)}\n`);
|
|
2215
2300
|
return;
|
|
2216
2301
|
}
|
|
@@ -2292,7 +2377,11 @@ class AgenticStreamClient {
|
|
|
2292
2377
|
output.write(`\n[${event.type}] ${describePermission(event)}\n`);
|
|
2293
2378
|
this.renderedText = this.answerText();
|
|
2294
2379
|
}
|
|
2295
|
-
else if ([
|
|
2380
|
+
else if ([
|
|
2381
|
+
'permission.response_recorded',
|
|
2382
|
+
'permission.delivery_unconfirmed',
|
|
2383
|
+
'permission.response_refused',
|
|
2384
|
+
].includes(event.type)) {
|
|
2296
2385
|
output.write(`\n[permission] ${String(event.message ?? '')}\n`);
|
|
2297
2386
|
this.renderedText = this.answerText();
|
|
2298
2387
|
}
|
|
@@ -2317,7 +2406,8 @@ class AgenticStreamClient {
|
|
|
2317
2406
|
output.write(`\n[question] ${why}\n`);
|
|
2318
2407
|
this.renderedText = this.answerText();
|
|
2319
2408
|
}
|
|
2320
|
-
else if (event.type === 'permission.denied' &&
|
|
2409
|
+
else if (event.type === 'permission.denied' &&
|
|
2410
|
+
event.reason === 'noninteractive_or_not_enabled') {
|
|
2321
2411
|
// Said out loud, because nobody was asked and the agent will not say it plainly: this run
|
|
2322
2412
|
// had no terminal to ask at and was not given --approve-tools, so the call was refused.
|
|
2323
2413
|
output.write(`\n[permission] ${describePermission(event)} denied: nobody could be asked, and this run was not started with --approve-tools\n`);
|
|
@@ -2433,10 +2523,16 @@ class AgenticStreamClient {
|
|
|
2433
2523
|
try {
|
|
2434
2524
|
const terminal = await reconcile();
|
|
2435
2525
|
if (terminal && typeof terminal.text === 'string' && terminal.text) {
|
|
2436
|
-
return {
|
|
2526
|
+
return {
|
|
2527
|
+
text: terminal.text,
|
|
2528
|
+
...(terminal.publicProjection ? { publicProjection: terminal.publicProjection } : {}),
|
|
2529
|
+
};
|
|
2437
2530
|
}
|
|
2438
2531
|
if (terminal)
|
|
2439
|
-
empty = {
|
|
2532
|
+
empty = {
|
|
2533
|
+
text: '',
|
|
2534
|
+
...(terminal.publicProjection ? { publicProjection: terminal.publicProjection } : {}),
|
|
2535
|
+
};
|
|
2440
2536
|
}
|
|
2441
2537
|
catch {
|
|
2442
2538
|
// No evidence either way; try again, then settle on the stream.
|
|
@@ -2498,8 +2594,9 @@ class AgenticStreamClient {
|
|
|
2498
2594
|
// other way round).
|
|
2499
2595
|
// (Compared as the reader sees it: the record holds a widget as its fallback sentence,
|
|
2500
2596
|
// which is shorter than the block the stream carried, and the raw length would never lose.)
|
|
2501
|
-
if (recorded &&
|
|
2502
|
-
recorded.
|
|
2597
|
+
if (recorded &&
|
|
2598
|
+
(recorded.publicProjection === 'lookback_answer_v1' ||
|
|
2599
|
+
recorded.text.length > (0, widgetTerminalText_1.widgetsAsText)(this.accumulated, { final: true }).length)) {
|
|
2503
2600
|
this.accumulated = recorded.text;
|
|
2504
2601
|
this.publicProjection = recorded.publicProjection;
|
|
2505
2602
|
}
|
|
@@ -45,7 +45,9 @@ function renderActionHistory(page, log) {
|
|
|
45
45
|
status: text(row.status),
|
|
46
46
|
check: text(row.checkId),
|
|
47
47
|
// A failed or refused action's reason is the point of reading history.
|
|
48
|
-
why
|
|
48
|
+
// A skipped action says why in `skippedReason` (a zero budget, a fenced Check), which this column
|
|
49
|
+
// never read, so every skipped row printed an empty WHY (OSK-21610).
|
|
50
|
+
why: [text(row.errorCode, ''), brief(row.errorMessage ?? row.skippedReason ?? row.reason)]
|
|
49
51
|
.filter(Boolean)
|
|
50
52
|
.join(' · '),
|
|
51
53
|
})), [
|
|
@@ -125,6 +125,18 @@ export type CommitmentPreflightReport = {
|
|
|
125
125
|
* for an allowed activation or an older server that does not send the block.
|
|
126
126
|
*/
|
|
127
127
|
export declare function activationRefusalLines(activation: CommitmentPreflightReport['activation']): string[];
|
|
128
|
+
/**
|
|
129
|
+
* Whether the server's activation answer names this gate as one that refuses activation (OSK-21258).
|
|
130
|
+
* A gate can be advisory for a run (`admission: 'advisory'`) and still be what `resume` refuses with a
|
|
131
|
+
* 409; the gate list printed it `info (advisory: does not stop a run)` right under an "Activation:
|
|
132
|
+
* would be refused" section naming the same gate. Matches on the gate id, and on the reason too when
|
|
133
|
+
* both sides carry one, so a gate advisory for one reason is not promoted by another. An older server
|
|
134
|
+
* that sends no answer promotes nothing.
|
|
135
|
+
*/
|
|
136
|
+
export declare function gateRefusesActivation(activation: CommitmentPreflightReport['activation'], gate: {
|
|
137
|
+
id?: string;
|
|
138
|
+
reason?: string | null;
|
|
139
|
+
}): boolean;
|
|
128
140
|
/**
|
|
129
141
|
* The lines every Commitment read surface prints for "whose permissions does a
|
|
130
142
|
* run use?" — `show`, `effective-policy` and `preflight` share it so the three
|
|
@@ -1023,6 +1035,18 @@ export declare function describeCommitmentPriority(priority: CommitmentView['pri
|
|
|
1023
1035
|
* stated: the id, and the command that says whether it is active (OSK-21242).
|
|
1024
1036
|
*/
|
|
1025
1037
|
export declare function describeBoundCompass(commitment: Pick<CommitmentView, 'compassId' | 'agentId'>): string;
|
|
1038
|
+
/**
|
|
1039
|
+
* The bound Compass, as `explain` prints it (OSK-21332). `explain` is where a person asks "why does
|
|
1040
|
+
* this Commitment behave this way", and a bound Compass (or an archived one, which runs the
|
|
1041
|
+
* Commitment on defaults) is part of the answer. The explain payload names neither the Compass nor
|
|
1042
|
+
* its status, so the caller reads the Commitment itself, the same read `show` renders, and hands the
|
|
1043
|
+
* result here: the line is `describeBoundCompass`, the one `show` prints, not a second wording.
|
|
1044
|
+
*
|
|
1045
|
+
* `null` means that read did not come back. It is said, not skipped: a silent skip is
|
|
1046
|
+
* indistinguishable from "no Compass is bound". A Commitment bound to none prints nothing, as in
|
|
1047
|
+
* `show`.
|
|
1048
|
+
*/
|
|
1049
|
+
export declare function explainCompassLines(commitmentId: string, commitment: Pick<CommitmentView, 'compassId' | 'agentId'> | null | undefined): string[];
|
|
1026
1050
|
export declare function renderCommitmentSummary(commitment: CommitmentView, log: (line: string) => void,
|
|
1027
1051
|
/**
|
|
1028
1052
|
* The most recent check, when the caller has one. Optional because pause /
|
package/dist/lib/commitments.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
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.activationRefusalLines = activationRefusalLines;
|
|
5
|
+
exports.gateRefusesActivation = gateRefusesActivation;
|
|
5
6
|
exports.permissionLines = permissionLines;
|
|
6
7
|
exports.scheduleIssueLines = scheduleIssueLines;
|
|
7
8
|
exports.retiredFieldsLine = retiredFieldsLine;
|
|
@@ -35,6 +36,7 @@ exports.renderCommitmentWriteWarnings = renderCommitmentWriteWarnings;
|
|
|
35
36
|
exports.describeCommitmentRevision = describeCommitmentRevision;
|
|
36
37
|
exports.describeCommitmentPriority = describeCommitmentPriority;
|
|
37
38
|
exports.describeBoundCompass = describeBoundCompass;
|
|
39
|
+
exports.explainCompassLines = explainCompassLines;
|
|
38
40
|
exports.renderCommitmentSummary = renderCommitmentSummary;
|
|
39
41
|
exports.describeWakeDecision = describeWakeDecision;
|
|
40
42
|
exports.renderCheckList = renderCheckList;
|
|
@@ -124,6 +126,20 @@ function activationRefusalLines(activation) {
|
|
|
124
126
|
}
|
|
125
127
|
return lines;
|
|
126
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Whether the server's activation answer names this gate as one that refuses activation (OSK-21258).
|
|
131
|
+
* A gate can be advisory for a run (`admission: 'advisory'`) and still be what `resume` refuses with a
|
|
132
|
+
* 409; the gate list printed it `info (advisory: does not stop a run)` right under an "Activation:
|
|
133
|
+
* would be refused" section naming the same gate. Matches on the gate id, and on the reason too when
|
|
134
|
+
* both sides carry one, so a gate advisory for one reason is not promoted by another. An older server
|
|
135
|
+
* that sends no answer promotes nothing.
|
|
136
|
+
*/
|
|
137
|
+
function gateRefusesActivation(activation, gate) {
|
|
138
|
+
if (!activation || activation.canActivate !== false || !gate?.id)
|
|
139
|
+
return false;
|
|
140
|
+
return (activation.blockers ?? []).some((blocker) => blocker.gate === gate.id &&
|
|
141
|
+
(!blocker.reason || !gate.reason || blocker.reason === gate.reason));
|
|
142
|
+
}
|
|
127
143
|
/**
|
|
128
144
|
* The lines every Commitment read surface prints for "whose permissions does a
|
|
129
145
|
* run use?" — `show`, `effective-policy` and `preflight` share it so the three
|
|
@@ -499,7 +515,8 @@ function unrecognizedCreateKeys(src) {
|
|
|
499
515
|
if (known.includes(key) || SERVER_OWNED_CONTRACT_KEYS.has(key))
|
|
500
516
|
continue;
|
|
501
517
|
const near = key.length >= 4
|
|
502
|
-
? known.find((candidate) => candidate.length >= 4 &&
|
|
518
|
+
? known.find((candidate) => candidate.length >= 4 &&
|
|
519
|
+
editDistance(key.toLowerCase(), candidate.toLowerCase()) <= 2)
|
|
503
520
|
: undefined;
|
|
504
521
|
if (near)
|
|
505
522
|
typos.push({ key, suggestion: near });
|
|
@@ -521,7 +538,7 @@ function ignoredCreateKeysWarning(keys) {
|
|
|
521
538
|
if (keys.length === 0)
|
|
522
539
|
return null;
|
|
523
540
|
return (`--from-json: ${keys.map((key) => `"${key}"`).join(', ')} ${keys.length === 1 ? 'is' : 'are'} not sent ` +
|
|
524
|
-
|
|
541
|
+
'(the CLI does not forward it), so the Commitment is built without it.');
|
|
525
542
|
}
|
|
526
543
|
/**
|
|
527
544
|
* Normalize a `--from-json` body into something the create endpoint accepts.
|
|
@@ -1291,6 +1308,25 @@ function describeBoundCompass(commitment) {
|
|
|
1291
1308
|
const agent = commitment.agentId ?? '<agent-id>';
|
|
1292
1309
|
return `Compass: ${id} — status not shown here; an archived compass governs nothing: \`skrr compass show ${id} --agent ${agent}\``;
|
|
1293
1310
|
}
|
|
1311
|
+
/**
|
|
1312
|
+
* The bound Compass, as `explain` prints it (OSK-21332). `explain` is where a person asks "why does
|
|
1313
|
+
* this Commitment behave this way", and a bound Compass (or an archived one, which runs the
|
|
1314
|
+
* Commitment on defaults) is part of the answer. The explain payload names neither the Compass nor
|
|
1315
|
+
* its status, so the caller reads the Commitment itself, the same read `show` renders, and hands the
|
|
1316
|
+
* result here: the line is `describeBoundCompass`, the one `show` prints, not a second wording.
|
|
1317
|
+
*
|
|
1318
|
+
* `null` means that read did not come back. It is said, not skipped: a silent skip is
|
|
1319
|
+
* indistinguishable from "no Compass is bound". A Commitment bound to none prints nothing, as in
|
|
1320
|
+
* `show`.
|
|
1321
|
+
*/
|
|
1322
|
+
function explainCompassLines(commitmentId, commitment) {
|
|
1323
|
+
if (!commitment) {
|
|
1324
|
+
return [
|
|
1325
|
+
`Compass: not shown — the commitment could not be read just now; \`skrr commitments show ${commitmentId}\` prints it`,
|
|
1326
|
+
];
|
|
1327
|
+
}
|
|
1328
|
+
return commitment.compassId ? [describeBoundCompass(commitment)] : [];
|
|
1329
|
+
}
|
|
1294
1330
|
function renderCommitmentSummary(commitment, log,
|
|
1295
1331
|
/**
|
|
1296
1332
|
* The most recent check, when the caller has one. Optional because pause /
|
|
@@ -1809,6 +1845,9 @@ const CYCLE_GLYPH = {
|
|
|
1809
1845
|
stopped: '●',
|
|
1810
1846
|
quiet: '○',
|
|
1811
1847
|
in_flight: '◌',
|
|
1848
|
+
// An applied result whose agent said work is warranted and left nothing the platform can see
|
|
1849
|
+
// (OSK-21608): not a tick, because the work is still owed, and not a stop, because it applied.
|
|
1850
|
+
owed: '◐',
|
|
1812
1851
|
};
|
|
1813
1852
|
/** `MM-DD HH:MM` in the reader's local time; `-` when the time is unknown. */
|
|
1814
1853
|
function cycleTime(value) {
|
|
@@ -1838,6 +1877,11 @@ function cycleSummary(cycle) {
|
|
|
1838
1877
|
return `${question} ${reason || gate} — ${fix || detail || 'stopped'}${moved}`;
|
|
1839
1878
|
}
|
|
1840
1879
|
if (cycle.outcome === 'advanced') {
|
|
1880
|
+
if (cycle.owed) {
|
|
1881
|
+
return cycle.advancementKinds?.length
|
|
1882
|
+
? `${cycle.advancementKinds.join(', ')} — ${data_provider_2.COMMITMENT_CYCLE_OWED_SENTENCE}`
|
|
1883
|
+
: data_provider_2.COMMITMENT_CYCLE_OWED_SENTENCE;
|
|
1884
|
+
}
|
|
1841
1885
|
return cycle.advancementKinds?.length
|
|
1842
1886
|
? cycle.advancementKinds.join(', ')
|
|
1843
1887
|
: 'applied an agent result';
|
|
@@ -1848,6 +1892,13 @@ function cycleSummary(cycle) {
|
|
|
1848
1892
|
}
|
|
1849
1893
|
return cycle.note || cycle.decision?.summary || cycle.decision?.posture || 'nothing to act on';
|
|
1850
1894
|
}
|
|
1895
|
+
/** A record-only follow-through says what it is, and what it noted, instead of claiming progress. */
|
|
1896
|
+
function recordOnlyEchoSummary(cycle) {
|
|
1897
|
+
const noted = cycle.advancementKinds?.length
|
|
1898
|
+
? ` (noted: ${cycle.advancementKinds.join(', ')})`
|
|
1899
|
+
: '';
|
|
1900
|
+
return `records the earlier run's outcome; no work of its own${noted}`;
|
|
1901
|
+
}
|
|
1851
1902
|
/** Content-free rows admitted by the native sensor loader; licenses stay unknown. */
|
|
1852
1903
|
function platformFetchLines(cycle) {
|
|
1853
1904
|
const lines = [];
|
|
@@ -1879,8 +1930,17 @@ function formatCommitmentCycles(page) {
|
|
|
1879
1930
|
return ['No cycles recorded yet.'];
|
|
1880
1931
|
const lines = [];
|
|
1881
1932
|
for (const cycle of cycles) {
|
|
1882
|
-
|
|
1883
|
-
|
|
1933
|
+
// A follow-through Check that only records the Run before it is not what the loop did, and
|
|
1934
|
+
// `✓ advanced` above a stopped Run reads as if the work had moved (OSK-21240).
|
|
1935
|
+
const echo = (0, data_provider_2.isRecordOnlyEchoCycle)(cycle);
|
|
1936
|
+
const owed = !echo && cycle.outcome === 'advanced' && cycle.owed === true;
|
|
1937
|
+
const glyph = echo
|
|
1938
|
+
? CYCLE_GLYPH.quiet
|
|
1939
|
+
: owed
|
|
1940
|
+
? CYCLE_GLYPH.owed
|
|
1941
|
+
: CYCLE_GLYPH[cycle.outcome] || '?';
|
|
1942
|
+
const word = echo ? 'recorded' : owed ? 'owed' : String(cycle.outcome);
|
|
1943
|
+
lines.push(`${cycleTime(cycle.startedAt)} ${glyph} ${word.padEnd(9)} ${cycleWake(cycle).padEnd(18)} ${echo ? recordOnlyEchoSummary(cycle) : cycleSummary(cycle)}`);
|
|
1884
1944
|
lines.push(...platformFetchLines(cycle));
|
|
1885
1945
|
lines.push(...formatCycleLatency(cycle));
|
|
1886
1946
|
for (const task of cycle.tasks || []) {
|
package/dist/lib/compasses.d.ts
CHANGED
|
@@ -134,6 +134,8 @@ export declare const compassApi: {
|
|
|
134
134
|
data: CompassRevision[];
|
|
135
135
|
/** True when the ledger holds rows older than this page. */
|
|
136
136
|
has_more?: boolean;
|
|
137
|
+
/** How many revisions the ledger holds in all; absent from a server that predates it. */
|
|
138
|
+
total?: number;
|
|
137
139
|
}>;
|
|
138
140
|
trace: (id: string, query: Parameters<typeof withQuery>[1]) => Promise<CompassTrace>;
|
|
139
141
|
revert: (id: string, body: Record<string, unknown>) => Promise<Compass>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One reading of "an ISO-8601 UTC instant" for every CLI flag that takes one.
|
|
3
|
+
*
|
|
4
|
+
* `tasks remind --at` and `tasks schedule --at` each had their own: remind ran
|
|
5
|
+
* `new Date(value)` behind a `/^\d{4}-\d{2}-\d{2}T/` check, and schedule forwarded
|
|
6
|
+
* the string and printed the server's zod issue. `new Date()` is the wrong tool
|
|
7
|
+
* for an instant, in two ways that both stored a different time than the one
|
|
8
|
+
* typed and said nothing (OSK-21504, OSK-21506):
|
|
9
|
+
*
|
|
10
|
+
* - a time with no zone is read in the zone of the MACHINE running the CLI, so
|
|
11
|
+
* the same command stored different instants on different laptops;
|
|
12
|
+
* - a day that does not exist overflows into the next month
|
|
13
|
+
* (`2027-02-30` is `2027-03-02`).
|
|
14
|
+
*
|
|
15
|
+
* The rule here is the one `tasks schedule --help` already states and the server's
|
|
16
|
+
* `z.string().datetime()` enforces: a calendar-real date, a time with seconds, and
|
|
17
|
+
* a literal `Z`. A numeric offset is refused too, even though it names an instant:
|
|
18
|
+
* the two commands must agree, and the server's schedule door does not take one.
|
|
19
|
+
*
|
|
20
|
+
* Never reads the machine's zone. Pure.
|
|
21
|
+
*/
|
|
22
|
+
export type InstantProblem = 'not_a_date' | 'date_only' | 'no_zone' | 'numeric_offset' | 'not_a_real_date' | 'not_a_real_time';
|
|
23
|
+
export type InstantParse = {
|
|
24
|
+
ok: true;
|
|
25
|
+
date: Date;
|
|
26
|
+
iso: string;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
problem: InstantProblem;
|
|
30
|
+
message: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Parse a flag value as a UTC instant, or say precisely what is wrong with it.
|
|
34
|
+
*
|
|
35
|
+
* `label` is what the user typed (`--at`, `scheduledFor`), so the message names
|
|
36
|
+
* their input rather than a REST field they never saw.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseUtcInstant(raw: string, label?: string): InstantParse;
|
|
39
|
+
/** `2027-02-02T09:00:00Z` - the instant in UTC, whole seconds when there are no milliseconds. */
|
|
40
|
+
export declare function formatUtcInstant(date: Date): string;
|
|
41
|
+
/**
|
|
42
|
+
* `2027-02-02 01:00:00 PST` - the same instant on this machine's wall clock, with the
|
|
43
|
+
* zone NAMED. `toLocaleString()` printed neither the zone nor anything that mapped back
|
|
44
|
+
* to the instant that was typed (OSK-21505). `timeZone` is for tests and defaults to
|
|
45
|
+
* the machine's own.
|
|
46
|
+
*/
|
|
47
|
+
export declare function formatLocalInstant(date: Date, timeZone?: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* The instant for a person: UTC first (it is what was typed and what is stored), then the
|
|
50
|
+
* local reading with its zone name. A machine already on UTC gets the UTC form alone.
|
|
51
|
+
*/
|
|
52
|
+
export declare function describeInstant(date: Date, timeZone?: string): string;
|