@skrr-ai/cli 0.1.118 → 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.
- package/dist/base-command.d.ts +1 -0
- package/dist/base-command.js +7 -1
- package/dist/commands/memory/versions/list.d.ts +2 -0
- package/dist/commands/memory/versions/list.js +3 -2
- package/dist/commands/memory/versions/show.d.ts +2 -0
- package/dist/commands/memory/versions/show.js +3 -2
- package/dist/lib/api-fetch.d.ts +4 -0
- package/dist/lib/api-fetch.js +25 -7
- package/dist/lib/memory-history-session.d.ts +14 -0
- package/dist/lib/memory-history-session.js +60 -0
- package/dist/lib/scoped-memory-command.d.ts +6 -0
- package/dist/lib/scoped-memory-command.js +34 -3
- package/dist/lib/task-runtime-headers.js +13 -3
- package/oclif.manifest.json +5 -1
- package/package.json +1 -1
package/dist/base-command.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export declare abstract class BaseCommand extends Command {
|
|
|
32
32
|
* acquiring unrelated account authority. Human invocations are unaffected.
|
|
33
33
|
*/
|
|
34
34
|
static autonomousCredentialPolicy: 'delegated' | 'bound-session';
|
|
35
|
+
static sessionReadPolicy?: 'memory-history-v1';
|
|
35
36
|
/** Add the owning agent's web detail link without overwriting resource URLs. */
|
|
36
37
|
protected withAgentContext<T>(agentId: string, value: T): T & {
|
|
37
38
|
agentUrl: string | null;
|
package/dist/base-command.js
CHANGED
|
@@ -53,6 +53,7 @@ const cli_id_1 = require("./lib/cli-id");
|
|
|
53
53
|
const keychain_2 = require("./lib/keychain");
|
|
54
54
|
const outbox_1 = require("./lib/outbox");
|
|
55
55
|
const node_adapter_1 = require("./lib/node-adapter");
|
|
56
|
+
const memory_history_session_1 = require("./lib/memory-history-session");
|
|
56
57
|
const migrate_1 = require("./lib/migrate");
|
|
57
58
|
const auth_core_init_1 = require("./lib/auth-core-init");
|
|
58
59
|
const credential_resolver_1 = require("./lib/credential-resolver");
|
|
@@ -125,6 +126,7 @@ class BaseCommand extends core_1.Command {
|
|
|
125
126
|
* acquiring unrelated account authority. Human invocations are unaffected.
|
|
126
127
|
*/
|
|
127
128
|
static autonomousCredentialPolicy = 'delegated';
|
|
129
|
+
static sessionReadPolicy;
|
|
128
130
|
/** Add the owning agent's web detail link without overwriting resource URLs. */
|
|
129
131
|
withAgentContext(agentId, value) {
|
|
130
132
|
const agentUrl = (0, web_url_1.agentUrlFrom)(this.cliConfig.baseURL, { id: agentId });
|
|
@@ -292,7 +294,11 @@ class BaseCommand extends core_1.Command {
|
|
|
292
294
|
// touching migration, Keychain/file auth, refresh, or daemon auto-broker
|
|
293
295
|
// state. The restricted session token (or a null credential when it is
|
|
294
296
|
// missing) is the only adapter authority exposed to the command.
|
|
295
|
-
|
|
297
|
+
const historySession = this.constructor.sessionReadPolicy === 'memory-history-v1' &&
|
|
298
|
+
(0, memory_history_session_1.hasMemoryHistoryRuntime)() ? (0, memory_history_session_1.memoryHistorySession)() : null;
|
|
299
|
+
let autonomousCredential = historySession
|
|
300
|
+
? { token: historySession.token, source: 'env-token', kind: (0, auth_core_1.classifyTokenKind)(historySession.token) }
|
|
301
|
+
: (0, credential_resolver_1.resolveTrustedAutonomousCredential)();
|
|
296
302
|
const boundSessionCommand = Boolean(autonomousCredential &&
|
|
297
303
|
this.constructor.autonomousCredentialPolicy === 'bound-session');
|
|
298
304
|
if ((0, delegated_cli_1.isDelegatedCliSession)() && !boundSessionCommand) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ScopedMemoryCommand } from '../../../lib/scoped-memory-command';
|
|
2
2
|
export default class MemoryVersionsList extends ScopedMemoryCommand {
|
|
3
|
+
static autonomousCredentialPolicy: "bound-session";
|
|
4
|
+
static sessionReadPolicy: "memory-history-v1";
|
|
3
5
|
static description: string;
|
|
4
6
|
static args: {
|
|
5
7
|
memory: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
@@ -4,14 +4,15 @@ const list_output_1 = require("../../../lib/list-output");
|
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const scoped_memory_command_1 = require("../../../lib/scoped-memory-command");
|
|
6
6
|
class MemoryVersionsList extends scoped_memory_command_1.ScopedMemoryCommand {
|
|
7
|
+
static autonomousCredentialPolicy = 'bound-session';
|
|
8
|
+
static sessionReadPolicy = 'memory-history-v1';
|
|
7
9
|
static description = 'List immutable versions for one Memory topic';
|
|
8
10
|
static args = { memory: core_1.Args.string({ required: true, ignoreStdin: true }) };
|
|
9
11
|
static flags = { ...scoped_memory_command_1.scopedMemoryFlags, limit: core_1.Flags.integer({ default: 100 }) };
|
|
10
12
|
async run() {
|
|
11
13
|
this.requireAuth();
|
|
12
14
|
const { args, flags } = await this.parse(MemoryVersionsList);
|
|
13
|
-
const
|
|
14
|
-
const result = await this.memoryApi(flags.agent, `/stores/${store.id}/versions${this.query(flags, { memoryId: args.memory, limit: flags.limit })}`);
|
|
15
|
+
const result = await this.memoryHistoryApi(flags, { memoryId: args.memory, limit: flags.limit });
|
|
15
16
|
if (flags.json)
|
|
16
17
|
return this.printJsonResult((0, list_output_1.listResult)(result, 'versions'));
|
|
17
18
|
for (const version of result.versions)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ScopedMemoryCommand } from '../../../lib/scoped-memory-command';
|
|
2
2
|
export default class MemoryVersionsShow extends ScopedMemoryCommand {
|
|
3
|
+
static autonomousCredentialPolicy: "bound-session";
|
|
4
|
+
static sessionReadPolicy: "memory-history-v1";
|
|
3
5
|
static description: string;
|
|
4
6
|
static args: {
|
|
5
7
|
id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
@@ -3,14 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const scoped_memory_command_1 = require("../../../lib/scoped-memory-command");
|
|
5
5
|
class MemoryVersionsShow extends scoped_memory_command_1.ScopedMemoryCommand {
|
|
6
|
+
static autonomousCredentialPolicy = 'bound-session';
|
|
7
|
+
static sessionReadPolicy = 'memory-history-v1';
|
|
6
8
|
static description = 'Show one immutable MemoryVersion';
|
|
7
9
|
static args = { id: core_1.Args.string({ required: true, ignoreStdin: true }) };
|
|
8
10
|
static flags = scoped_memory_command_1.scopedMemoryFlags;
|
|
9
11
|
async run() {
|
|
10
12
|
this.requireAuth();
|
|
11
13
|
const { args, flags } = await this.parse(MemoryVersionsShow);
|
|
12
|
-
const
|
|
13
|
-
const result = await this.memoryApi(flags.agent, `/stores/${store.id}/versions/${encodeURIComponent(args.id)}${this.query(flags)}`);
|
|
14
|
+
const result = await this.memoryHistoryApi(flags, { versionId: args.id });
|
|
14
15
|
this.print(result, flags.json, `${result.id} ${result.operation}\n${JSON.stringify(result.payloadSnapshot, null, 2)}`);
|
|
15
16
|
}
|
|
16
17
|
}
|
package/dist/lib/api-fetch.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export interface ApiFetchOptions {
|
|
|
5
5
|
body?: unknown;
|
|
6
6
|
/** Override credential resolution — useful for tests. */
|
|
7
7
|
credential?: ResolvedCredential;
|
|
8
|
+
/** Exact admitted-session Memory history GET; never uses owner credentials. */
|
|
9
|
+
sessionRead?: 'memory-history-v1';
|
|
8
10
|
/**
|
|
9
11
|
* Extra request headers. Added for `X-Idempotency-Key` (OSK-2806): the
|
|
10
12
|
* shared data-provider transport takes no per-call options, so a replayed
|
|
@@ -37,6 +39,8 @@ export declare class ApiFetchError extends Error {
|
|
|
37
39
|
* attributed by guesswork — see `node-adapter.ts`.
|
|
38
40
|
*/
|
|
39
41
|
readonly requestId?: string;
|
|
42
|
+
/** Client recovery guidance; never changes the server's refusal status/code. */
|
|
43
|
+
recoveryHint?: string;
|
|
40
44
|
/** Milliseconds the server asked us to wait (`Retry-After`) on a 429. */
|
|
41
45
|
retryAfterMs?: number;
|
|
42
46
|
/**
|
package/dist/lib/api-fetch.js
CHANGED
|
@@ -49,6 +49,7 @@ const workspace_header_1 = require("./workspace-header");
|
|
|
49
49
|
const task_runtime_headers_1 = require("./task-runtime-headers");
|
|
50
50
|
const non_json_response_1 = require("./non-json-response");
|
|
51
51
|
const rate_limit_retry_1 = require("./rate-limit-retry");
|
|
52
|
+
const memory_history_session_1 = require("./memory-history-session");
|
|
52
53
|
class ApiFetchError extends Error {
|
|
53
54
|
status;
|
|
54
55
|
body;
|
|
@@ -65,6 +66,8 @@ class ApiFetchError extends Error {
|
|
|
65
66
|
* attributed by guesswork — see `node-adapter.ts`.
|
|
66
67
|
*/
|
|
67
68
|
requestId;
|
|
69
|
+
/** Client recovery guidance; never changes the server's refusal status/code. */
|
|
70
|
+
recoveryHint;
|
|
68
71
|
/** Milliseconds the server asked us to wait (`Retry-After`) on a 429. */
|
|
69
72
|
retryAfterMs;
|
|
70
73
|
/**
|
|
@@ -92,7 +95,11 @@ exports.ApiFetchError = ApiFetchError;
|
|
|
92
95
|
* `err.status` / `err.code`.
|
|
93
96
|
*/
|
|
94
97
|
async function apiFetch(pathOrUrl, opts = {}) {
|
|
95
|
-
const
|
|
98
|
+
const historyRead = opts.sessionRead
|
|
99
|
+
? (0, memory_history_session_1.bindMemoryHistoryRead)(pathOrUrl, opts.method ?? 'GET') : null;
|
|
100
|
+
const sessionCredential = historyRead
|
|
101
|
+
? { token: historyRead.token, source: 'env-token', kind: (0, credential_resolver_1.kindOf)(historyRead.token) }
|
|
102
|
+
: (0, credential_resolver_1.resolveTrustedAutonomousCredential)();
|
|
96
103
|
// A few Agent-only APIs (message/voice/telephony intents) deliberately pass
|
|
97
104
|
// the daemon-issued session token explicitly. Keep those endpoints bound to
|
|
98
105
|
// the session principal; every other CLI API call in a delegated Agent uses
|
|
@@ -104,17 +111,17 @@ async function apiFetch(pathOrUrl, opts = {}) {
|
|
|
104
111
|
// equality check against it silently fail open.
|
|
105
112
|
const envSessionToken = process.env.OVERSKY_SESSION_TOKEN?.trim() || null;
|
|
106
113
|
const explicitSessionCredential = Boolean(opts.credential?.token && envSessionToken && opts.credential.token === envSessionToken);
|
|
107
|
-
const delegatedCli = (0, delegated_cli_1.isDelegatedCliSession)() && !explicitSessionCredential;
|
|
114
|
+
const delegatedCli = (0, delegated_cli_1.isDelegatedCliSession)() && !explicitSessionCredential && !historyRead;
|
|
108
115
|
const autonomousCredential = delegatedCli
|
|
109
116
|
? await (0, delegated_cli_1.fetchDelegatedCliCredential)()
|
|
110
117
|
: sessionCredential;
|
|
111
118
|
const baseURL = autonomousCredential ? trustedAutonomousBaseUrl() : (0, config_1.loadConfig)().baseURL;
|
|
112
119
|
const method = opts.method ?? 'GET';
|
|
113
|
-
const url = /^https?:\/\//i.test(pathOrUrl)
|
|
120
|
+
const url = historyRead?.url ?? (/^https?:\/\//i.test(pathOrUrl)
|
|
114
121
|
? autonomousCredential
|
|
115
122
|
? assertTrustedAutonomousUrl(pathOrUrl, baseURL)
|
|
116
123
|
: pathOrUrl
|
|
117
|
-
: `${baseURL.replace(/\/+$/, '')}${pathOrUrl.startsWith('/') ? '' : '/'}${pathOrUrl}
|
|
124
|
+
: `${baseURL.replace(/\/+$/, '')}${pathOrUrl.startsWith('/') ? '' : '/'}${pathOrUrl}`);
|
|
118
125
|
// A FormData body passes through untouched — fetch sets the multipart
|
|
119
126
|
// boundary itself, and stringifying would destroy it. Upload commands
|
|
120
127
|
// (tasks attachments) need this on the session-scoped path, which is not
|
|
@@ -190,13 +197,16 @@ async function apiFetch(pathOrUrl, opts = {}) {
|
|
|
190
197
|
// OSK-12020 — a fresh proof per attempt for a DPoP-bound hand-off token
|
|
191
198
|
// (the 401 retry below re-enters here and gets its own). No-op otherwise.
|
|
192
199
|
Object.assign(headers, await (0, dpop_auth_1.dpopHeadersFor)(credential.token, method, url));
|
|
193
|
-
const sent = (0, task_runtime_headers_1.withTaskRuntimeHeaders)((0, workspace_header_1.withWorkspaceHeader)(headers
|
|
200
|
+
const sent = (0, task_runtime_headers_1.withTaskRuntimeHeaders)((0, workspace_header_1.withWorkspaceHeader)(headers, historyRead ? {
|
|
201
|
+
OVERSKY_AUTONOMOUS_SESSION: '1',
|
|
202
|
+
OVERSKY_SESSION_WORKSPACE_ID: process.env.OVERSKY_SESSION_WORKSPACE_ID,
|
|
203
|
+
} : process.env), url, method);
|
|
194
204
|
// E6: a 429 on a GET or an idempotency-keyed write is retried (bounded,
|
|
195
205
|
// jittered, honouring Retry-After). The last 429's wait is kept for the error.
|
|
196
206
|
const { response, retryAfterMs } = await (0, rate_limit_retry_1.fetchWithRateLimitRetry)(() => fetch(url, {
|
|
197
207
|
method,
|
|
198
208
|
headers: sent,
|
|
199
|
-
...(0, task_runtime_headers_1.taskRuntimeRedirectOptions)(sent),
|
|
209
|
+
...(historyRead ? { redirect: 'error' } : (0, task_runtime_headers_1.taskRuntimeRedirectOptions)(sent)),
|
|
200
210
|
body,
|
|
201
211
|
...(opts.signal ? { signal: opts.signal } : {}),
|
|
202
212
|
}), { method, headers: sent, signal: opts.signal });
|
|
@@ -218,6 +228,7 @@ async function apiFetch(pathOrUrl, opts = {}) {
|
|
|
218
228
|
}
|
|
219
229
|
}
|
|
220
230
|
else if (res.status === 401 &&
|
|
231
|
+
!historyRead &&
|
|
221
232
|
(0, daemonBroker_1.isBrokeredHandoffToken)(cred.token) &&
|
|
222
233
|
!isAuthEndpoint(pathOrUrl)) {
|
|
223
234
|
// Broker-mode hand-off credential: nothing was persisted, so there is no
|
|
@@ -279,7 +290,14 @@ async function apiFetch(pathOrUrl, opts = {}) {
|
|
|
279
290
|
catch {
|
|
280
291
|
/* ignore body read failures */
|
|
281
292
|
}
|
|
282
|
-
const
|
|
293
|
+
const unsupportedHistory = historyRead && res.status === 404 &&
|
|
294
|
+
(!code || ['API_ROUTE_NOT_FOUND', 'ROUTE_NOT_FOUND'].includes(code));
|
|
295
|
+
const error = new ApiFetchError(unsupportedHistory
|
|
296
|
+
? 'This server does not support session-scoped Memory history. Upgrade the server before retrying; owner-credential fallback is disabled.'
|
|
297
|
+
: `HTTP ${res.status} ${res.statusText} — ${method} ${pathOrUrl}`, res.status, text, unsupportedHistory ? 'MEMORY_HISTORY_API_UNSUPPORTED' : code, method, res.headers.get('x-request-id') || undefined);
|
|
298
|
+
if (historyRead && res.status === 401) {
|
|
299
|
+
error.recoveryHint = 'Reconnect the managed Agent session and retry. If the refusal persists, check that this server supports session-scoped Memory history. Owner-credential fallback is disabled.';
|
|
300
|
+
}
|
|
283
301
|
if (brokerFailure)
|
|
284
302
|
error.brokerFailure = brokerFailure;
|
|
285
303
|
if (res.status === 429 && lastRetryAfterMs !== undefined)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type RuntimeEnv = Record<string, string | undefined>;
|
|
2
|
+
export declare function hasMemoryHistoryRuntime(env?: RuntimeEnv): boolean;
|
|
3
|
+
/** Only this explicit read contract may select a primary session credential. */
|
|
4
|
+
export declare function bindMemoryHistoryRead(pathOrUrl: string, method: string, env?: RuntimeEnv): {
|
|
5
|
+
url: string;
|
|
6
|
+
origin: string;
|
|
7
|
+
token: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function memoryHistorySession(env?: RuntimeEnv): {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
token: string;
|
|
12
|
+
server: URL;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasMemoryHistoryRuntime = hasMemoryHistoryRuntime;
|
|
4
|
+
exports.bindMemoryHistoryRead = bindMemoryHistoryRead;
|
|
5
|
+
exports.memoryHistorySession = memoryHistorySession;
|
|
6
|
+
function hasMemoryHistoryRuntime(env = process.env) {
|
|
7
|
+
// Interactive SDK children can carry the daemon's session tuple without an
|
|
8
|
+
// AutonomousRun or the wrapper's autonomous marker. Partial tuples must not
|
|
9
|
+
// silently turn a managed history read into an owner-account read.
|
|
10
|
+
return env.OVERSKY_AUTONOMOUS_SESSION === '1' ||
|
|
11
|
+
Boolean(env.OVERSKY_SESSION_ID?.trim() || env.OVERSKY_SESSION_TOKEN?.trim());
|
|
12
|
+
}
|
|
13
|
+
function refusal(message, code, status) {
|
|
14
|
+
return Object.assign(new Error(message), { code, status });
|
|
15
|
+
}
|
|
16
|
+
/** Only this explicit read contract may select a primary session credential. */
|
|
17
|
+
function bindMemoryHistoryRead(pathOrUrl, method, env = process.env) {
|
|
18
|
+
const { sessionId, token, server } = memoryHistorySession(env);
|
|
19
|
+
let target;
|
|
20
|
+
try {
|
|
21
|
+
target = new URL(pathOrUrl, server);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw refusal('Memory history requires the bound session server. Start a new managed Agent session.', 'MEMORY_RUNTIME_ORIGIN_MISMATCH', 403);
|
|
25
|
+
}
|
|
26
|
+
if (target.origin !== server.origin || target.username || target.password) {
|
|
27
|
+
throw refusal('Memory history cannot send its session credential to another server.', 'MEMORY_RUNTIME_ORIGIN_MISMATCH', 403);
|
|
28
|
+
}
|
|
29
|
+
const route = /^\/api\/agentic\/sessions\/([^/]+)\/memory\/agents\/([^/]+)\/versions(?:\/([^/]+))?$/.exec(target.pathname);
|
|
30
|
+
let selectedSession;
|
|
31
|
+
let selectedAgent;
|
|
32
|
+
try {
|
|
33
|
+
selectedSession = route ? decodeURIComponent(route[1]) : undefined;
|
|
34
|
+
selectedAgent = route ? decodeURIComponent(route[2]) : undefined;
|
|
35
|
+
}
|
|
36
|
+
catch { /* malformed encoded selectors are refused below */ }
|
|
37
|
+
if (method.toUpperCase() !== 'GET' || !route || selectedSession !== sessionId ||
|
|
38
|
+
(env.OVERSKY_SESSION_AGENT_ID?.trim() && selectedAgent !== env.OVERSKY_SESSION_AGENT_ID.trim())) {
|
|
39
|
+
throw refusal('Memory history must target this admitted Agent session. Start a session for the selected Agent.', 'MEMORY_RUNTIME_SCOPE_MISMATCH', 403);
|
|
40
|
+
}
|
|
41
|
+
return { url: target.toString(), origin: server.origin, token };
|
|
42
|
+
}
|
|
43
|
+
function memoryHistorySession(env = process.env) {
|
|
44
|
+
const sessionId = env.OVERSKY_SESSION_ID?.trim();
|
|
45
|
+
const token = env.OVERSKY_SESSION_TOKEN?.trim();
|
|
46
|
+
if (!sessionId || !token?.startsWith('agst_')) {
|
|
47
|
+
throw refusal('Memory history requires the bound Agent session credential. Start a managed Agent session; owner credentials cannot replace it.', 'MEMORY_RUNTIME_CREDENTIAL_MISSING', 401);
|
|
48
|
+
}
|
|
49
|
+
let trusted;
|
|
50
|
+
try {
|
|
51
|
+
trusted = new URL(env.OVERSKY_SERVER_URL || '');
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
throw refusal('Memory history requires the bound session server. Start a new managed Agent session.', 'MEMORY_RUNTIME_ORIGIN_MISMATCH', 403);
|
|
55
|
+
}
|
|
56
|
+
if (!['https:', 'http:'].includes(trusted.protocol) || trusted.username || trusted.password) {
|
|
57
|
+
throw refusal('Memory history cannot send its session credential to another server.', 'MEMORY_RUNTIME_ORIGIN_MISMATCH', 403);
|
|
58
|
+
}
|
|
59
|
+
return { sessionId, token, server: trusted };
|
|
60
|
+
}
|
|
@@ -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)(
|
|
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) {
|
|
@@ -12,7 +12,7 @@ function withTaskRuntimeHeaders(headers, requestUrl, method, env = process.env)
|
|
|
12
12
|
const sent = { ...headers };
|
|
13
13
|
// A companion credential never travels on a read, another API surface,
|
|
14
14
|
// or a caller-selected header. Preserve existing nonsecret correlation
|
|
15
|
-
// hints outside
|
|
15
|
+
// hints outside Task authorship mutations for other APIs' provenance checks.
|
|
16
16
|
for (const key of Object.keys(sent)) {
|
|
17
17
|
if (key.toLowerCase() === 'x-oversky-session-authorization')
|
|
18
18
|
delete sent[key];
|
|
@@ -31,7 +31,17 @@ function withTaskRuntimeHeaders(headers, requestUrl, method, env = process.env)
|
|
|
31
31
|
throw refuseOrigin();
|
|
32
32
|
return sent;
|
|
33
33
|
}
|
|
34
|
-
|
|
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)
|
|
35
45
|
return sent;
|
|
36
46
|
const names = new Set(['x-oversky-agent-id', 'x-oversky-session-id', 'x-oversky-autonomous-run-id']);
|
|
37
47
|
for (const key of Object.keys(sent))
|
|
@@ -50,7 +60,7 @@ function withTaskRuntimeHeaders(headers, requestUrl, method, env = process.env)
|
|
|
50
60
|
throw refuseOrigin();
|
|
51
61
|
}
|
|
52
62
|
// The companion credential only travels to the managed runtime's server
|
|
53
|
-
// and only to
|
|
63
|
+
// and only to Task mutations/materialization. Bound routes use it as Authorization.
|
|
54
64
|
if (target.origin !== trusted.origin)
|
|
55
65
|
throw refuseOrigin();
|
|
56
66
|
if (!sessionId || !token) {
|
package/oclif.manifest.json
CHANGED
|
@@ -73568,6 +73568,8 @@
|
|
|
73568
73568
|
"pluginName": "@skrr-ai/cli",
|
|
73569
73569
|
"pluginType": "core",
|
|
73570
73570
|
"strict": true,
|
|
73571
|
+
"autonomousCredentialPolicy": "bound-session",
|
|
73572
|
+
"sessionReadPolicy": "memory-history-v1",
|
|
73571
73573
|
"isESM": false,
|
|
73572
73574
|
"relativePath": [
|
|
73573
73575
|
"dist",
|
|
@@ -73928,6 +73930,8 @@
|
|
|
73928
73930
|
"pluginName": "@skrr-ai/cli",
|
|
73929
73931
|
"pluginType": "core",
|
|
73930
73932
|
"strict": true,
|
|
73933
|
+
"autonomousCredentialPolicy": "bound-session",
|
|
73934
|
+
"sessionReadPolicy": "memory-history-v1",
|
|
73931
73935
|
"isESM": false,
|
|
73932
73936
|
"relativePath": [
|
|
73933
73937
|
"dist",
|
|
@@ -138173,5 +138177,5 @@
|
|
|
138173
138177
|
]
|
|
138174
138178
|
}
|
|
138175
138179
|
},
|
|
138176
|
-
"version": "0.1.
|
|
138180
|
+
"version": "0.1.119"
|
|
138177
138181
|
}
|