@skrr-ai/cli 0.1.117 → 0.1.119

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/base-command.d.ts +1 -0
  2. package/dist/base-command.js +7 -1
  3. package/dist/commands/actions/browse.js +2 -1
  4. package/dist/commands/actions/install.d.ts +1 -0
  5. package/dist/commands/actions/install.js +21 -1
  6. package/dist/commands/actions/list.js +2 -1
  7. package/dist/commands/actions/show.js +2 -1
  8. package/dist/commands/memory/versions/list.d.ts +2 -0
  9. package/dist/commands/memory/versions/list.js +3 -2
  10. package/dist/commands/memory/versions/show.d.ts +2 -0
  11. package/dist/commands/memory/versions/show.js +3 -2
  12. package/dist/commands/skills/browse.js +2 -1
  13. package/dist/commands/skills/details.js +4 -2
  14. package/dist/commands/skills/featured.js +2 -1
  15. package/dist/commands/skills/install.d.ts +1 -0
  16. package/dist/commands/skills/install.js +21 -1
  17. package/dist/commands/skills/search.js +2 -1
  18. package/dist/commands/store/acquisitions/list.js +2 -1
  19. package/dist/commands/store/acquisitions/show.js +2 -1
  20. package/dist/commands/store/browse.js +4 -2
  21. package/dist/commands/store/install.d.ts +1 -0
  22. package/dist/commands/store/install.js +26 -2
  23. package/dist/commands/store/releases.js +4 -2
  24. package/dist/commands/store/show.js +3 -2
  25. package/dist/commands/store/tip.js +2 -2
  26. package/dist/generated/agent-guides.json +63 -41
  27. package/dist/lib/action-listings.js +2 -1
  28. package/dist/lib/api-fetch.d.ts +4 -0
  29. package/dist/lib/api-fetch.js +26 -6
  30. package/dist/lib/memory-history-session.d.ts +14 -0
  31. package/dist/lib/memory-history-session.js +60 -0
  32. package/dist/lib/node-adapter.js +4 -3
  33. package/dist/lib/scoped-memory-command.d.ts +6 -0
  34. package/dist/lib/scoped-memory-command.js +34 -3
  35. package/dist/lib/store-listing.d.ts +6 -1
  36. package/dist/lib/store-listing.js +6 -1
  37. package/dist/lib/task-admission.js +27 -5
  38. package/dist/lib/task-runtime-headers.d.ts +11 -0
  39. package/dist/lib/task-runtime-headers.js +83 -0
  40. package/dist/lib/task-write-attribution.js +1 -1
  41. package/dist/lib/usd-micros.d.ts +27 -0
  42. package/dist/lib/usd-micros.js +67 -2
  43. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.d.ts +17 -0
  44. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.js +35 -2
  45. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.d.ts +17 -0
  46. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.js +44 -0
  47. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  48. package/dist/node_modules/@skrr-ai/data-provider/index.js +11587 -11548
  49. package/oclif.manifest.json +28 -2
  50. package/package.json +1 -1
