@roackb2/heddle 0.0.27 → 0.0.28

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 (116) hide show
  1. package/README.md +20 -0
  2. package/dist/src/cli/ask.d.ts +2 -0
  3. package/dist/src/cli/ask.d.ts.map +1 -1
  4. package/dist/src/cli/ask.js +80 -0
  5. package/dist/src/cli/ask.js.map +1 -1
  6. package/dist/src/cli/chat/App.d.ts.map +1 -1
  7. package/dist/src/cli/chat/App.js +11 -1
  8. package/dist/src/cli/chat/App.js.map +1 -1
  9. package/dist/src/cli/chat/components/RuntimeHostInterstitial.d.ts +7 -0
  10. package/dist/src/cli/chat/components/RuntimeHostInterstitial.d.ts.map +1 -0
  11. package/dist/src/cli/chat/components/RuntimeHostInterstitial.js +22 -0
  12. package/dist/src/cli/chat/components/RuntimeHostInterstitial.js.map +1 -0
  13. package/dist/src/cli/chat/components/index.d.ts +1 -0
  14. package/dist/src/cli/chat/components/index.d.ts.map +1 -1
  15. package/dist/src/cli/chat/components/index.js +1 -0
  16. package/dist/src/cli/chat/components/index.js.map +1 -1
  17. package/dist/src/cli/chat/hooks/useAgentRun.d.ts.map +1 -1
  18. package/dist/src/cli/chat/hooks/useAgentRun.js +45 -1
  19. package/dist/src/cli/chat/hooks/useAgentRun.js.map +1 -1
  20. package/dist/src/cli/chat/hooks/useChatSessions.d.ts +3 -1
  21. package/dist/src/cli/chat/hooks/useChatSessions.d.ts.map +1 -1
  22. package/dist/src/cli/chat/hooks/useChatSessions.js +7 -2
  23. package/dist/src/cli/chat/hooks/useChatSessions.js.map +1 -1
  24. package/dist/src/cli/chat/utils/runtime.d.ts +3 -0
  25. package/dist/src/cli/chat/utils/runtime.d.ts.map +1 -1
  26. package/dist/src/cli/chat/utils/runtime.js +1 -0
  27. package/dist/src/cli/chat/utils/runtime.js.map +1 -1
  28. package/dist/src/cli/daemon.d.ts +1 -0
  29. package/dist/src/cli/daemon.d.ts.map +1 -1
  30. package/dist/src/cli/daemon.js +6 -1
  31. package/dist/src/cli/daemon.js.map +1 -1
  32. package/dist/src/cli/heartbeat/task-commands.d.ts.map +1 -1
  33. package/dist/src/cli/heartbeat/task-commands.js +10 -1
  34. package/dist/src/cli/heartbeat/task-commands.js.map +1 -1
  35. package/dist/src/cli/heartbeat/worker.d.ts.map +1 -1
  36. package/dist/src/cli/heartbeat/worker.js +9 -1
  37. package/dist/src/cli/heartbeat/worker.js.map +1 -1
  38. package/dist/src/cli/main.js +81 -6
  39. package/dist/src/cli/main.js.map +1 -1
  40. package/dist/src/cli/remote/control-plane-client.d.ts +247 -0
  41. package/dist/src/cli/remote/control-plane-client.d.ts.map +1 -0
  42. package/dist/src/cli/remote/control-plane-client.js +11 -0
  43. package/dist/src/cli/remote/control-plane-client.js.map +1 -0
  44. package/dist/src/core/chat/session-lease.d.ts +19 -0
  45. package/dist/src/core/chat/session-lease.d.ts.map +1 -0
  46. package/dist/src/core/chat/session-lease.js +44 -0
  47. package/dist/src/core/chat/session-lease.js.map +1 -0
  48. package/dist/src/core/chat/session-submit.d.ts +3 -0
  49. package/dist/src/core/chat/session-submit.d.ts.map +1 -1
  50. package/dist/src/core/chat/session-submit.js +138 -108
  51. package/dist/src/core/chat/session-submit.js.map +1 -1
  52. package/dist/src/core/chat/storage.d.ts +4 -1
  53. package/dist/src/core/chat/storage.d.ts.map +1 -1
  54. package/dist/src/core/chat/storage.js +31 -0
  55. package/dist/src/core/chat/storage.js.map +1 -1
  56. package/dist/src/core/chat/types.d.ts +9 -0
  57. package/dist/src/core/chat/types.d.ts.map +1 -1
  58. package/dist/src/core/runtime/daemon-registry.d.ts +39 -0
  59. package/dist/src/core/runtime/daemon-registry.d.ts.map +1 -0
  60. package/dist/src/core/runtime/daemon-registry.js +120 -0
  61. package/dist/src/core/runtime/daemon-registry.js.map +1 -0
  62. package/dist/src/core/runtime/heartbeat-task-store.d.ts +2 -0
  63. package/dist/src/core/runtime/heartbeat-task-store.d.ts.map +1 -1
  64. package/dist/src/core/runtime/heartbeat-task-store.js +1 -0
  65. package/dist/src/core/runtime/heartbeat-task-store.js.map +1 -1
  66. package/dist/src/core/runtime/heartbeat-views.d.ts +2 -0
  67. package/dist/src/core/runtime/heartbeat-views.d.ts.map +1 -1
  68. package/dist/src/core/runtime/heartbeat-views.js +2 -0
  69. package/dist/src/core/runtime/heartbeat-views.js.map +1 -1
  70. package/dist/src/core/runtime/runtime-hosts.d.ts +30 -0
  71. package/dist/src/core/runtime/runtime-hosts.d.ts.map +1 -0
  72. package/dist/src/core/runtime/runtime-hosts.js +95 -0
  73. package/dist/src/core/runtime/runtime-hosts.js.map +1 -0
  74. package/dist/src/core/runtime/workspaces.d.ts +48 -0
  75. package/dist/src/core/runtime/workspaces.d.ts.map +1 -0
  76. package/dist/src/core/runtime/workspaces.js +139 -0
  77. package/dist/src/core/runtime/workspaces.js.map +1 -0
  78. package/dist/src/server/app.d.ts +5 -2
  79. package/dist/src/server/app.d.ts.map +1 -1
  80. package/dist/src/server/app.js +31 -7
  81. package/dist/src/server/app.js.map +1 -1
  82. package/dist/src/server/features/control-plane/router.d.ts +45 -0
  83. package/dist/src/server/features/control-plane/router.d.ts.map +1 -1
  84. package/dist/src/server/features/control-plane/router.js +95 -18
  85. package/dist/src/server/features/control-plane/router.js.map +1 -1
  86. package/dist/src/server/features/control-plane/services/ask.d.ts +17 -0
  87. package/dist/src/server/features/control-plane/services/ask.d.ts.map +1 -0
  88. package/dist/src/server/features/control-plane/services/ask.js +45 -0
  89. package/dist/src/server/features/control-plane/services/ask.js.map +1 -0
  90. package/dist/src/server/features/control-plane/services/chat-sessions.d.ts +6 -0
  91. package/dist/src/server/features/control-plane/services/chat-sessions.d.ts.map +1 -1
  92. package/dist/src/server/features/control-plane/services/chat-sessions.js +5 -2
  93. package/dist/src/server/features/control-plane/services/chat-sessions.js.map +1 -1
  94. package/dist/src/server/features/control-plane/services/control-plane-state.d.ts.map +1 -1
  95. package/dist/src/server/features/control-plane/services/control-plane-state.js +11 -5
  96. package/dist/src/server/features/control-plane/services/control-plane-state.js.map +1 -1
  97. package/dist/src/server/features/control-plane/types.d.ts +17 -0
  98. package/dist/src/server/features/control-plane/types.d.ts.map +1 -1
  99. package/dist/src/server/index.d.ts.map +1 -1
  100. package/dist/src/server/index.js +114 -6
  101. package/dist/src/server/index.js.map +1 -1
  102. package/dist/src/server/router.d.ts +49 -0
  103. package/dist/src/server/router.d.ts.map +1 -1
  104. package/dist/src/server/router.js +4 -0
  105. package/dist/src/server/router.js.map +1 -1
  106. package/dist/src/server/static.d.ts.map +1 -1
  107. package/dist/src/server/static.js +7 -5
  108. package/dist/src/server/static.js.map +1 -1
  109. package/dist/src/server/types.d.ts +22 -0
  110. package/dist/src/server/types.d.ts.map +1 -1
  111. package/dist/src/web/assets/index-COAJyPuH.css +2 -0
  112. package/dist/src/web/assets/index-DxGlp0Wk.js +52 -0
  113. package/dist/src/web/index.html +2 -2
  114. package/package.json +4 -1
  115. package/dist/src/web/assets/index-BPZ31ir-.js +0 -12
  116. package/dist/src/web/assets/index-C9sFoCL6.css +0 -2
