@yu_robotics/remote-cli 1.6.156 → 1.6.165

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 (66) hide show
  1. package/README.md +14 -3
  2. package/dist/client/MessageHandler.d.ts +4 -5
  3. package/dist/client/MessageHandler.d.ts.map +1 -1
  4. package/dist/client/MessageHandler.js +102 -9
  5. package/dist/client/MessageHandler.js.map +1 -1
  6. package/dist/commands/config.d.ts.map +1 -1
  7. package/dist/commands/config.js +15 -0
  8. package/dist/commands/config.js.map +1 -1
  9. package/dist/commands/session.d.ts +17 -0
  10. package/dist/commands/session.d.ts.map +1 -0
  11. package/dist/commands/session.js +37 -0
  12. package/dist/commands/session.js.map +1 -0
  13. package/dist/delegation/GitCheckpoint.d.ts +1 -0
  14. package/dist/delegation/GitCheckpoint.d.ts.map +1 -1
  15. package/dist/delegation/GitCheckpoint.js +15 -4
  16. package/dist/delegation/GitCheckpoint.js.map +1 -1
  17. package/dist/executor/IExecutor.d.ts +21 -0
  18. package/dist/executor/IExecutor.d.ts.map +1 -1
  19. package/dist/executor/PiExecutor.d.ts +26 -1
  20. package/dist/executor/PiExecutor.d.ts.map +1 -1
  21. package/dist/executor/PiExecutor.js +256 -15
  22. package/dist/executor/PiExecutor.js.map +1 -1
  23. package/dist/index.js +19 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/maintenance/release-notes.json +1 -1
  26. package/dist/session/SessionCatalog.d.ts +76 -0
  27. package/dist/session/SessionCatalog.d.ts.map +1 -0
  28. package/dist/session/SessionCatalog.js +197 -0
  29. package/dist/session/SessionCatalog.js.map +1 -0
  30. package/dist/session/SessionControl.d.ts +56 -0
  31. package/dist/session/SessionControl.d.ts.map +1 -0
  32. package/dist/session/SessionControl.js +262 -0
  33. package/dist/session/SessionControl.js.map +1 -0
  34. package/dist/session/SessionLocalInfo.d.ts +48 -0
  35. package/dist/session/SessionLocalInfo.d.ts.map +1 -0
  36. package/dist/session/SessionLocalInfo.js +145 -0
  37. package/dist/session/SessionLocalInfo.js.map +1 -0
  38. package/dist/session/SessionMenu.d.ts +102 -0
  39. package/dist/session/SessionMenu.d.ts.map +1 -0
  40. package/dist/session/SessionMenu.js +144 -0
  41. package/dist/session/SessionMenu.js.map +1 -0
  42. package/dist/session/SessionResumeService.d.ts +38 -0
  43. package/dist/session/SessionResumeService.d.ts.map +1 -0
  44. package/dist/session/SessionResumeService.js +147 -0
  45. package/dist/session/SessionResumeService.js.map +1 -0
  46. package/dist/session/adapters/ClaudeSessionCatalog.d.ts +10 -0
  47. package/dist/session/adapters/ClaudeSessionCatalog.d.ts.map +1 -0
  48. package/dist/session/adapters/ClaudeSessionCatalog.js +156 -0
  49. package/dist/session/adapters/ClaudeSessionCatalog.js.map +1 -0
  50. package/dist/session/adapters/CodexSessionCatalog.d.ts +9 -0
  51. package/dist/session/adapters/CodexSessionCatalog.d.ts.map +1 -0
  52. package/dist/session/adapters/CodexSessionCatalog.js +214 -0
  53. package/dist/session/adapters/CodexSessionCatalog.js.map +1 -0
  54. package/dist/session/adapters/PiSessionCatalog.d.ts +52 -0
  55. package/dist/session/adapters/PiSessionCatalog.d.ts.map +1 -0
  56. package/dist/session/adapters/PiSessionCatalog.js +252 -0
  57. package/dist/session/adapters/PiSessionCatalog.js.map +1 -0
  58. package/dist/session/adapters/index.d.ts +13 -0
  59. package/dist/session/adapters/index.d.ts.map +1 -0
  60. package/dist/session/adapters/index.js +17 -0
  61. package/dist/session/adapters/index.js.map +1 -0
  62. package/dist/types/config.d.ts +11 -0
  63. package/dist/types/config.d.ts.map +1 -1
  64. package/dist/types/config.js +3 -0
  65. package/dist/types/config.js.map +1 -1
  66. package/package.json +1 -1