@@ -67,6 +67,12 @@ export declare function parseMemoryApiError(error: ApiFetchError): {
67
67
  export declare abstract class ScopedMemoryCommand extends BaseCommand {
68
68
  protected endpoint(agentId: string, suffix?: string): string;
69
69
  protected memoryApi<T>(agentId: string, suffix: string, options?: ApiFetchOptions): Promise<T>;
70
+ protected memoryHistoryApi<T>(flags: ScopedFlags, options: {
71
+ versionId?: string;
72
+ memoryId?: string;
73
+ limit?: number;
74
+ }): Promise<T>;
75
+ private memoryRequest;
70
76
  protected query(flags: ScopedFlags, extra?: Record<string, unknown>): string;
71
77
  protected resolveStore(flags: ScopedFlags, { write }?: {
72
78
  write?: boolean | undefined;
@@ -10,6 +10,7 @@ const core_1 = require("@oclif/core");
10
10
  const node_crypto_1 = require("node:crypto");
11
11
  const base_command_1 = require("../base-command");
12
12
  const api_fetch_1 = require("./api-fetch");
13
+ const memory_history_session_1 = require("./memory-history-session");
13
14
  function parseMemoryStoreResolutions(value) {
14
15
  if (value === undefined)
15
16
  return undefined;
@@ -120,6 +121,9 @@ const ERROR_EXITS = {
120
121
  MEMORY_AUTHORIZATION_STALE: 8,
121
122
  };
122
123
  function parseMemoryApiError(error) {
124
+ if (error.code === 'MEMORY_HISTORY_API_UNSUPPORTED') {
125
+ return { code: error.code, message: error.message, retryable: false };
126
+ }
123
127
  try {
124
128
  const body = JSON.parse(error.body || '{}');
125
129
  const details = {
@@ -136,7 +140,7 @@ function parseMemoryApiError(error) {
136
140
  };
137
141
  return {
138
142
  code: String(body.code || body.error || error.code || 'MEMORY_API_ERROR').toUpperCase(),
139
- message: [body.message || error.message, body.remedy].filter(Boolean).join(' '),
143
+ message: [body.message || error.message, body.remedy, error.recoveryHint].filter(Boolean).join(' '),
140
144
  retryable: body.retryable === true,
141
145
  ...(Object.keys(details).length ? { details } : {}),
142
146
  };
@@ -144,7 +148,7 @@ function parseMemoryApiError(error) {
144
148
  catch {
145
149
  return {
146
150
  code: error.code || 'MEMORY_API_ERROR',
147
- message: error.message,
151
+ message: [error.message, error.recoveryHint].filter(Boolean).join(' '),
148
152
  retryable: false,
149
153
  };
150
154
  }
@@ -154,8 +158,35 @@ class ScopedMemoryCommand extends base_command_1.BaseCommand {
154
158
  return `/api/agents/${encodeURIComponent(agentId)}/memories/workspace${suffix}`;
155
159
  }
156
160
  async memoryApi(agentId, suffix, options = {}) {
161
+ return this.memoryRequest(this.endpoint(agentId, suffix), options);
162
+ }
163
+ async memoryHistoryApi(flags, options) {
164
+ const suffix = options.versionId ? `/${encodeURIComponent(options.versionId)}` : '';
165
+ const query = { memoryId: options.memoryId, limit: options.limit };
166
+ if (!(0, memory_history_session_1.hasMemoryHistoryRuntime)()) {
167
+ const store = await this.resolveStore(flags);
168
+ return this.memoryApi(flags.agent, `/stores/${store.id}/versions${suffix}${this.query(flags, query)}`);
169
+ }
170
+ const { sessionId } = (0, memory_history_session_1.memoryHistorySession)();
171
+ if (Boolean(flags.store) === Boolean(flags.scope)) {
172
+ this.error('Pass exactly one of --store <id> or --scope agent:<id>|space:<id>.', { exit: 2 });
173
+ }
174
+ if (flags.scope && !/^(agent|space):[^:]+$/.test(flags.scope)) {
175
+ this.error('--scope must be agent:<id> or space:<id>.', { exit: 2 });
176
+ }
177
+ if (flags.scope?.startsWith('space:') && !flags.space) {
178
+ this.error(`MEMORY_SPACE_CONTEXT_REQUIRED: --space ${flags.scope.slice(6)} is required when selecting ${flags.scope}.`, { exit: 2, code: 'MEMORY_SPACE_CONTEXT_REQUIRED' });
179
+ }
180
+ // Mount resolution belongs to the admitted session facade. A generic Store
181
+ // preflight would require an owner credential before the session-only read.
182
+ const path = `/api/agentic/sessions/${encodeURIComponent(sessionId)}/memory/agents/${encodeURIComponent(flags.agent)}/versions${suffix}`;
183
+ return this.memoryRequest(`${path}${this.query(flags, {
184
+ ...query, ...(flags.store ? { storeId: flags.store } : { scope: flags.scope }),
185
+ })}`, { sessionRead: 'memory-history-v1' });
186
+ }
187
+ async memoryRequest(path, options) {
157
188
  try {
158
- return await (0, api_fetch_1.apiFetch)(this.endpoint(agentId, suffix), options);
189
+ return await (0, api_fetch_1.apiFetch)(path, options);
159
190
  }
160
191
  catch (error) {
161
192
  if (error instanceof api_fetch_1.ApiFetchError) {
@@ -15,7 +15,12 @@ import { type ReleaseCapabilityManifestView, type StoreAgentInstallFunnelSummary
15
15
  * "withheld" with the floor it missed, never as 0 — a zero is a claim that
16
16
  * nobody did the thing, and the server did not make that claim.
17
17
  */
18
- /** A listing's price in token credits (1,000,000 = $1), as the Store states it. */
18
+ /**
19
+ * A listing's price as the Store prints it — `$4.00`, or `Free`. The wire
20
+ * value is integer micro-USD (1,000,000 = $1); every store, skills and
21
+ * actions command prints through this one formatter so no surface can quote
22
+ * the same price in a different unit (OSK-18256).
23
+ */
19
24
  export declare function formatListingPrice(price: number | null | undefined): string;
20
25
  /** `4.6 ★ (12 ratings)`, or `No ratings yet` — never `0.0` for an unrated listing. */
21
26
  export declare function formatListingRating(rating: number | null | undefined, count: number | null | undefined): string;
@@ -29,7 +29,12 @@ const data_provider_1 = require("@skrr-ai/data-provider");
29
29
  * "withheld" with the floor it missed, never as 0 — a zero is a claim that
30
30
  * nobody did the thing, and the server did not make that claim.
31
31
  */
32
- /** A listing's price in token credits (1,000,000 = $1), as the Store states it. */
32
+ /**
33
+ * A listing's price as the Store prints it — `$4.00`, or `Free`. The wire
34
+ * value is integer micro-USD (1,000,000 = $1); every store, skills and
35
+ * actions command prints through this one formatter so no surface can quote
36
+ * the same price in a different unit (OSK-18256).
37
+ */
33
38
  function formatListingPrice(price) {
34
39
  return (0, data_provider_1.formatStoreAcquisitionPrice)(price);
35
40
  }
@@ -6,14 +6,34 @@ function admissionLines(value) {
6
6
  if (!value)
7
7
  return ['Execution admission: not reported by this server.'];
8
8
  const lines = [`Approval prerequisites: ${value.state}.`];
9
+ const observation = value.runtimePolicyObservation;
9
10
  if (value.permission) {
10
- lines.push(`Permission policy: ${value.permission.mode || 'inherited/unknown'} (${value.permission.source || 'source unavailable'}).`);
11
+ lines.push(!value.permission.mode &&
12
+ value.permission.source === 'daemon' &&
13
+ observation?.source !== 'not_applicable'
14
+ ? 'Permission override: inherit daemon (no Agent or Harness override).'
15
+ : `Permission policy: ${value.permission.mode || 'inherited/unknown'} (${value.permission.source || 'source unavailable'}).`);
16
+ }
17
+ const configured = observation?.configuredDefault;
18
+ if (observation ? observation.source !== 'not_applicable' : value.permission?.source === 'daemon') {
19
+ if (configured && observation?.source === 'daemon_registration') {
20
+ lines.push(`Daemon last reported configured default: ${configured.permissionMode} (${configured.provider}/${configured.engine}; process registration at ${observation.reportedAt}). This is not the future effective policy.`);
21
+ }
22
+ else {
23
+ lines.push('Daemon configured default: unreported. Check status on the selected machine and daemon profile; reconnect or update that daemon to refresh its report.');
24
+ }
25
+ if (value.taskCommandApproval?.support === 'reported') {
26
+ lines.push(`Exact Task-command approval protocol: reported (${value.taskCommandApproval.provider}/${value.taskCommandApproval.engine}). Run, owner and command readiness remain unverified; no command is authorized.`);
27
+ }
28
+ else {
29
+ lines.push('Exact Task-command approval protocol: unreported for the selected engine.');
30
+ }
11
31
  }
12
32
  if (value.workingDirectory) {
13
33
  lines.push(`Working directory: ${value.workingDirectory.path || 'daemon configured default'} (${value.workingDirectory.source}; existence and access are checked on the machine at start).`);
14
34
  }
15
35
  if (value.unattended && !value.approverAvailable) {
16
- lines.push('This run is unattended. Tools that need approval cannot obtain it during the run.');
36
+ lines.push('This run is unattended; generic provider interactive approval is unavailable. Exact Task-command requests have a separate approval path when supported.');
17
37
  }
18
38
  for (const constraint of value.constraints || []) {
19
39
  if (constraint.message)
@@ -29,7 +49,9 @@ function isAdmissionPreview(value) {
29
49
  return false;
30
50
  const row = value;
31
51
  const admission = row.executionAdmission;
32
- return row.dryRun === true && typeof row.taskId === 'string' && Boolean(admission &&
33
- ['ready', 'blocked', 'unknown'].includes(String(admission.state)) &&
34
- admission.scope === 'approval_prerequisites');
52
+ return (row.dryRun === true &&
53
+ typeof row.taskId === 'string' &&
54
+ Boolean(admission &&
55
+ ['ready', 'blocked', 'unknown'].includes(String(admission.state)) &&
56
+ admission.scope === 'approval_prerequisites'));
35
57
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Account Authorization decides access; a separate server-issued session
3
+ * credential proves Agent authorship. Environment ids are selectors only.
4
+ * Share this across both CLI transports so Task authorship cannot depend on
5
+ * which command happens to use dataService rather than apiFetch.
6
+ */
7
+ export declare function withTaskRuntimeHeaders(headers: Record<string, string>, requestUrl: string, method: string, env?: Record<string, string | undefined>): Record<string, string>;
8
+ /** Custom credentials never follow HTTP redirects, including 302 POST-to-GET. */
9
+ export declare function taskRuntimeRedirectOptions(headers: Record<string, string>): {
10
+ redirect?: 'error';
11
+ };
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withTaskRuntimeHeaders = withTaskRuntimeHeaders;
4
+ exports.taskRuntimeRedirectOptions = taskRuntimeRedirectOptions;
5
+ /**
6
+ * Account Authorization decides access; a separate server-issued session
7
+ * credential proves Agent authorship. Environment ids are selectors only.
8
+ * Share this across both CLI transports so Task authorship cannot depend on
9
+ * which command happens to use dataService rather than apiFetch.
10
+ */
11
+ function withTaskRuntimeHeaders(headers, requestUrl, method, env = process.env) {
12
+ const sent = { ...headers };
13
+ // A companion credential never travels on a read, another API surface,
14
+ // or a caller-selected header. Preserve existing nonsecret correlation
15
+ // hints outside Task authorship mutations for other APIs' provenance checks.
16
+ for (const key of Object.keys(sent)) {
17
+ if (key.toLowerCase() === 'x-oversky-session-authorization')
18
+ delete sent[key];
19
+ }
20
+ if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(method.toUpperCase()))
21
+ return sent;
22
+ const refuseOrigin = () => Object.assign(new Error('Managed Task requests require their bound server origin; refusing account-attributed fallback.'), {
23
+ code: 'SESSION_TASK_ORIGIN_MISMATCH',
24
+ });
25
+ let target;
26
+ try {
27
+ target = new URL(requestUrl);
28
+ }
29
+ catch {
30
+ if (env.OVERSKY_AUTONOMOUS_SESSION === '1')
31
+ throw refuseOrigin();
32
+ return sent;
33
+ }
34
+ // Some platform operations materialize Tasks outside the Task router. Keep
35
+ // their exact POST routes here; action/library edits, picker reads and other
36
+ // integration routes must never receive the companion credential.
37
+ const taskMutation = /^\/api\/tasks(?:\/|$)/.test(target.pathname);
38
+ const taskCreation = method.toUpperCase() === 'POST' && [
39
+ /^\/api\/agents\/[^/]+\/agent-actions\/[^/]+\/run\/?$/,
40
+ /^\/api\/(?:shortcuts|task-templates)\/[^/]+\/(?:instantiate|trigger)\/?$/,
41
+ /^\/api\/templates\/[^/]+\/(?:instantiate|fork)\/?$/,
42
+ /^\/api\/store\/services\/[^/]+\/install\/?$/,
43
+ ].some((route) => route.test(target.pathname));
44
+ if (!taskMutation && !taskCreation)
45
+ return sent;
46
+ const names = new Set(['x-oversky-agent-id', 'x-oversky-session-id', 'x-oversky-autonomous-run-id']);
47
+ for (const key of Object.keys(sent))
48
+ if (names.has(key.toLowerCase()))
49
+ delete sent[key];
50
+ if (env.OVERSKY_AUTONOMOUS_SESSION !== '1')
51
+ return sent;
52
+ const runId = env.OVERSKY_AUTONOMOUS_RUN_ID?.trim();
53
+ const sessionId = env.OVERSKY_SESSION_ID?.trim();
54
+ const token = env.OVERSKY_SESSION_TOKEN?.trim();
55
+ let trusted;
56
+ try {
57
+ trusted = new URL(env.OVERSKY_SERVER_URL || '');
58
+ }
59
+ catch {
60
+ throw refuseOrigin();
61
+ }
62
+ // The companion credential only travels to the managed runtime's server
63
+ // and only to Task mutations/materialization. Bound routes use it as Authorization.
64
+ if (target.origin !== trusted.origin)
65
+ throw refuseOrigin();
66
+ if (!sessionId || !token) {
67
+ throw Object.assign(new Error('Managed Task requests require their bound session credential; refusing account-attributed fallback.'), {
68
+ code: 'SESSION_TASK_CREDENTIAL_MISSING',
69
+ });
70
+ }
71
+ if (runId)
72
+ sent['X-Oversky-Autonomous-Run-Id'] = runId;
73
+ sent['X-Oversky-Session-Id'] = sessionId;
74
+ sent['X-Oversky-Session-Authorization'] = `Bearer ${token}`;
75
+ const agentId = env.OVERSKY_SESSION_AGENT_ID?.trim();
76
+ if (agentId)
77
+ sent['X-Oversky-Agent-Id'] = agentId;
78
+ return sent;
79
+ }
80
+ /** Custom credentials never follow HTTP redirects, including 302 POST-to-GET. */
81
+ function taskRuntimeRedirectOptions(headers) {
82
+ return headers['X-Oversky-Session-Authorization'] ? { redirect: 'error' } : {};
83
+ }
@@ -4,7 +4,7 @@ exports.ACCOUNT_ATTRIBUTION_NOTICE = exports.AGENT_BINDING_GUIDANCE = void 0;
4
4
  exports.taskWriteAttribution = taskWriteAttribution;
5
5
  exports.attributionLines = attributionLines;
6
6
  exports.runtimeWriteAttribution = runtimeWriteAttribution;
7
- exports.AGENT_BINDING_GUIDANCE = 'Assign this runtime in skrr → Engines, then run in its authenticated Agent session. ' +
7
+ exports.AGENT_BINDING_GUIDANCE = 'Run `skrr tasks start <task>` to execute in the assigned Agent’s authenticated runtime. ' +
8
8
  'An --agent flag or an environment Agent id cannot establish authenticated authorship.';
9
9
  exports.ACCOUNT_ATTRIBUTION_NOTICE = 'Without authenticated Agent provenance, Task writes are attributed to the signed-in account. ' +
10
10
  'An account-attributed assessment is not proof of an independent human check.';
@@ -7,3 +7,30 @@
7
7
  * Returns null for anything that is not a positive amount with ≤ 2 decimals.
8
8
  */
9
9
  export declare function usdToMicros(amount: string): number | null;
10
+ /**
11
+ * The same parse, but "0" is a price too: `--expected-price 0` pins free.
12
+ * Returns null for anything that is not an amount with ≤ 2 decimals.
13
+ */
14
+ export declare function usdToMicrosOrZero(amount: string): number | null;
15
+ /**
16
+ * `--expected-price` as micros: `undefined` when the flag was not passed,
17
+ * `null` when the value is not a USD amount.
18
+ */
19
+ export declare function expectedPriceFlagMicros(value: string | undefined): number | null | undefined;
20
+ /**
21
+ * The refusal text when `--expected-price` does not match what a paid
22
+ * install would now charge — one wording for every install command so the
23
+ * pin reads the same whichever listing it guarded (OSK-18256).
24
+ */
25
+ export declare function expectedPriceMismatchMessage(expectedMicros: number, chargeMicros: number): string;
26
+ /**
27
+ * A `--json` payload with every unstated-unit store price restated in
28
+ * dollars: a numeric `price`, `listPrice` or `charge` (integer micro-USD,
29
+ * the one unit store prices travel the wire in — see `storePricing.ts` in
30
+ * `@skrr-ai/data-provider`) gains a sibling `<name>Usd` in dollars, so a
31
+ * reader never has to know the conversion (OSK-18256). Fields that already
32
+ * name their unit (`amountMicros`, `*Micros`) and anything that is not a
33
+ * finite number pass through untouched; the wire fields themselves keep
34
+ * their values.
35
+ */
36
+ export declare function withUsdPrices<T>(payload: T): T;
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.usdToMicros = usdToMicros;
4
+ exports.usdToMicrosOrZero = usdToMicrosOrZero;
5
+ exports.expectedPriceFlagMicros = expectedPriceFlagMicros;
6
+ exports.expectedPriceMismatchMessage = expectedPriceMismatchMessage;
7
+ exports.withUsdPrices = withUsdPrices;
8
+ const data_provider_1 = require("@skrr-ai/data-provider");
4
9
  /**
5
10
  * Parse a user-typed USD amount ("5", "2.50") into integer micros.
6
11
  *
@@ -10,12 +15,72 @@ exports.usdToMicros = usdToMicros;
10
15
  * Returns null for anything that is not a positive amount with ≤ 2 decimals.
11
16
  */
12
17
  function usdToMicros(amount) {
18
+ const cents = parseUsdCents(amount);
19
+ if (cents === null || cents <= 0)
20
+ return null;
21
+ return cents * 10_000;
22
+ }
23
+ /**
24
+ * The same parse, but "0" is a price too: `--expected-price 0` pins free.
25
+ * Returns null for anything that is not an amount with ≤ 2 decimals.
26
+ */
27
+ function usdToMicrosOrZero(amount) {
28
+ const cents = parseUsdCents(amount);
29
+ return cents === null ? null : cents * 10_000;
30
+ }
31
+ function parseUsdCents(amount) {
13
32
  const trimmed = amount.trim();
14
33
  if (!/^\d+(\.\d{1,2})?$/.test(trimmed))
15
34
  return null;
16
35
  const [whole, frac = ''] = trimmed.split('.');
17
36
  const cents = Number(whole) * 100 + Number(frac.padEnd(2, '0'));
18
- if (!Number.isSafeInteger(cents) || cents <= 0)
37
+ // cents * 10_000 must stay an exact integer micros value.
38
+ if (!Number.isSafeInteger(cents) || cents > Number.MAX_SAFE_INTEGER / 10_000)
19
39
  return null;
20
- return cents * 10_000;
40
+ return cents;
41
+ }
42
+ /**
43
+ * `--expected-price` as micros: `undefined` when the flag was not passed,
44
+ * `null` when the value is not a USD amount.
45
+ */
46
+ function expectedPriceFlagMicros(value) {
47
+ return value === undefined ? undefined : usdToMicrosOrZero(value);
48
+ }
49
+ /**
50
+ * The refusal text when `--expected-price` does not match what a paid
51
+ * install would now charge — one wording for every install command so the
52
+ * pin reads the same whichever listing it guarded (OSK-18256).
53
+ */
54
+ function expectedPriceMismatchMessage(expectedMicros, chargeMicros) {
55
+ return (`--expected-price ${(0, data_provider_1.formatStoreAcquisitionPrice)(expectedMicros)} does not match the price ` +
56
+ `the listing now charges (${(0, data_provider_1.formatStoreAcquisitionPrice)(chargeMicros)}). ` +
57
+ 'Nothing was installed — read the price again and ask the person before buying.');
58
+ }
59
+ /** Fields that carry a store price in integer micro-USD without saying so. */
60
+ const MICROS_PRICE_FIELDS = new Set(['price', 'listPrice', 'charge']);
61
+ /**
62
+ * A `--json` payload with every unstated-unit store price restated in
63
+ * dollars: a numeric `price`, `listPrice` or `charge` (integer micro-USD,
64
+ * the one unit store prices travel the wire in — see `storePricing.ts` in
65
+ * `@skrr-ai/data-provider`) gains a sibling `<name>Usd` in dollars, so a
66
+ * reader never has to know the conversion (OSK-18256). Fields that already
67
+ * name their unit (`amountMicros`, `*Micros`) and anything that is not a
68
+ * finite number pass through untouched; the wire fields themselves keep
69
+ * their values.
70
+ */
71
+ function withUsdPrices(payload) {
72
+ if (Array.isArray(payload)) {
73
+ return payload.map((item) => withUsdPrices(item));
74
+ }
75
+ if (payload === null || typeof payload !== 'object') {
76
+ return payload;
77
+ }
78
+ const out = {};
79
+ for (const [key, value] of Object.entries(payload)) {
80
+ out[key] = withUsdPrices(value);
81
+ if (MICROS_PRICE_FIELDS.has(key) && typeof value === 'number' && Number.isFinite(value)) {
82
+ out[`${key}Usd`] = (0, data_provider_1.listingPriceMicrosToUsd)(value);
83
+ }
84
+ }
85
+ return out;
21
86
  }
@@ -30,3 +30,20 @@ export interface HarnessPermissionControlReport {
30
30
  * rather than a green badge. Additive fields are stripped for compatibility.
31
31
  */
32
32
  export declare function parseHarnessPermissionControls(value: unknown): HarnessPermissionControls | null;
33
+ /** Last-reported local configuration and adapter protocol, never authority. */
34
+ export interface HarnessSessionPolicyReport {
35
+ version: 1;
36
+ configuredDefault: {
37
+ provider: 'claude';
38
+ engine: 'sdk' | 'cli';
39
+ permissionMode: 'default' | 'acceptEdits' | 'plan' | 'bypassPermissions';
40
+ } | null;
41
+ taskCommandApproval: {
42
+ protocol: 'task_command_once_v1';
43
+ adapters: readonly {
44
+ provider: 'claude';
45
+ engine: 'sdk';
46
+ }[];
47
+ } | null;
48
+ }
49
+ export declare function parseHarnessSessionPolicyReport(value: unknown): HarnessSessionPolicyReport | null;
@@ -18,7 +18,8 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var harnessPermissionControls_exports = {};
20
20
  __export(harnessPermissionControls_exports, {
21
- parseHarnessPermissionControls: () => parseHarnessPermissionControls
21
+ parseHarnessPermissionControls: () => parseHarnessPermissionControls,
22
+ parseHarnessSessionPolicyReport: () => parseHarnessSessionPolicyReport
22
23
  });
23
24
  module.exports = __toCommonJS(harnessPermissionControls_exports);
24
25
  const PLAN = ["harness", "reported-actions", "unsupported", "unknown"];
@@ -63,7 +64,39 @@ function parseHarnessPermissionControls(value) {
63
64
  return null;
64
65
  }
65
66
  }
67
+ function parseHarnessSessionPolicyReport(value) {
68
+ try {
69
+ if (!value || typeof value !== "object" || Array.isArray(value))
70
+ return null;
71
+ const row = value;
72
+ if (row.version !== 1)
73
+ return null;
74
+ let configuredDefault = null;
75
+ let taskCommandApproval = null;
76
+ const config = row.configuredDefault;
77
+ if (config && typeof config === "object" && !Array.isArray(config) && config.provider === "claude" && ["sdk", "cli"].includes(config.engine) && ["default", "acceptEdits", "plan", "bypassPermissions"].includes(config.permissionMode)) {
78
+ configuredDefault = Object.freeze({
79
+ provider: "claude",
80
+ engine: config.engine,
81
+ permissionMode: config.permissionMode
82
+ });
83
+ }
84
+ const approval = row.taskCommandApproval;
85
+ if (approval && typeof approval === "object" && !Array.isArray(approval) && approval.protocol === "task_command_once_v1" && Array.isArray(approval.adapters) && approval.adapters.length === 1 && approval.adapters[0]?.provider === "claude" && approval.adapters[0]?.engine === "sdk") {
86
+ taskCommandApproval = Object.freeze({
87
+ protocol: "task_command_once_v1",
88
+ adapters: Object.freeze([
89
+ Object.freeze({ provider: "claude", engine: "sdk" })
90
+ ])
91
+ });
92
+ }
93
+ return Object.freeze({ version: 1, configuredDefault, taskCommandApproval });
94
+ } catch {
95
+ return null;
96
+ }
97
+ }
66
98
  // Annotate the CommonJS export names for ESM import in node:
67
99
  0 && (module.exports = {
68
- parseHarnessPermissionControls
100
+ parseHarnessPermissionControls,
101
+ parseHarnessSessionPolicyReport
69
102
  });
@@ -30,3 +30,20 @@ export interface HarnessPermissionControlReport {
30
30
  * rather than a green badge. Additive fields are stripped for compatibility.
31
31
  */
32
32
  export declare function parseHarnessPermissionControls(value: unknown): HarnessPermissionControls | null;
33
+ /** Last-reported local configuration and adapter protocol, never authority. */
34
+ export interface HarnessSessionPolicyReport {
35
+ version: 1;
36
+ configuredDefault: {
37
+ provider: 'claude';
38
+ engine: 'sdk' | 'cli';
39
+ permissionMode: 'default' | 'acceptEdits' | 'plan' | 'bypassPermissions';
40
+ } | null;
41
+ taskCommandApproval: {
42
+ protocol: 'task_command_once_v1';
43
+ adapters: readonly {
44
+ provider: 'claude';
45
+ engine: 'sdk';
46
+ }[];
47
+ } | null;
48
+ }
49
+ export declare function parseHarnessSessionPolicyReport(value: unknown): HarnessSessionPolicyReport | null;
@@ -57,3 +57,47 @@ export function parseHarnessPermissionControls(value) {
57
57
  return null;
58
58
  }
59
59
  }
60
+ export function parseHarnessSessionPolicyReport(value) {
61
+ try {
62
+ if (!value || typeof value !== 'object' || Array.isArray(value))
63
+ return null;
64
+ const row = value;
65
+ if (row.version !== 1)
66
+ return null;
67
+ let configuredDefault = null;
68
+ let taskCommandApproval = null;
69
+ const config = row.configuredDefault;
70
+ if (config &&
71
+ typeof config === 'object' &&
72
+ !Array.isArray(config) &&
73
+ config.provider === 'claude' &&
74
+ ['sdk', 'cli'].includes(config.engine) &&
75
+ ['default', 'acceptEdits', 'plan', 'bypassPermissions'].includes(config.permissionMode)) {
76
+ configuredDefault = Object.freeze({
77
+ provider: 'claude',
78
+ engine: config.engine,
79
+ permissionMode: config.permissionMode,
80
+ });
81
+ }
82
+ const approval = row.taskCommandApproval;
83
+ if (approval &&
84
+ typeof approval === 'object' &&
85
+ !Array.isArray(approval) &&
86
+ approval.protocol === 'task_command_once_v1' &&
87
+ Array.isArray(approval.adapters) &&
88
+ approval.adapters.length === 1 &&
89
+ approval.adapters[0]?.provider === 'claude' &&
90
+ approval.adapters[0]?.engine === 'sdk') {
91
+ taskCommandApproval = Object.freeze({
92
+ protocol: 'task_command_once_v1',
93
+ adapters: Object.freeze([
94
+ Object.freeze({ provider: 'claude', engine: 'sdk' }),
95
+ ]),
96
+ });
97
+ }
98
+ return Object.freeze({ version: 1, configuredDefault, taskCommandApproval });
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skrr-ai/auth-core",
3
- "version": "0.4.30",
3
+ "version": "0.4.31",
4
4
  "main": "dist/cjs/index.js",
5
5
  "types": "dist/esm/index.d.ts",
6
6
  "exports": {