@@ -0,0 +1,247 @@
1
+ import type { ResolvedRuntimeHost } from '../../core/runtime/runtime-hosts.js';
2
+ export declare function createDaemonControlPlaneClient(host: Extract<ResolvedRuntimeHost, {
3
+ kind: 'daemon';
4
+ }>): import("@trpc/client").TRPCClient<import("@trpc/server").TRPCBuiltRouter<{
5
+ ctx: import("../../server/types.js").HeddleServerContext;
6
+ meta: object;
7
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape;
8
+ transformer: false;
9
+ }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
10
+ health: import("@trpc/server").TRPCQueryProcedure<{
11
+ input: void;
12
+ output: {
13
+ ok: boolean;
14
+ service: string;
15
+ workspaceRoot: string;
16
+ stateRoot: string;
17
+ activeWorkspaceId: string;
18
+ workspace: import("../../core/runtime/workspaces.js").WorkspaceDescriptor;
19
+ workspaces: import("../../core/runtime/workspaces.js").WorkspaceDescriptor[];
20
+ runtimeHost: import("../../server/types.js").HeddleRuntimeHostInfo | null;
21
+ };
22
+ meta: object;
23
+ }>;
24
+ controlPlane: import("@trpc/server").TRPCBuiltRouter<{
25
+ ctx: import("../../server/types.js").HeddleServerContext;
26
+ meta: object;
27
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape;
28
+ transformer: false;
29
+ }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
30
+ state: import("@trpc/server").TRPCQueryProcedure<{
31
+ input: void;
32
+ output: import("../../server/features/control-plane/types.js").ControlPlaneState;
33
+ meta: object;
34
+ }>;
35
+ sessions: import("@trpc/server").TRPCQueryProcedure<{
36
+ input: void;
37
+ output: {
38
+ sessions: import("../../server/features/control-plane/types.js").ChatSessionView[];
39
+ };
40
+ meta: object;
41
+ }>;
42
+ sessionCreate: import("@trpc/server").TRPCMutationProcedure<{
43
+ input: {
44
+ name?: string | undefined;
45
+ model?: string | undefined;
46
+ apiKeyPresent?: boolean | undefined;
47
+ } | undefined;
48
+ output: import("../../server/features/control-plane/types.js").ChatSessionDetail;
49
+ meta: object;
50
+ }>;
51
+ session: import("@trpc/server").TRPCQueryProcedure<{
52
+ input: {
53
+ id: string;
54
+ apiKey?: string | undefined;
55
+ };
56
+ output: import("../../server/features/control-plane/types.js").ChatSessionDetail | null;
57
+ meta: object;
58
+ }>;
59
+ modelOptions: import("@trpc/server").TRPCQueryProcedure<{
60
+ input: void;
61
+ output: {
62
+ groups: import("../../index.js").BuiltInModelGroup[];
63
+ };
64
+ meta: object;
65
+ }>;
66
+ sessionSettingsUpdate: import("@trpc/server").TRPCMutationProcedure<{
67
+ input: {
68
+ id: string;
69
+ model?: string | undefined;
70
+ driftEnabled?: boolean | undefined;
71
+ };
72
+ output: import("../../server/features/control-plane/types.js").ChatSessionDetail;
73
+ meta: object;
74
+ }>;
75
+ sessionTurnReview: import("@trpc/server").TRPCQueryProcedure<{
76
+ input: {
77
+ sessionId: string;
78
+ turnId: string;
79
+ };
80
+ output: import("../../server/features/control-plane/types.js").ChatTurnReview | null;
81
+ meta: object;
82
+ }>;
83
+ sessionPendingApproval: import("@trpc/server").TRPCQueryProcedure<{
84
+ input: {
85
+ id: string;
86
+ apiKey?: string | undefined;
87
+ };
88
+ output: import("../../server/features/control-plane/types.js").ControlPlanePendingApproval | null;
89
+ meta: object;
90
+ }>;
91
+ sessionRunning: import("@trpc/server").TRPCQueryProcedure<{
92
+ input: {
93
+ id: string;
94
+ apiKey?: string | undefined;
95
+ };
96
+ output: {
97
+ running: boolean;
98
+ };
99
+ meta: object;
100
+ }>;
101
+ sessionResolveApproval: import("@trpc/server").TRPCMutationProcedure<{
102
+ input: {
103
+ sessionId: string;
104
+ approved: boolean;
105
+ reason?: string | undefined;
106
+ };
107
+ output: {
108
+ resolved: boolean;
109
+ };
110
+ meta: object;
111
+ }>;
112
+ sessionCancel: import("@trpc/server").TRPCMutationProcedure<{
113
+ input: {
114
+ id: string;
115
+ apiKey?: string | undefined;
116
+ };
117
+ output: {
118
+ cancelled: boolean;
119
+ };
120
+ meta: object;
121
+ }>;
122
+ sessionSendPrompt: import("@trpc/server").TRPCMutationProcedure<{
123
+ input: {
124
+ sessionId: string;
125
+ prompt: string;
126
+ apiKey?: string | undefined;
127
+ };
128
+ output: {
129
+ session: import("../../server/features/control-plane/types.js").ChatSessionDetail;
130
+ outcome: import("../../index.js").StopReason;
131
+ summary: string;
132
+ };
133
+ meta: object;
134
+ }>;
135
+ sessionContinue: import("@trpc/server").TRPCMutationProcedure<{
136
+ input: {
137
+ id: string;
138
+ apiKey?: string | undefined;
139
+ };
140
+ output: {
141
+ session: import("../../server/features/control-plane/types.js").ChatSessionDetail;
142
+ outcome: import("../../index.js").StopReason;
143
+ summary: string;
144
+ };
145
+ meta: object;
146
+ }>;
147
+ agentAsk: import("@trpc/server").TRPCMutationProcedure<{
148
+ input: {
149
+ goal: string;
150
+ model?: string | undefined;
151
+ maxSteps?: number | undefined;
152
+ apiKey?: string | undefined;
153
+ searchIgnoreDirs?: string[] | undefined;
154
+ systemContext?: string | undefined;
155
+ };
156
+ output: import("../../server/features/control-plane/services/ask.js").ControlPlaneAskResult;
157
+ meta: object;
158
+ }>;
159
+ heartbeatTasks: import("@trpc/server").TRPCQueryProcedure<{
160
+ input: void;
161
+ output: {
162
+ tasks: import("../../index.js").HeartbeatTaskView[];
163
+ };
164
+ meta: object;
165
+ }>;
166
+ heartbeatRuns: import("@trpc/server").TRPCQueryProcedure<{
167
+ input: {
168
+ taskId?: string | undefined;
169
+ limit?: number | undefined;
170
+ } | undefined;
171
+ output: {
172
+ runs: import("../../index.js").HeartbeatRunView[];
173
+ };
174
+ meta: object;
175
+ }>;
176
+ heartbeatTaskEnable: import("@trpc/server").TRPCMutationProcedure<{
177
+ input: {
178
+ taskId: string;
179
+ };
180
+ output: {
181
+ task: import("../../index.js").HeartbeatTaskView;
182
+ };
183
+ meta: object;
184
+ }>;
185
+ heartbeatTaskDisable: import("@trpc/server").TRPCMutationProcedure<{
186
+ input: {
187
+ taskId: string;
188
+ };
189
+ output: {
190
+ task: import("../../index.js").HeartbeatTaskView;
191
+ };
192
+ meta: object;
193
+ }>;
194
+ heartbeatTaskTrigger: import("@trpc/server").TRPCMutationProcedure<{
195
+ input: {
196
+ taskId: string;
197
+ };
198
+ output: {
199
+ task: import("../../index.js").HeartbeatTaskView;
200
+ };
201
+ meta: object;
202
+ }>;
203
+ workspaceFileSearch: import("@trpc/server").TRPCQueryProcedure<{
204
+ input: {
205
+ query?: string | undefined;
206
+ limit?: number | undefined;
207
+ } | undefined;
208
+ output: {
209
+ files: import("../../server/features/control-plane/services/workspace-files.js").WorkspaceFileSuggestion[];
210
+ };
211
+ meta: object;
212
+ }>;
213
+ workspaceSetActive: import("@trpc/server").TRPCMutationProcedure<{
214
+ input: {
215
+ workspaceId: string;
216
+ };
217
+ output: {
218
+ activeWorkspaceId: string;
219
+ workspace: import("../../core/runtime/workspaces.js").WorkspaceDescriptor;
220
+ workspaces: import("../../core/runtime/workspaces.js").WorkspaceDescriptor[];
221
+ };
222
+ meta: object;
223
+ }>;
224
+ workspaceCreate: import("@trpc/server").TRPCMutationProcedure<{
225
+ input: {
226
+ name: string;
227
+ anchorRoot: string;
228
+ repoRoots?: string[] | undefined;
229
+ setActive?: boolean | undefined;
230
+ };
231
+ output: {
232
+ activeWorkspaceId: string;
233
+ workspace: import("../../core/runtime/workspaces.js").WorkspaceDescriptor;
234
+ workspaces: import("../../core/runtime/workspaces.js").WorkspaceDescriptor[];
235
+ };
236
+ meta: object;
237
+ }>;
238
+ layoutSnapshotSave: import("@trpc/server").TRPCMutationProcedure<{
239
+ input: {
240
+ snapshot: unknown;
241
+ };
242
+ output: import("../../server/features/control-plane/services/layout-snapshots.js").SavedLayoutSnapshot;
243
+ meta: object;
244
+ }>;
245
+ }>>;
246
+ }>>>;
247
+ //# sourceMappingURL=control-plane-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-plane-client.d.ts","sourceRoot":"","sources":["../../../../src/cli/remote/control-plane-client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAE/E,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,OAAO,CAAC,mBAAmB,EAAE;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAQpG"}
@@ -0,0 +1,11 @@
1
+ import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
2
+ export function createDaemonControlPlaneClient(host) {
3
+ return createTRPCProxyClient({
4
+ links: [
5
+ httpBatchLink({
6
+ url: `http://${host.endpoint.host}:${host.endpoint.port}/trpc`,
7
+ }),
8
+ ],
9
+ });
10
+ }
11
+ //# sourceMappingURL=control-plane-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-plane-client.js","sourceRoot":"","sources":["../../../../src/cli/remote/control-plane-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIpE,MAAM,UAAU,8BAA8B,CAAC,IAAsD;IACnG,OAAO,qBAAqB,CAAY;QACtC,KAAK,EAAE;YACL,aAAa,CAAC;gBACZ,GAAG,EAAE,UAAU,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,OAAO;aAC/D,CAAC;SACH;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { ChatSession, ChatSessionLease } from './types.js';
2
+ export type ChatSessionLeaseOwner = {
3
+ ownerKind: ChatSessionLease['ownerKind'];
4
+ ownerId: string;
5
+ clientLabel?: string;
6
+ };
7
+ export declare function isSessionLeaseFresh(lease: ChatSessionLease | undefined, options?: {
8
+ now?: number;
9
+ staleAfterMs?: number;
10
+ }): boolean;
11
+ export declare function acquireSessionLease(session: ChatSession, owner: ChatSessionLeaseOwner, options?: {
12
+ now?: number;
13
+ }): ChatSession;
14
+ export declare function releaseSessionLease(session: ChatSession, owner?: Pick<ChatSessionLeaseOwner, 'ownerId'>): ChatSession;
15
+ export declare function getSessionLeaseConflict(session: ChatSession, owner: ChatSessionLeaseOwner, options?: {
16
+ now?: number;
17
+ staleAfterMs?: number;
18
+ }): string | undefined;
19
+ //# sourceMappingURL=session-lease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-lease.d.ts","sourceRoot":"","sources":["../../../../src/core/chat/session-lease.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAIhE,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,gBAAgB,GAAG,SAAS,EACnC,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD,OAAO,CAWT;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,qBAAqB,EAC5B,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACzB,WAAW,CAYb;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,SAAS,CAAC,GAAG,WAAW,CASrH;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,qBAAqB,EAC5B,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD,MAAM,GAAG,SAAS,CAUpB"}
@@ -0,0 +1,44 @@
1
+ const DEFAULT_SESSION_LEASE_STALE_AFTER_MS = 15 * 60 * 1000;
2
+ export function isSessionLeaseFresh(lease, options) {
3
+ if (!lease) {
4
+ return false;
5
+ }
6
+ const lastSeenAt = Date.parse(lease.lastSeenAt);
7
+ if (!Number.isFinite(lastSeenAt)) {
8
+ return false;
9
+ }
10
+ return (options?.now ?? Date.now()) - lastSeenAt <= (options?.staleAfterMs ?? DEFAULT_SESSION_LEASE_STALE_AFTER_MS);
11
+ }
12
+ export function acquireSessionLease(session, owner, options) {
13
+ const timestamp = new Date(options?.now ?? Date.now()).toISOString();
14
+ return {
15
+ ...session,
16
+ lease: {
17
+ ownerKind: owner.ownerKind,
18
+ ownerId: owner.ownerId,
19
+ acquiredAt: session.lease?.ownerId === owner.ownerId ? session.lease.acquiredAt : timestamp,
20
+ lastSeenAt: timestamp,
21
+ clientLabel: owner.clientLabel,
22
+ },
23
+ };
24
+ }
25
+ export function releaseSessionLease(session, owner) {
26
+ if (!session.lease || (owner && session.lease.ownerId !== owner.ownerId)) {
27
+ return session;
28
+ }
29
+ return {
30
+ ...session,
31
+ lease: undefined,
32
+ };
33
+ }
34
+ export function getSessionLeaseConflict(session, owner, options) {
35
+ if (!session.lease || session.lease.ownerId === owner.ownerId || !isSessionLeaseFresh(session.lease, options)) {
36
+ return undefined;
37
+ }
38
+ return [
39
+ `Session ${session.id} is already active in ${session.lease.clientLabel ?? `${session.lease.ownerKind} (${session.lease.ownerId})`}.`,
40
+ 'Continuing from multiple clients in the same session may corrupt the conversation.',
41
+ 'Wait for the other client to finish or use a different session.',
42
+ ].join(' ');
43
+ }
44
+ //# sourceMappingURL=session-lease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-lease.js","sourceRoot":"","sources":["../../../../src/core/chat/session-lease.ts"],"names":[],"mappings":"AAEA,MAAM,oCAAoC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAQ5D,MAAM,UAAU,mBAAmB,CACjC,KAAmC,EACnC,OAAiD;IAEjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAChD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,UAAU,IAAI,CAAC,OAAO,EAAE,YAAY,IAAI,oCAAoC,CAAC,CAAC;AACtH,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,OAAoB,EACpB,KAA4B,EAC5B,OAA0B;IAE1B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACrE,OAAO;QACL,GAAG,OAAO;QACV,KAAK,EAAE;YACL,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;YAC3F,UAAU,EAAE,SAAS;YACrB,WAAW,EAAE,KAAK,CAAC,WAAW;SAC/B;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAoB,EAAE,KAA8C;IACtG,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACzE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO;QACL,GAAG,OAAO;QACV,KAAK,EAAE,SAAS;KACjB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,OAAoB,EACpB,KAA4B,EAC5B,OAAiD;IAEjD,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;QAC9G,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,WAAW,OAAO,CAAC,EAAE,yBAAyB,OAAO,CAAC,KAAK,CAAC,WAAW,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,KAAK,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG;QACrI,oFAAoF;QACpF,iEAAiE;KAClE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import type { ToolCall, ToolDefinition } from '../../index.js';
2
+ import { type ChatSessionLeaseOwner } from './session-lease.js';
2
3
  import type { ChatSession } from './types.js';