@@ -0,0 +1,102 @@
1
+ import type { BackendKey } from '../types/config';
2
+ import { type SessionCatalog } from './SessionCatalog';
3
+ /** Initial UI limits from the session handoff specification. */
4
+ export declare const SESSION_MENU_PAGE_SIZE = 8;
5
+ export declare const SESSION_MENU_TTL_MS: number;
6
+ /** Bounded number of live snapshots kept in memory per CLI process. */
7
+ export declare const SESSION_MENU_LIMIT = 64;
8
+ /** Who may use a snapshot: the original requester, thread and workspace. */
9
+ export interface SessionMenuScope {
10
+ openId?: string;
11
+ threadId: string;
12
+ workingDirectory: string;
13
+ }
14
+ export interface SessionMenuSnapshot {
15
+ token: string;
16
+ backend: BackendKey;
17
+ scope: SessionMenuScope;
18
+ catalog: SessionCatalog;
19
+ /** Sanitized search text that produced this snapshot, if any. */
20
+ query?: string;
21
+ createdAt: number;
22
+ }
23
+ export type SessionMenuCommand = {
24
+ kind: 'page';
25
+ token: string;
26
+ page: number;
27
+ } | {
28
+ kind: 'search';
29
+ token: string;
30
+ text: string;
31
+ } | {
32
+ kind: 'select';
33
+ token: string;
34
+ row: number;
35
+ } | {
36
+ kind: 'id';
37
+ id: string;
38
+ } | {
39
+ kind: 'confirm' | 'cancel';
40
+ token: string;
41
+ } | {
42
+ kind: 'invalid';
43
+ error: string;
44
+ };
45
+ /** `/session` subcommands; a bare `/session` is the status view. */
46
+ export type SessionBindingCommand = {
47
+ kind: 'status';
48
+ } | {
49
+ kind: 'detach';
50
+ } | {
51
+ kind: 'confirm' | 'cancel';
52
+ token: string;
53
+ } | {
54
+ kind: 'invalid';
55
+ error: string;
56
+ };
57
+ /** A pending, single-use confirmation. Locators stay local to the CLI. */
58
+ export interface SessionConfirmation {
59
+ token: string;
60
+ action: 'resume' | 'detach';
61
+ backend: BackendKey;
62
+ scope: SessionMenuScope;
63
+ sessionId: string;
64
+ /** Adapter revision captured at selection; a later change requires reselection. */
65
+ revision?: string;
66
+ /** Session this confirmation replaces on the same thread, when it had one. */
67
+ replaces?: string;
68
+ createdAt: number;
69
+ }
70
+ /**
71
+ * Parse snapshot-bound chooser commands. Returns undefined for anything that is
72
+ * not a menu command so the regular session gate keeps handling it.
73
+ */
74
+ export declare function parseSessionMenuCommand(content: string): SessionMenuCommand | undefined;
75
+ /** Parse `/session [detach|confirm <token>|cancel <token>]`. */
76
+ export declare function parseSessionBindingCommand(content: string): SessionBindingCommand | undefined;
77
+ /**
78
+ * In-memory, read-only chooser snapshots. Tokens are unguessable, expire after
79
+ * five minutes, and only resolve for the original requester, thread and
80
+ * workspace, so an old list never redirects to another context. Snapshots never
81
+ * authorize a native action; they only fix the rows a user is paging through.
82
+ */
83
+ export declare class SessionMenuStore {
84
+ private readonly now;
85
+ private readonly snapshots;
86
+ private readonly confirmations;
87
+ constructor(now?: () => number);
88
+ create(backend: BackendKey, scope: SessionMenuScope, catalog: SessionCatalog, query?: string): SessionMenuSnapshot;
89
+ /** Resolve a token for the same scope; any mismatch is indistinguishable from expiry. */
90
+ get(token: string, scope: SessionMenuScope): SessionMenuSnapshot | undefined;
91
+ /** Search returns a new snapshot; the original rows and token stay unchanged. */
92
+ search(token: string, scope: SessionMenuScope, text: string): SessionMenuSnapshot | undefined;
93
+ get size(): number;
94
+ /** Issue a confirmation; any earlier pending confirmation for the same thread is superseded. */
95
+ confirm(pending: Omit<SessionConfirmation, 'token' | 'createdAt'>): SessionConfirmation;
96
+ /** Single use: a matching confirmation is removed whether it is confirmed or cancelled. */
97
+ take(token: string, scope: SessionMenuScope, action: SessionConfirmation['action']): SessionConfirmation | undefined;
98
+ get pendingConfirmations(): number;
99
+ private evict;
100
+ }
101
+ export declare function sessionMenuPageCount(snapshot: SessionMenuSnapshot): number;
102
+ //# sourceMappingURL=SessionMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionMenu.d.ts","sourceRoot":"","sources":["../../src/session/SessionMenu.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionMenuStore = exports.SESSION_MENU_LIMIT = exports.SESSION_MENU_TTL_MS = exports.SESSION_MENU_PAGE_SIZE = void 0;
4
+ exports.parseSessionMenuCommand = parseSessionMenuCommand;
5
+ exports.parseSessionBindingCommand = parseSessionBindingCommand;
6
+ exports.sessionMenuPageCount = sessionMenuPageCount;
7
+ const crypto_1 = require("crypto");
8
+ const SessionCatalog_1 = require("./SessionCatalog");
9
+ /** Initial UI limits from the session handoff specification. */
10
+ exports.SESSION_MENU_PAGE_SIZE = 8;
11
+ exports.SESSION_MENU_TTL_MS = 5 * 60 * 1000;
12
+ /** Bounded number of live snapshots kept in memory per CLI process. */
13
+ exports.SESSION_MENU_LIMIT = 64;
14
+ const TOKEN = /^[A-Za-z0-9_-]{12}$/;
15
+ const NATIVE_ID = /^[A-Za-z0-9._:-]{1,128}$/;
16
+ /**
17
+ * Parse snapshot-bound chooser commands. Returns undefined for anything that is
18
+ * not a menu command so the regular session gate keeps handling it.
19
+ */
20
+ function parseSessionMenuCommand(content) {
21
+ const parts = content.trim().split(/\s+/);
22
+ if (!/^\/(?:resume|r)$/i.test(parts[0]) || !['page', 'search', 'select', 'id', 'confirm', 'cancel'].includes(parts[1]))
23
+ return undefined;
24
+ if (parts[1] === 'id') {
25
+ if (parts.length !== 3 || !NATIVE_ID.test(parts[2]))
26
+ return { kind: 'invalid', error: 'Expected /resume id <native-session-id>.' };
27
+ return { kind: 'id', id: parts[2] };
28
+ }
29
+ const token = parts[2];
30
+ if (!token || !TOKEN.test(token))
31
+ return { kind: 'invalid', error: 'Expected a menu token from a /resume list.' };
32
+ if (parts[1] === 'confirm' || parts[1] === 'cancel') {
33
+ if (parts.length !== 3)
34
+ return { kind: 'invalid', error: `Expected /resume ${parts[1]} <token>.` };
35
+ return { kind: parts[1], token };
36
+ }
37
+ if (parts[1] === 'select') {
38
+ if (parts.length !== 4 || !/^[1-9]\d{0,3}$/.test(parts[3]))
39
+ return { kind: 'invalid', error: 'Expected /resume select <menu-token> <row-number>.' };
40
+ return { kind: 'select', token, row: Number(parts[3]) };
41
+ }
42
+ if (parts[1] === 'page') {
43
+ if (parts.length !== 4 || !/^[1-9]\d{0,3}$/.test(parts[3]))
44
+ return { kind: 'invalid', error: 'Expected /resume page <menu-token> <page-number>.' };
45
+ return { kind: 'page', token, page: Number(parts[3]) };
46
+ }
47
+ const text = (0, SessionCatalog_1.safeSessionLabel)(parts.slice(3).join(' '));
48
+ if (!text)
49
+ return { kind: 'invalid', error: 'Expected /resume search <menu-token> <text>.' };
50
+ return { kind: 'search', token, text };
51
+ }
52
+ /** Parse `/session [detach|confirm <token>|cancel <token>]`. */
53
+ function parseSessionBindingCommand(content) {
54
+ const parts = content.trim().split(/\s+/);
55
+ if (!/^\/session$/i.test(parts[0]))
56
+ return undefined;
57
+ if (parts.length === 1)
58
+ return { kind: 'status' };
59
+ if (parts[1] === 'detach' && parts.length === 2)
60
+ return { kind: 'detach' };
61
+ if ((parts[1] === 'confirm' || parts[1] === 'cancel') && parts.length === 3 && TOKEN.test(parts[2]))
62
+ return { kind: parts[1], token: parts[2] };
63
+ return { kind: 'invalid', error: 'Expected /session, /session detach, /session confirm <token> or /session cancel <token>.' };
64
+ }
65
+ function sameScope(a, b) {
66
+ return a.openId === b.openId && a.threadId === b.threadId && a.workingDirectory === b.workingDirectory;
67
+ }
68
+ /**
69
+ * In-memory, read-only chooser snapshots. Tokens are unguessable, expire after
70
+ * five minutes, and only resolve for the original requester, thread and
71
+ * workspace, so an old list never redirects to another context. Snapshots never
72
+ * authorize a native action; they only fix the rows a user is paging through.
73
+ */
74
+ class SessionMenuStore {
75
+ now;
76
+ snapshots = new Map();
77
+ confirmations = new Map();
78
+ constructor(now = Date.now) {
79
+ this.now = now;
80
+ }
81
+ create(backend, scope, catalog, query) {
82
+ this.evict();
83
+ const token = (0, crypto_1.randomBytes)(9).toString('base64url');
84
+ const snapshot = { token, backend, scope: { ...scope }, catalog, query, createdAt: this.now() };
85
+ this.snapshots.set(token, snapshot);
86
+ while (this.snapshots.size > exports.SESSION_MENU_LIMIT)
87
+ this.snapshots.delete(this.snapshots.keys().next().value);
88
+ return snapshot;
89
+ }
90
+ /** Resolve a token for the same scope; any mismatch is indistinguishable from expiry. */
91
+ get(token, scope) {
92
+ this.evict();
93
+ const snapshot = this.snapshots.get(token);
94
+ return snapshot && sameScope(snapshot.scope, scope) ? snapshot : undefined;
95
+ }
96
+ /** Search returns a new snapshot; the original rows and token stay unchanged. */
97
+ search(token, scope, text) {
98
+ const source = this.get(token, scope);
99
+ if (!source)
100
+ return undefined;
101
+ const needle = text.toLowerCase();
102
+ const candidates = source.catalog.candidates.filter(item => item.id.toLowerCase().includes(needle) || (item.name ?? '').toLowerCase().includes(needle));
103
+ return this.create(source.backend, scope, { ...source.catalog, candidates }, text);
104
+ }
105
+ get size() { return this.snapshots.size; }
106
+ /** Issue a confirmation; any earlier pending confirmation for the same thread is superseded. */
107
+ confirm(pending) {
108
+ this.evict();
109
+ for (const [token, existing] of this.confirmations) {
110
+ if (existing.scope.threadId === pending.scope.threadId)
111
+ this.confirmations.delete(token);
112
+ }
113
+ const confirmation = { ...pending, scope: { ...pending.scope },
114
+ token: (0, crypto_1.randomBytes)(9).toString('base64url'), createdAt: this.now() };
115
+ this.confirmations.set(confirmation.token, confirmation);
116
+ while (this.confirmations.size > exports.SESSION_MENU_LIMIT)
117
+ this.confirmations.delete(this.confirmations.keys().next().value);
118
+ return confirmation;
119
+ }
120
+ /** Single use: a matching confirmation is removed whether it is confirmed or cancelled. */
121
+ take(token, scope, action) {
122
+ this.evict();
123
+ const confirmation = this.confirmations.get(token);
124
+ if (!confirmation || confirmation.action !== action || !sameScope(confirmation.scope, scope))
125
+ return undefined;
126
+ this.confirmations.delete(token);
127
+ return confirmation;
128
+ }
129
+ get pendingConfirmations() { return this.confirmations.size; }
130
+ evict() {
131
+ const cutoff = this.now() - exports.SESSION_MENU_TTL_MS;
132
+ for (const [token, snapshot] of this.snapshots)
133
+ if (snapshot.createdAt <= cutoff)
134
+ this.snapshots.delete(token);
135
+ for (const [token, pending] of this.confirmations)
136
+ if (pending.createdAt <= cutoff)
137
+ this.confirmations.delete(token);
138
+ }
139
+ }
140
+ exports.SessionMenuStore = SessionMenuStore;
141
+ function sessionMenuPageCount(snapshot) {
142
+ return Math.max(1, Math.ceil(snapshot.catalog.candidates.length / exports.SESSION_MENU_PAGE_SIZE));
143
+ }
144
+ //# sourceMappingURL=SessionMenu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionMenu.js","sourceRoot":"","sources":["../../src/session/SessionMenu.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { BackendKey } from '../types/config';
2
+ import type { SessionCatalog } from './SessionCatalog';
3
+ export declare const SESSION_BACKENDS: readonly ["claude", "codex", "agy", "opencode", "kimi", "zcode", "pi", "dsh"];
4
+ export interface ResumeGateResult {
5
+ success: false;
6
+ code: 'sharing_disabled' | 'invalid_session_configuration' | 'native_capability_unavailable' | 'invalid_session_command';
7
+ error: string;
8
+ }
9
+ /** User-facing backend name. */
10
+ export declare function sessionBackendLabel(backend: BackendKey): string;
11
+ /** Backends whose strict native resume is verified and implemented. */
12
+ export declare const NATIVE_RESUME_BACKENDS: readonly BackendKey[];
13
+ /** Validate the opt-in without resetting unrelated configuration on malformed input. */
14
+ export declare function validateSessionSharing(value: unknown): string | undefined;
15
+ /** Backends whose read-only catalog has been implemented; connecting remains unavailable. */
16
+ export declare const CATALOG_BACKENDS: readonly BackendKey[];
17
+ /** Optional paging view for a tokenized snapshot. */
18
+ export interface CatalogView {
19
+ token: string;
20
+ page: number;
21
+ pageSize: number;
22
+ query?: string;
23
+ /** Rows can be selected for a confirmed native connection. */
24
+ connectable?: boolean;
25
+ }
26
+ /** Read-only menu rendering shared by the implemented catalog backends. */
27
+ export declare function formatCatalog(catalog: SessionCatalog, backend: BackendKey, view?: CatalogView): string;
28
+ /** Re-check local opt-in for a backend without parsing a command (menu actions). */
29
+ export declare function sessionSharingDenied(backend: BackendKey, configuration: unknown): ResumeGateResult | undefined;
30
+ /** Backwards-compatible alias for the Pi-only renderer used by earlier releases. */
31
+ export declare function formatPiCatalog(catalog: SessionCatalog): string;
32
+ /** Return the target backend for a bare chooser request, or undefined for other session controls. */
33
+ export declare function catalogTarget(content: string, currentBackend: BackendKey): BackendKey | undefined;
34
+ /** Recognize session controls before executor construction or slash passthrough. */
35
+ export declare function isSessionControl(content: string): boolean;
36
+ /** Fail closed until discovery, ownership, preservation and native identity are certified. */
37
+ export declare function sessionResumeGate(content: string, currentBackend: BackendKey, configuration: unknown): ResumeGateResult;
38
+ //# sourceMappingURL=SessionResumeService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionResumeService.d.ts","sourceRoot":"","sources":["../../src/session/SessionResumeService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwB,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,eAAO,MAAM,gBAAgB,+EAAgF,CAAC;AAE9G,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,KAAK,CAAC;IACf,IAAI,EAAE,kBAAkB,GAAG,+BAA+B,GAAG,+BAA+B,GAAG,yBAAyB,CAAC;IACzH,KAAK,EAAE,MAAM,CAAC;CACf;AAOD,gCAAgC;AAChC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAE/D;AAED,uEAAuE;AACvE,eAAO,MAAM,sBAAsB,EAAE,SAAS,UAAU,EAAW,CAAC;AAMpE,wFAAwF;AACxF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAczE;AAED,6FAA6F;AAC7F,eAAO,MAAM,gBAAgB,EAAE,SAAS,UAAU,EAAyD,CAAC;AAI5G,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CA6BtG;AAED,oFAAoF;AACpF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS,CAW9G;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAE/D;AAED,qGAAqG;AACrG,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAMjG;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,GAAG,gBAAgB,CA6BvH"}
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CATALOG_BACKENDS = exports.NATIVE_RESUME_BACKENDS = exports.SESSION_BACKENDS = void 0;
4
+ exports.sessionBackendLabel = sessionBackendLabel;
5
+ exports.validateSessionSharing = validateSessionSharing;
6
+ exports.formatCatalog = formatCatalog;
7
+ exports.sessionSharingDenied = sessionSharingDenied;
8
+ exports.formatPiCatalog = formatPiCatalog;
9
+ exports.catalogTarget = catalogTarget;
10
+ exports.isSessionControl = isSessionControl;
11
+ exports.sessionResumeGate = sessionResumeGate;
12
+ const adapters_1 = require("./adapters");
13
+ exports.SESSION_BACKENDS = ['claude', 'codex', 'agy', 'opencode', 'kimi', 'zcode', 'pi', 'dsh'];
14
+ const BACKEND_LABELS = {
15
+ claude: 'Claude Code', codex: 'Codex', agy: 'AGY', opencode: 'OpenCode',
16
+ kimi: 'Kimi', zcode: 'ZCode', pi: 'Pi', dsh: 'DSH',
17
+ };
18
+ /** User-facing backend name. */
19
+ function sessionBackendLabel(backend) {
20
+ return BACKEND_LABELS[backend];
21
+ }
22
+ /** Backends whose strict native resume is verified and implemented. */
23
+ exports.NATIVE_RESUME_BACKENDS = ['pi'];
24
+ function object(value) {
25
+ return !!value && typeof value === 'object' && !Array.isArray(value);
26
+ }
27
+ /** Validate the opt-in without resetting unrelated configuration on malformed input. */
28
+ function validateSessionSharing(value) {
29
+ if (value === undefined)
30
+ return undefined;
31
+ if (!object(value) || Object.keys(value).some(key => !['enabled', 'backends'].includes(key)))
32
+ return 'Invalid session-sharing configuration.';
33
+ if (value.enabled !== undefined && typeof value.enabled !== 'boolean')
34
+ return 'Session-sharing enabled must be a boolean.';
35
+ if (value.backends === undefined)
36
+ return undefined;
37
+ if (!object(value.backends))
38
+ return 'Session-sharing backends must be an object.';
39
+ for (const [backend, options] of Object.entries(value.backends)) {
40
+ if (!exports.SESSION_BACKENDS.includes(backend) || !object(options)
41
+ || Object.keys(options).some(key => key !== 'enabled')
42
+ || (options.enabled !== undefined && typeof options.enabled !== 'boolean')) {
43
+ return 'Invalid session-sharing backend configuration.';
44
+ }
45
+ }
46
+ return undefined;
47
+ }
48
+ /** Backends whose read-only catalog has been implemented; connecting remains unavailable. */
49
+ exports.CATALOG_BACKENDS = Object.keys(adapters_1.SESSION_CATALOG_READERS);
50
+ const CATALOG_LABELS = { pi: 'Pi', codex: 'Codex', claude: 'Claude Code' };
51
+ /** Read-only menu rendering shared by the implemented catalog backends. */
52
+ function formatCatalog(catalog, backend, view) {
53
+ const label = CATALOG_LABELS[backend] ?? BACKEND_LABELS[backend];
54
+ const pageSize = view?.pageSize ?? 8;
55
+ const pages = Math.max(1, Math.ceil(catalog.candidates.length / pageSize));
56
+ const page = Math.min(Math.max(view?.page ?? 1, 1), pages);
57
+ const offset = (page - 1) * pageSize;
58
+ const rows = catalog.candidates.slice(offset, offset + pageSize).map((item, index) => {
59
+ const title = item.name ?? `Session ${item.id.slice(0, 8)}`;
60
+ const updated = new Date(item.modifiedAt).toISOString().slice(0, 16).replace('T', ' ');
61
+ const count = item.countComplete ? `${item.messageCount} messages` : 'messages unavailable';
62
+ return `${offset + index + 1}. ${title} · \`${item.id}\` · ${count} · ${item.source} · updated ${updated} UTC`;
63
+ });
64
+ const hidden = catalog.candidates.length - (offset + rows.length);
65
+ const more = !view && hidden > 0 ? [`… ${hidden} more not shown.`] : [];
66
+ const body = rows.length ? rows.join('\n')
67
+ : view?.query ? `No ${label} sessions match "${view.query}".` : `No ${label} sessions were found for this workspace.`;
68
+ const coverage = catalog.complete ? [] : ['Coverage may be incomplete.'];
69
+ const navigation = view ? [
70
+ `Page ${page}/${pages}${view.query ? ` · search "${view.query}"` : ''} · menu \`${view.token}\` (expires in 5 minutes).`,
71
+ ...(view.connectable && rows.length ? [`Select: \`/resume select ${view.token} <row-number>\``] : []),
72
+ ...(page < pages ? [`Next: \`/resume page ${view.token} ${page + 1}\``] : []),
73
+ `Search: \`/resume search ${view.token} <text>\``,
74
+ ] : [];
75
+ const footer = [...more, ...navigation, ...coverage, ...catalog.notes.map(note => `Note: ${note}`)];
76
+ return [`🗂️ ${label} sessions in this workspace (read-only):`, '', body,
77
+ ...(footer.length ? ['', ...footer] : []), '', view?.connectable
78
+ ? 'Selecting a row only shows a confirmation; nothing is loaded and no task is sent until you confirm.'
79
+ : 'Connecting to a session is not available yet: ownership and history-preserving handoff are still being verified. No session was loaded or task sent.']
80
+ .join('\n');
81
+ }
82
+ /** Re-check local opt-in for a backend without parsing a command (menu actions). */
83
+ function sessionSharingDenied(backend, configuration) {
84
+ const invalid = validateSessionSharing(configuration);
85
+ if (invalid)
86
+ return { success: false, code: 'invalid_session_configuration', error: invalid };
87
+ const config = configuration;
88
+ if (config?.enabled !== true) {
89
+ return { success: false, code: 'sharing_disabled', error: 'Session sharing is disabled. Native resume is not yet available; enabling configuration does not certify an adapter.' };
90
+ }
91
+ if (config.backends?.[backend]?.enabled === false) {
92
+ return { success: false, code: 'sharing_disabled', error: `Session sharing is disabled for ${BACKEND_LABELS[backend]}.` };
93
+ }
94
+ return undefined;
95
+ }
96
+ /** Backwards-compatible alias for the Pi-only renderer used by earlier releases. */
97
+ function formatPiCatalog(catalog) {
98
+ return formatCatalog(catalog, 'pi');
99
+ }
100
+ /** Return the target backend for a bare chooser request, or undefined for other session controls. */
101
+ function catalogTarget(content, currentBackend) {
102
+ const parts = content.trim().split(/\s+/);
103
+ if (!/^\/(?:resume|r)$/i.test(parts[0]))
104
+ return undefined;
105
+ if (parts.length === 1)
106
+ return currentBackend;
107
+ if (parts.length === 3 && parts[1] === 'backend' && exports.SESSION_BACKENDS.includes(parts[2]))
108
+ return parts[2];
109
+ return undefined;
110
+ }
111
+ /** Recognize session controls before executor construction or slash passthrough. */
112
+ function isSessionControl(content) {
113
+ return /^\/(?:resume|r|session)(?:\s|$)/i.test(content.trim());
114
+ }
115
+ /** Fail closed until discovery, ownership, preservation and native identity are certified. */
116
+ function sessionResumeGate(content, currentBackend, configuration) {
117
+ const invalid = validateSessionSharing(configuration);
118
+ if (invalid)
119
+ return { success: false, code: 'invalid_session_configuration', error: invalid };
120
+ const config = configuration;
121
+ if (config?.enabled !== true) {
122
+ return { success: false, code: 'sharing_disabled', error: 'Session sharing is disabled. Native resume is not yet available; enabling configuration does not certify an adapter.' };
123
+ }
124
+ const parts = content.trim().split(/\s+/);
125
+ let backend = currentBackend;
126
+ const sessionStatus = /^\/session$/i.test(parts[0]);
127
+ if (/^\/(?:resume|r)$/i.test(parts[0]) && parts[1] === 'backend') {
128
+ if (!parts[2] || !exports.SESSION_BACKENDS.includes(parts[2])) {
129
+ return { success: false, code: 'invalid_session_command', error: `Expected a backend key: ${exports.SESSION_BACKENDS.join(', ')}.` };
130
+ }
131
+ backend = parts[2];
132
+ }
133
+ if (config?.backends?.[backend]?.enabled === false) {
134
+ return { success: false, code: 'sharing_disabled', error: `Session sharing is disabled for ${BACKEND_LABELS[backend]}.` };
135
+ }
136
+ if (sessionStatus) {
137
+ return {
138
+ success: false, code: 'native_capability_unavailable',
139
+ error: `No native session is bound to this thread. ${BACKEND_LABELS[backend]} session status and detach become available after native resume is verified. No session was loaded or task sent.\n\nOn this computer, run \`remote-cli session show --thread <thread-name> --backend ${backend}\` for local-only reopen guidance; it never starts a backend.`,
140
+ };
141
+ }
142
+ return {
143
+ success: false, code: 'native_capability_unavailable',
144
+ error: `${BACKEND_LABELS[backend]} native resume is not available in this release. Session discovery, ownership and history-preserving handoff are still being verified. No session was loaded or task sent.`,
145
+ };
146
+ }
147
+ //# sourceMappingURL=SessionResumeService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionResumeService.js","sourceRoot":"","sources":["../../src/session/SessionResumeService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type SessionCatalog, type SessionCatalogOptions } from '../SessionCatalog';
2
+ /** Project directory name: leading separator dropped, separators replaced with `-`. */
3
+ export declare function claudeProjectDirectoryName(cwd: string): string;
4
+ /**
5
+ * Read-only discovery of Claude Code sessions whose recorded workspace equals the
6
+ * thread workspace. It never starts Claude Code, reads further than the bounded
7
+ * head, migrates files, or writes anything. Counts stay unavailable.
8
+ */
9
+ export declare function listClaudeSessions(workingDirectory: string, options?: SessionCatalogOptions): SessionCatalog;
10
+ //# sourceMappingURL=ClaudeSessionCatalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClaudeSessionCatalog.d.ts","sourceRoot":"","sources":["../../../src/session/adapters/ClaudeSessionCatalog.ts"],"names":[],"mappings":"AAGA,OAAO,EASL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAE3B,MAAM,mBAAmB,CAAC;AAc3B,uFAAuF;AACvF,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9D;AAiED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,cAAc,CA+BhH"}
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.claudeProjectDirectoryName = claudeProjectDirectoryName;
7
+ exports.listClaudeSessions = listClaudeSessions;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const SessionCatalog_1 = require("../SessionCatalog");
12
+ /**
13
+ * Claude Code stores one JSONL transcript per session under
14
+ * `<config-dir>/projects/<encoded-cwd>/<session-id>.jsonl`. A transcript record
15
+ * is large and `cwd` first appears after ~2.5 KiB in the observed release, so a
16
+ * bounded head read is used instead of the shared 64 KiB header budget. The cap
17
+ * stays a hard resource bound; an oversized head becomes a coverage gap.
18
+ */
19
+ const CLAUDE_HEAD_MAX_BYTES = 256 * 1024;
20
+ /** Reject a transcript that cannot show its recorded workspace inside the head. */
21
+ const CLAUDE_CWD_MARKER = '"cwd"';
22
+ const CLAUDE_MAX_RECORD_BYTES = 64 * 1024;
23
+ /** Project directory name: leading separator dropped, separators replaced with `-`. */
24
+ function claudeProjectDirectoryName(cwd) {
25
+ return `-${path_1.default.resolve(cwd).replace(/^[/\\]/, '').replace(/[/\\:]/g, '-')}`;
26
+ }
27
+ /** Read a bounded head of a private single-link regular file; never follows a symlink. */
28
+ function readHead(file, maxBytes) {
29
+ let fd;
30
+ try {
31
+ const before = fs_1.default.lstatSync(file);
32
+ if (!before.isFile() || before.isSymbolicLink() || before.nlink !== 1 || before.size === 0)
33
+ return undefined;
34
+ fd = fs_1.default.openSync(file, fs_1.default.constants.O_RDONLY | (fs_1.default.constants.O_NOFOLLOW ?? 0));
35
+ const stat = fs_1.default.fstatSync(fd);
36
+ if (stat.ino !== before.ino || stat.dev !== before.dev)
37
+ return undefined;
38
+ const size = Math.min(stat.size, maxBytes);
39
+ const buffer = Buffer.alloc(size);
40
+ const length = fs_1.default.readSync(fd, buffer, 0, size, 0);
41
+ return { text: buffer.subarray(0, length).toString('utf8'), truncated: stat.size > maxBytes };
42
+ }
43
+ catch {
44
+ return undefined;
45
+ }
46
+ finally {
47
+ if (fd !== undefined)
48
+ fs_1.default.closeSync(fd);
49
+ }
50
+ }
51
+ /**
52
+ * Parse the bounded head of one transcript. Only whole newline-terminated records
53
+ * are considered, so a partially read trailing record can never be misread.
54
+ */
55
+ function headOf(file, expectedDirectory) {
56
+ const head = readHead(file, CLAUDE_HEAD_MAX_BYTES);
57
+ if (!head)
58
+ return undefined;
59
+ const lines = head.text.split('\n');
60
+ if (head.truncated)
61
+ lines.pop();
62
+ const sessionIdFromName = path_1.default.basename(file, '.jsonl');
63
+ let sessionId;
64
+ let cwd;
65
+ let sidechain = false;
66
+ let lastPrompt;
67
+ for (const line of lines) {
68
+ if (!line || line.length > CLAUDE_MAX_RECORD_BYTES)
69
+ continue;
70
+ let record;
71
+ try {
72
+ record = JSON.parse(line);
73
+ }
74
+ catch {
75
+ continue;
76
+ }
77
+ if (typeof record.sessionId === 'string' && record.sessionId) {
78
+ // Later records can belong to another session; a transcript must stay single-session.
79
+ if (!sessionId)
80
+ sessionId = record.sessionId;
81
+ else if (record.sessionId !== sessionId)
82
+ return undefined;
83
+ }
84
+ if (typeof record.cwd === 'string' && record.cwd)
85
+ cwd ??= record.cwd;
86
+ if (record.isSidechain === true)
87
+ sidechain = true;
88
+ if (record.type === 'last-prompt' && typeof record.lastPrompt === 'string')
89
+ lastPrompt = record.lastPrompt;
90
+ }
91
+ if (!sessionId || sessionId !== sessionIdFromName || sessionId.length > SessionCatalog_1.SESSION_CATALOG_MAX_ID
92
+ || /[\u0000-\u001f\u007f\s`]/.test(sessionId) || SessionCatalog_1.SYNTHETIC_SESSION.test(sessionId))
93
+ return undefined;
94
+ if (!cwd || !path_1.default.isAbsolute(cwd))
95
+ return undefined;
96
+ // The containing directory must itself encode this recorded workspace.
97
+ if (path_1.default.basename(expectedDirectory) !== claudeProjectDirectoryName(cwd))
98
+ return undefined;
99
+ let modifiedAt;
100
+ try {
101
+ modifiedAt = fs_1.default.lstatSync(file).mtimeMs;
102
+ }
103
+ catch {
104
+ return undefined;
105
+ }
106
+ if (!Number.isFinite(modifiedAt) || modifiedAt - Date.now() > SessionCatalog_1.SESSION_CATALOG_MAX_MODIFIED_SKEW_MS)
107
+ return undefined;
108
+ return { sessionId, cwd, modifiedAt, sidechain, summary: lastPrompt ? (0, SessionCatalog_1.safeSessionLabel)(lastPrompt) : undefined };
109
+ }
110
+ /**
111
+ * Read-only discovery of Claude Code sessions whose recorded workspace equals the
112
+ * thread workspace. It never starts Claude Code, reads further than the bounded
113
+ * head, migrates files, or writes anything. Counts stay unavailable.
114
+ */
115
+ function listClaudeSessions(workingDirectory, options = {}) {
116
+ const home = options.home ?? os_1.default.homedir();
117
+ const env = options.env ?? process.env;
118
+ const remoteDirectory = options.remoteCliDirectory ?? path_1.default.join(home, '.remote-cli');
119
+ const notes = [];
120
+ const threadCwd = (0, SessionCatalog_1.canonicalPath)(workingDirectory);
121
+ if (!threadCwd)
122
+ return { candidates: [], complete: false, notes: ['The thread working directory is unavailable.'] };
123
+ const configured = env.CLAUDE_CONFIG_DIR ? (0, SessionCatalog_1.expandHomePath)(env.CLAUDE_CONFIG_DIR, home) : undefined;
124
+ if (env.CLAUDE_CONFIG_DIR && !configured)
125
+ notes.push('A relative Claude config directory override was ignored.');
126
+ const configDirectory = configured ?? path_1.default.join(home, '.claude');
127
+ const projectDirectory = path_1.default.join(configDirectory, 'projects', claudeProjectDirectoryName(threadCwd));
128
+ if (env.CLAUDE_CONFIG_DIR && !configured) {
129
+ return { candidates: [], complete: false,
130
+ notes: [...notes, 'The native Claude config directory could not be resolved.'] };
131
+ }
132
+ const stores = [{ directory: projectDirectory, source: 'native-local' }];
133
+ const scan = (0, SessionCatalog_1.scanSessionCatalogs)(stores, (file, root, store) => {
134
+ let head;
135
+ try {
136
+ head = headOf(file, root);
137
+ }
138
+ catch {
139
+ head = undefined;
140
+ }
141
+ if (!head || head.sidechain || (0, SessionCatalog_1.canonicalPath)(head.cwd) !== threadCwd)
142
+ return undefined;
143
+ return { id: head.sessionId, name: head.summary, source: store.source,
144
+ modifiedAt: head.modifiedAt, messageCount: 0, countComplete: false };
145
+ }, { onGap: reason => notes.push(reason === 'truncated'
146
+ ? 'Some Claude session storage could not be fully scanned.'
147
+ : 'The Claude project store could not be read safely.') });
148
+ if (scan.duplicates)
149
+ notes.push(`${scan.duplicates} duplicated session ID(s) were hidden.`);
150
+ if (scan.candidates.length)
151
+ notes.push('Claude message counts are not available without reading native transcripts.');
152
+ if (scan.complete && !scan.candidates.length)
153
+ notes.push('No Claude Code sessions were found for this workspace.');
154
+ return { candidates: scan.candidates, complete: scan.complete, notes };
155
+ }
156
+ //# sourceMappingURL=ClaudeSessionCatalog.js.map