3
4
  import type { AgentLoopEvent } from '../../index.js';
4
5
  export type SubmitChatSessionPromptArgs = {
@@ -20,10 +21,12 @@ export type SubmitChatSessionPromptArgs = {
20
21
  reason?: string;
21
22
  }>;
22
23
  abortSignal?: AbortSignal;
24
+ leaseOwner?: ChatSessionLeaseOwner;
23
25
  };
24
26
  export declare function submitChatSessionPrompt(args: SubmitChatSessionPromptArgs): Promise<{
25
27
  outcome: import("../types.js").StopReason;
26
28
  summary: string;
27
29
  session: ChatSession;
28
30
  }>;
31
+ export declare function clearChatSessionLease(sessionStoragePath: string, sessionId: string, owner: ChatSessionLeaseOwner): void;
29
32
  //# sourceMappingURL=session-submit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-submit.d.ts","sourceRoot":"","sources":["../../../../src/core/chat/session-submit.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAK/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,MAAM,2BAA2B,GAAG;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC1C,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAChJ,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5G,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,CAAC;AAEF,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,2BAA2B;;;;GAsJ9E"}
1
+ {"version":3,"file":"session-submit.d.ts","sourceRoot":"","sources":["../../../../src/core/chat/session-submit.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAI/D,OAAO,EAAqE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEnI,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,MAAM,2BAA2B,GAAG;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC1C,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAChJ,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5G,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,UAAU,CAAC,EAAE,qBAAqB,CAAC;CACpC,CAAC;AAEF,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,2BAA2B;;;;GAyK9E;AAED,wBAAgB,qBAAqB,CAAC,kBAAkB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,QAgBhH"}
@@ -3,6 +3,7 @@ import { createDefaultAgentTools, createLlmAdapter, DEFAULT_OPENAI_MODEL, inferP
3
3
  import { buildCompactionRunningContext, compactChatHistoryWithArchive, estimateChatHistoryTokens } from './compaction.js';
4
4
  import { buildConversationMessages } from './conversation-lines.js';
5
5
  import { formatChatFailureMessage } from './failure-messages.js';
6
+ import { acquireSessionLease, getSessionLeaseConflict, releaseSessionLease } from './session-lease.js';
6
7
  import { loadChatSessions, saveChatSessions, touchSession } from './storage.js';
7
8
  import { saveTrace } from './trace.js';
8
9
  import { countAssistantSteps, summarizeTrace } from './trace-summary.js';
@@ -19,6 +20,17 @@ export async function submitChatSessionPrompt(args) {
19
20
  if (!apiKey) {
20
21
  throw new Error(`Missing provider API key for ${provider}`);
21
22
  }
23
+ const leaseOwner = args.leaseOwner ?? {
24
+ ownerKind: 'ask',
25
+ ownerId: `submit-${process.pid}`,
26
+ clientLabel: 'another Heddle client',
27
+ };
28
+ const leaseConflict = getSessionLeaseConflict(session, leaseOwner);
29
+ if (leaseConflict) {
30
+ throw new Error(leaseConflict);
31
+ }
32
+ const leasedSession = touchSession(acquireSessionLease(session, leaseOwner));
33
+ saveChatSessions(args.sessionStoragePath, sessions.map((candidate) => candidate.id === session.id ? leasedSession : candidate));
22
34
  const llm = createLlmAdapter({ model, apiKey });
23
35
  const tools = createDefaultAgentTools({
24
36
  model,
@@ -28,114 +40,132 @@ export async function submitChatSessionPrompt(args) {
28
40
  searchIgnoreDirs: [],
29
41
  includePlanTool: true,
30
42
  });
31
- const preflightCompacted = await compactChatHistoryWithArchive({
32
- history: session.history,
33
- model,
34
- sessionId: session.id,
35
- stateRoot: args.stateRoot,
36
- toolNames: tools.map((tool) => tool.name),
37
- goal: args.prompt,
38
- onStatusChange: (event) => {
39
- args.onCompactionStatus?.(event);
40
- if (event.status === 'running') {
41
- const compactionSeed = touchSession({
42
- ...session,
43
- context: buildCompactionRunningContext({
44
- history: session.history,
45
- previous: session.context,
46
- archiveCount: session.archives?.length,
47
- currentSummaryPath: session.context?.currentSummaryPath,
48
- lastArchivePath: event.archivePath,
49
- }),
50
- });
51
- saveChatSessions(args.sessionStoragePath, sessions.map((candidate) => candidate.id === session.id ? compactionSeed : candidate));
52
- }
53
- },
54
- });
55
- const preflightSession = preflightCompacted.history !== session.history || preflightCompacted.archives.length !== (session.archives?.length ?? 0) ?
56
- touchSession({
57
- ...session,
58
- history: preflightCompacted.history,
59
- context: preflightCompacted.context,
60
- archives: preflightCompacted.archives,
61
- messages: buildConversationMessages(preflightCompacted.history),
62
- })
63
- : session;
64
- if (preflightSession !== session) {
65
- saveChatSessions(args.sessionStoragePath, sessions.map((candidate) => candidate.id === session.id ? preflightSession : candidate));
66
- }
67
- const result = await runAgentLoop({
68
- goal: args.prompt,
69
- model,
70
- apiKey,
71
- workspaceRoot: args.workspaceRoot,
72
- stateDir: args.stateRoot,
73
- memoryDir: join(args.stateRoot, 'memory'),
74
- llm,
75
- tools,
76
- includeDefaultTools: false,
77
- history: preflightSession.history,
78
- onEvent: args.onEvent,
79
- approveToolCall: args.approveToolCall,
80
- abortSignal: args.abortSignal,
81
- });
82
- const compacted = await compactChatHistoryWithArchive({
83
- history: result.transcript,
84
- model,
85
- sessionId: session.id,
86
- stateRoot: args.stateRoot,
87
- usage: result.usage,
88
- toolNames: tools.map((tool) => tool.name),
89
- goal: args.prompt,
90
- onStatusChange: (event) => {
91
- args.onCompactionStatus?.(event);
92
- if (event.status === 'running') {
93
- const compactionSeed = touchSession({
94
- ...preflightSession,
95
- history: result.transcript,
96
- context: buildCompactionRunningContext({
97
- history: result.transcript,
98
- previous: preflightSession.context,
99
- archiveCount: preflightSession.archives?.length,
100
- currentSummaryPath: preflightSession.context?.currentSummaryPath,
101
- lastArchivePath: event.archivePath,
102
- }),
103
- });
104
- saveChatSessions(args.sessionStoragePath, sessions.map((candidate) => candidate.id === session.id ? compactionSeed : candidate));
105
- }
106
- },
107
- });
108
- const traceFile = saveTrace(join(args.stateRoot, 'traces'), result.trace);
109
- const nextTurn = {
110
- id: `server-turn-${Date.now()}`,
111
- prompt: args.prompt,
112
- outcome: result.outcome,
113
- summary: result.summary,
114
- steps: countAssistantSteps(result.trace),
115
- traceFile,
116
- events: summarizeTrace(result.trace),
117
- };
118
- const formattedSummary = result.outcome === 'error' ?
119
- formatChatFailureMessage(result.summary, {
43
+ try {
44
+ const preflightCompacted = await compactChatHistoryWithArchive({
45
+ history: session.history,
120
46
  model,
121
- estimatedHistoryTokens: estimateChatHistoryTokens(session.history),
122
- })
123
- : result.summary;
124
- const updatedSession = touchSession({
125
- ...preflightSession,
126
- lastContinuePrompt: args.prompt,
127
- history: compacted.history,
128
- context: compacted.context,
129
- archives: compacted.archives,
130
- messages: buildConversationMessages(compacted.history),
131
- turns: [...preflightSession.turns, nextTurn].slice(-8),
132
- });
133
- const nextSessions = sessions.map((candidate) => candidate.id === session.id ? updatedSession : candidate);
134
- saveChatSessions(args.sessionStoragePath, nextSessions);
135
- return {
136
- outcome: result.outcome,
137
- summary: formattedSummary,
138
- session: updatedSession,
139
- };
47
+ sessionId: session.id,
48
+ stateRoot: args.stateRoot,
49
+ toolNames: tools.map((tool) => tool.name),
50
+ goal: args.prompt,
51
+ onStatusChange: (event) => {
52
+ args.onCompactionStatus?.(event);
53
+ if (event.status === 'running') {
54
+ const compactionSeed = touchSession({
55
+ ...leasedSession,
56
+ context: buildCompactionRunningContext({
57
+ history: leasedSession.history,
58
+ previous: leasedSession.context,
59
+ archiveCount: leasedSession.archives?.length,
60
+ currentSummaryPath: leasedSession.context?.currentSummaryPath,
61
+ lastArchivePath: event.archivePath,
62
+ }),
63
+ });
64
+ saveChatSessions(args.sessionStoragePath, sessions.map((candidate) => candidate.id === session.id ? compactionSeed : candidate));
65
+ }
66
+ },
67
+ });
68
+ const preflightSession = preflightCompacted.history !== leasedSession.history || preflightCompacted.archives.length !== (leasedSession.archives?.length ?? 0) ?
69
+ touchSession({
70
+ ...leasedSession,
71
+ history: preflightCompacted.history,
72
+ context: preflightCompacted.context,
73
+ archives: preflightCompacted.archives,
74
+ messages: buildConversationMessages(preflightCompacted.history),
75
+ })
76
+ : leasedSession;
77
+ if (preflightSession !== leasedSession) {
78
+ saveChatSessions(args.sessionStoragePath, sessions.map((candidate) => candidate.id === session.id ? preflightSession : candidate));
79
+ }
80
+ const result = await runAgentLoop({
81
+ goal: args.prompt,
82
+ model,
83
+ apiKey,
84
+ workspaceRoot: args.workspaceRoot,
85
+ stateDir: args.stateRoot,
86
+ memoryDir: join(args.stateRoot, 'memory'),
87
+ llm,
88
+ tools,
89
+ includeDefaultTools: false,
90
+ history: preflightSession.history,
91
+ onEvent: args.onEvent,
92
+ approveToolCall: args.approveToolCall,
93
+ abortSignal: args.abortSignal,
94
+ });
95
+ const compacted = await compactChatHistoryWithArchive({
96
+ history: result.transcript,
97
+ model,
98
+ sessionId: session.id,
99
+ stateRoot: args.stateRoot,
100
+ usage: result.usage,
101
+ toolNames: tools.map((tool) => tool.name),
102
+ goal: args.prompt,
103
+ onStatusChange: (event) => {
104
+ args.onCompactionStatus?.(event);
105
+ if (event.status === 'running') {
106
+ const compactionSeed = touchSession({
107
+ ...preflightSession,
108
+ history: result.transcript,
109
+ context: buildCompactionRunningContext({
110
+ history: result.transcript,
111
+ previous: preflightSession.context,
112
+ archiveCount: preflightSession.archives?.length,
113
+ currentSummaryPath: preflightSession.context?.currentSummaryPath,
114
+ lastArchivePath: event.archivePath,
115
+ }),
116
+ });
117
+ saveChatSessions(args.sessionStoragePath, sessions.map((candidate) => candidate.id === session.id ? compactionSeed : candidate));
118
+ }
119
+ },
120
+ });
121
+ const traceFile = saveTrace(join(args.stateRoot, 'traces'), result.trace);
122
+ const nextTurn = {
123
+ id: `server-turn-${Date.now()}`,
124
+ prompt: args.prompt,
125
+ outcome: result.outcome,
126
+ summary: result.summary,
127
+ steps: countAssistantSteps(result.trace),
128
+ traceFile,
129
+ events: summarizeTrace(result.trace),
130
+ };
131
+ const formattedSummary = result.outcome === 'error' ?
132
+ formatChatFailureMessage(result.summary, {
133
+ model,
134
+ estimatedHistoryTokens: estimateChatHistoryTokens(session.history),
135
+ })
136
+ : result.summary;
137
+ const updatedSession = touchSession({
138
+ ...preflightSession,
139
+ lastContinuePrompt: args.prompt,
140
+ history: compacted.history,
141
+ context: compacted.context,
142
+ archives: compacted.archives,
143
+ lease: undefined,
144
+ messages: buildConversationMessages(compacted.history),
145
+ turns: [...preflightSession.turns, nextTurn].slice(-8),
146
+ });
147
+ const nextSessions = sessions.map((candidate) => candidate.id === session.id ? updatedSession : candidate);
148
+ saveChatSessions(args.sessionStoragePath, nextSessions);
149
+ return {
150
+ outcome: result.outcome,
151
+ summary: formattedSummary,
152
+ session: updatedSession,
153
+ };
154
+ }
155
+ finally {
156
+ clearChatSessionLease(args.sessionStoragePath, session.id, leaseOwner);
157
+ }
158
+ }
159
+ export function clearChatSessionLease(sessionStoragePath, sessionId, owner) {
160
+ const sessions = loadChatSessions(sessionStoragePath, true);
161
+ const session = sessions.find((candidate) => candidate.id === sessionId);
162
+ if (!session?.lease) {
163
+ return;
164
+ }
165
+ const released = releaseSessionLease(session, owner);
166
+ if (released === session) {
167
+ return;
168
+ }
169
+ saveChatSessions(sessionStoragePath, sessions.map((candidate) => candidate.id === sessionId ? touchSession(released) : candidate));
140
170
  }
141
171
  //# sourceMappingURL=session-submit.js.map