@abelxiaoxing/cadence 2.0.12 → 2.0.14

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.
@@ -0,0 +1,147 @@
1
+ /** Private ordinary-chat context. No Pi session import, hidden thinking or provider envelopes. */
2
+ import { closeSync, constants, fstatSync, openSync, readSync } from "node:fs";
3
+ import {
4
+ canonicalJson,
5
+ closed,
6
+ integer,
7
+ list,
8
+ oneOf,
9
+ parseWireJson,
10
+ text,
11
+ } from "./worker-public.mjs";
12
+ import { DetailSanitizer, sanitizeText } from "./worker-redaction.mjs";
13
+
14
+ export const CONTEXT_BYTES = 2 * 1024 * 1024;
15
+ const clean = (s, secrets, max = CONTEXT_BYTES) =>
16
+ sanitizeText(text(s, max), secrets);
17
+ function argumentsData(value, secrets) {
18
+ const sanitizer = new DetailSanitizer("canonical-json", secrets);
19
+ const source =
20
+ sanitizer.write(canonicalJson(value, 64 * 1024)) + sanitizer.end();
21
+ if (sanitizer.limited) throw new Error("Unsupported context arguments");
22
+ return parseWireJson(source, 64 * 1024);
23
+ }
24
+ /** Deliberately retain text and tool exchanges, not just last assistant prose. */
25
+ export function conversationMessages(messages, secrets = []) {
26
+ return list(messages, 4096, (message) => {
27
+ if (!message || typeof message !== "object")
28
+ throw new Error("Invalid context");
29
+ const role = oneOf(message.role, ["user", "assistant", "toolResult"]);
30
+ const timestamp = integer(message.timestamp, 0);
31
+ const content =
32
+ typeof message.content === "string"
33
+ ? [{ type: "text", text: clean(message.content, secrets) }]
34
+ : list(message.content, 256, (block) => {
35
+ if (block.type === "text")
36
+ return { type: "text", text: clean(block.text, secrets) };
37
+ if (role === "assistant" && block.type === "toolCall")
38
+ return {
39
+ type: "toolCall",
40
+ id: clean(block.id, secrets, 512),
41
+ name: clean(block.name, secrets, 128),
42
+ arguments: argumentsData(block.arguments, secrets),
43
+ };
44
+ // Images, hidden thinking and opaque provider signatures never become retained context.
45
+ return null;
46
+ }).filter(Boolean);
47
+ if (role === "user") return { role, timestamp, content };
48
+ if (role === "toolResult")
49
+ return {
50
+ role,
51
+ timestamp,
52
+ content,
53
+ toolCallId: clean(message.toolCallId, secrets, 512),
54
+ toolName: clean(message.toolName, secrets, 128),
55
+ isError: message.isError === true,
56
+ };
57
+ const usage = closed(message.usage, [
58
+ "input",
59
+ "output",
60
+ "cacheRead",
61
+ "cacheWrite",
62
+ "totalTokens",
63
+ "cost",
64
+ ]);
65
+ const cost = closed(usage.cost, [
66
+ "input",
67
+ "output",
68
+ "cacheRead",
69
+ "cacheWrite",
70
+ "total",
71
+ ]);
72
+ for (const value of [
73
+ ...Object.values(usage).filter((v) => typeof v !== "object"),
74
+ ...Object.values(cost),
75
+ ])
76
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
77
+ throw new Error("Invalid usage");
78
+ return {
79
+ role,
80
+ timestamp,
81
+ content,
82
+ api: clean(message.api, secrets, 128),
83
+ provider: clean(message.provider, secrets, 128),
84
+ model: clean(message.model, secrets, 256),
85
+ usage,
86
+ stopReason: oneOf(message.stopReason, [
87
+ "stop",
88
+ "length",
89
+ "toolUse",
90
+ "error",
91
+ "aborted",
92
+ ]),
93
+ };
94
+ });
95
+ }
96
+ export function readConversationContext(path) {
97
+ if (path === null) return [];
98
+ const fd = openSync(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
99
+ try {
100
+ const st = fstatSync(fd);
101
+ if (
102
+ !st.isFile() ||
103
+ st.nlink !== 1 ||
104
+ st.size > CONTEXT_BYTES ||
105
+ (process.platform !== "win32" &&
106
+ (st.mode & 0o077 || st.uid !== process.getuid()))
107
+ )
108
+ throw new Error("Invalid context file");
109
+ const buffer = Buffer.alloc(st.size + 1);
110
+ let size = 0;
111
+ while (size < buffer.length) {
112
+ const n = readSync(fd, buffer, size, buffer.length - size, null);
113
+ if (!n) break;
114
+ size += n;
115
+ }
116
+ const after = fstatSync(fd);
117
+ if (
118
+ size !== st.size ||
119
+ after.size !== st.size ||
120
+ after.mtimeMs !== st.mtimeMs
121
+ )
122
+ throw new Error("Changed context file");
123
+ const o = closed(
124
+ parseWireJson(
125
+ new TextDecoder("utf-8", { fatal: true }).decode(
126
+ buffer.subarray(0, size),
127
+ ),
128
+ CONTEXT_BYTES,
129
+ ),
130
+ ["schema", "messages"],
131
+ );
132
+ if (o.schema !== "cadence-conversation-context/1")
133
+ throw new Error("Invalid context format");
134
+ return conversationMessages(o.messages);
135
+ } finally {
136
+ closeSync(fd);
137
+ }
138
+ }
139
+ export function encodeConversationContext(messages, secrets) {
140
+ return canonicalJson(
141
+ {
142
+ schema: "cadence-conversation-context/1",
143
+ messages: conversationMessages(messages, secrets),
144
+ },
145
+ CONTEXT_BYTES,
146
+ );
147
+ }
@@ -0,0 +1,47 @@
1
+ /** Ordinary Pi context only: no extension/plugin executor, persona or prompt composer. */
2
+ import { readFileSync, statSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import {
5
+ loadProjectContextFiles,
6
+ loadSkills,
7
+ } from "@earendil-works/pi-coding-agent";
8
+
9
+ function optional(path) {
10
+ try {
11
+ if (!statSync(path).isFile() || statSync(path).size > 64 * 1024)
12
+ throw new Error("Resource too large");
13
+ return readFileSync(path, "utf8");
14
+ } catch (error) {
15
+ if (error?.code === "ENOENT") return undefined;
16
+ throw error;
17
+ }
18
+ }
19
+ export function conversationResources(base, cwd, agentDir) {
20
+ const project = join(cwd, ".pi");
21
+ const system =
22
+ optional(join(project, "SYSTEM.md")) ??
23
+ optional(join(agentDir, "SYSTEM.md"));
24
+ const append = [
25
+ optional(join(agentDir, "APPEND_SYSTEM.md")),
26
+ optional(join(project, "APPEND_SYSTEM.md")),
27
+ ].filter((v) => v !== undefined);
28
+ const agentsFiles = loadProjectContextFiles({ cwd, agentDir });
29
+ const skills = loadSkills({
30
+ cwd,
31
+ agentDir,
32
+ skillPaths: [],
33
+ includeDefaults: true,
34
+ });
35
+ if (
36
+ Buffer.byteLength(JSON.stringify({ system, append, agentsFiles, skills })) >
37
+ 256 * 1024
38
+ )
39
+ throw new Error("Resources too large");
40
+ return {
41
+ ...base,
42
+ getSystemPrompt: () => system,
43
+ getAppendSystemPrompt: () => append,
44
+ getAgentsFiles: () => ({ agentsFiles }),
45
+ getSkills: () => skills,
46
+ };
47
+ }
@@ -2,5 +2,5 @@
2
2
  export const nativeBuild = {
3
3
  kind: "native",
4
4
  contractHash:
5
- "e9b1ea4bb8d1c275a0cb0cf70947f9ac58e5de0cc438d551dc96809f3e82dd7c",
5
+ "9c917c085d034e08764de359cee49aea748c395a63ed629fe67d149b97850fa1",
6
6
  } as const;
@@ -192,8 +192,8 @@ export class NativeConnection {
192
192
  // Failed detach keeps its exact transport/slot. Explicit re-entry may retry
193
193
  // detach, but never forget it and allocate a replacement on uncertainty.
194
194
  await this.detach();
195
- // Pi started in the home directory attaches to the ~/cadence landing owner,
196
- // the same one the management client starts there. Observation only.
195
+ // Native clients require an explicit project; never create or redirect to
196
+ // a default workspace. Observation only.
197
197
  const access = new ServiceCoordination(
198
198
  effectiveWorkspace(cwd).workspace,
199
199
  ).localAccess();
@@ -288,13 +288,10 @@ function stateHomeInside(root, stateHome = defaultStateHome()) {
288
288
  function privateWorkspace(requested, stateHome = defaultStateHome()) {
289
289
  return contains(join(canonicalAmbientRoot(stateHome), "cadence-assistant-v1"), canonicalAmbientRoot(requested));
290
290
  }
291
- var LANDING_WORKSPACE = "cadence";
292
- function effectiveWorkspace(requested, stateHome, home = homedir()) {
293
- if (privateWorkspace(requested, stateHome))
294
- throw new Error("Private state cannot be a workspace");
295
- if (!stateHomeInside(resolveWorkspace(requested).root, stateHome))
296
- return { workspace: requested, redirected: false };
297
- return { workspace: join(home, LANDING_WORKSPACE), redirected: true };
291
+ function effectiveWorkspace(requested, stateHome, _home) {
292
+ if (privateWorkspace(requested, stateHome) || stateHomeInside(resolveWorkspace(requested).root, stateHome))
293
+ throw new Error("Choose a project outside Cadence private state; no default workspace was created");
294
+ return { workspace: requested, redirected: false };
298
295
  }
299
296
 
300
297
  class TaskStore {
@@ -1,4 +1,6 @@
1
- // Ordinary SDK child, not Pi CLI startup. No discovery, credential files or fallback executor.
1
+ // Isolated SDK child, not Pi CLI startup. Task discovery stays disabled;
2
+ // ordinary conversations explicitly load only context/skills, never extensions.
3
+ import { writeFileSync } from "node:fs";
2
4
  import {
3
5
  createProvider,
4
6
  InMemoryCredentialStore,
@@ -12,6 +14,11 @@ import {
12
14
  SessionManager,
13
15
  SettingsManager,
14
16
  } from "@earendil-works/pi-coding-agent";
17
+ import {
18
+ encodeConversationContext,
19
+ readConversationContext,
20
+ } from "./conversation-context.mjs";
21
+ import { conversationResources } from "./conversation-resources.mjs";
15
22
  import { CaptureEmitter, SdkCapture } from "./worker-capture.mjs";
16
23
  import {
17
24
  closed,
@@ -185,13 +192,27 @@ try {
185
192
  if (inputBytes > 128 * 1024) refuse("parse", "input-bound");
186
193
  chunks.push(chunk);
187
194
  }
195
+ const parsed = parseWireJson(
196
+ new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks)),
197
+ LIMITS.workerRequestBytes,
198
+ );
188
199
  const envelope = closed(
189
- parseWireJson(
190
- new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks)),
191
- LIMITS.workerRequestBytes,
192
- ),
193
- ["request", "cwd", "model", "auth"],
200
+ parsed,
201
+ Object.hasOwn(parsed ?? {}, "conversation")
202
+ ? ["request", "cwd", "model", "auth", "conversation"]
203
+ : ["request", "cwd", "model", "auth"],
194
204
  );
205
+ const conversation =
206
+ envelope.conversation === undefined
207
+ ? null
208
+ : closed(envelope.conversation, ["previous", "agentDir"]);
209
+ if (
210
+ conversation &&
211
+ ((conversation.previous !== null &&
212
+ typeof conversation.previous !== "string") ||
213
+ typeof conversation.agentDir !== "string")
214
+ )
215
+ refuse("parse", "invalid-conversation");
195
216
  const publicRequest = decodeWorkerPublicRequest(envelope.request);
196
217
  const request = {
197
218
  ...envelope,
@@ -329,6 +350,10 @@ try {
329
350
  process.stdout.on("error", stdoutFailureListener);
330
351
  stage = "session";
331
352
  const requestedThinkingLevel = thinkingLevel ?? "off";
353
+ const manager = SessionManager.inMemory(cwd);
354
+ if (conversation)
355
+ for (const message of readConversationContext(conversation.previous))
356
+ manager.appendMessage(message);
332
357
  const created = await createAgentSession({
333
358
  cwd,
334
359
  agentDir: cwd,
@@ -351,8 +376,10 @@ try {
351
376
  ),
352
377
  ]
353
378
  : [],
354
- resourceLoader,
355
- sessionManager: SessionManager.inMemory(cwd),
379
+ resourceLoader: conversation
380
+ ? conversationResources(resourceLoader, cwd, conversation.agentDir)
381
+ : resourceLoader,
382
+ sessionManager: manager,
356
383
  settingsManager: SettingsManager.inMemory({
357
384
  compaction: { enabled: false },
358
385
  retry: { enabled: false },
@@ -483,6 +510,12 @@ try {
483
510
  stage = "assistant-final";
484
511
  if (failure || terminalStopReason !== "stop" || ingress.frames !== 0)
485
512
  refuse("assistant-final", "terminal-incomplete");
513
+ // Anonymous pipe only. The owner applies its access-secret sanitizer before
514
+ // any checkpoint reaches disk; worker output cannot contain owner credentials.
515
+ if (conversation) {
516
+ stage = "conversation-context";
517
+ writeFileSync(3, encodeConversationContext(session.messages, secrets));
518
+ }
486
519
  await capture.final();
487
520
  await emitter.drain();
488
521
  } catch {
@@ -0,0 +1,303 @@
1
+ /** Finite, authenticated workbench API. Ordinary chat is not task admission. */
2
+ import {
3
+ bool,
4
+ canonicalJson,
5
+ closed,
6
+ decodeId,
7
+ integer,
8
+ list,
9
+ nullable,
10
+ oneOf,
11
+ text,
12
+ } from "./core.ts";
13
+ import { type ChoiceVersion, decodeChoiceVersion } from "./models.ts";
14
+
15
+ export type WorkbenchRead =
16
+ | { kind: "conversations" }
17
+ | { kind: "task-origin"; taskId: string }
18
+ | { kind: "conversation"; id: string; before: string | null }
19
+ | { kind: "files"; path: string }
20
+ | { kind: "file"; path: string }
21
+ | { kind: "directories"; path: string }
22
+ | { kind: "projects" };
23
+ export type WorkbenchCommand =
24
+ | {
25
+ kind: "link-task";
26
+ commandId: string;
27
+ epoch: string;
28
+ taskId: string;
29
+ conversationId: string;
30
+ }
31
+ | {
32
+ kind: "send";
33
+ commandId: string;
34
+ conversationId: string;
35
+ head: string | null;
36
+ text: string;
37
+ model: ChoiceVersion;
38
+ }
39
+ | {
40
+ kind: "interrupt";
41
+ commandId: string;
42
+ conversationId: string;
43
+ turnId: string;
44
+ epoch: string;
45
+ }
46
+ | { kind: "open-project"; commandId: string; path: string; epoch: string };
47
+ export type ChatStatus =
48
+ | "queued"
49
+ | "running"
50
+ | "completed"
51
+ | "failed"
52
+ | "canceled"
53
+ | "timeout"
54
+ | "unknown";
55
+ export interface ChatItem {
56
+ id: string;
57
+ role: "user" | "assistant" | "tool";
58
+ text: string;
59
+ name: string;
60
+ args: string;
61
+ error: boolean;
62
+ }
63
+ export interface ChatTurn {
64
+ id: string;
65
+ epoch: string;
66
+ status: ChatStatus;
67
+ items: ChatItem[];
68
+ notice: string;
69
+ }
70
+ export interface ChatSummary {
71
+ id: string;
72
+ title: string;
73
+ head: string;
74
+ status: ChatStatus;
75
+ updatedAt: number;
76
+ }
77
+ export interface FileEntry {
78
+ name: string;
79
+ path: string;
80
+ directory: boolean;
81
+ }
82
+ export type WorkbenchResult =
83
+ | { kind: "task-origin"; taskId: string; conversationId: string | null }
84
+ | { kind: "conversations"; entries: ChatSummary[] }
85
+ | {
86
+ kind: "conversation";
87
+ summary: ChatSummary | null;
88
+ turns: ChatTurn[];
89
+ before: string | null;
90
+ }
91
+ | {
92
+ kind: "files" | "directories";
93
+ path: string;
94
+ parent: string | null;
95
+ entries: FileEntry[];
96
+ limited: boolean;
97
+ }
98
+ | { kind: "file"; path: string; text: string; limited: boolean }
99
+ | { kind: "projects"; entries: { path: string; current: boolean }[] }
100
+ | { kind: "accepted"; commandId: string }
101
+ | { kind: "project"; commandId: string; path: string; link: string };
102
+ export interface WorkbenchPort {
103
+ read(request: WorkbenchRead): WorkbenchResult;
104
+ command(
105
+ request: WorkbenchCommand,
106
+ ): WorkbenchResult | Promise<WorkbenchResult>;
107
+ }
108
+ const statuses = [
109
+ "queued",
110
+ "running",
111
+ "completed",
112
+ "failed",
113
+ "canceled",
114
+ "timeout",
115
+ "unknown",
116
+ ] as const;
117
+ export function decodeWorkbenchRead(v: unknown): WorkbenchRead {
118
+ const kind = Object.getOwnPropertyDescriptor(v ?? {}, "kind")?.value;
119
+ switch (kind) {
120
+ case "conversations":
121
+ case "projects":
122
+ closed(v, ["kind"]);
123
+ return { kind };
124
+ case "task-origin": {
125
+ const o = closed(v, ["kind", "taskId"]);
126
+ return { kind, taskId: decodeId(o.taskId) };
127
+ }
128
+ case "conversation": {
129
+ const o = closed(v, ["kind", "id", "before"]);
130
+ return { kind, id: decodeId(o.id), before: nullable(o.before, decodeId) };
131
+ }
132
+ default: {
133
+ const o = closed(v, ["kind", "path"]);
134
+ return {
135
+ kind: oneOf(o.kind, ["files", "file", "directories"]),
136
+ path: text(o.path, 4096),
137
+ };
138
+ }
139
+ }
140
+ }
141
+ export function decodeWorkbenchCommand(v: unknown): WorkbenchCommand {
142
+ const kind = Object.getOwnPropertyDescriptor(v ?? {}, "kind")?.value;
143
+ if (kind === "link-task") {
144
+ const o = closed(v, [
145
+ "kind",
146
+ "commandId",
147
+ "epoch",
148
+ "taskId",
149
+ "conversationId",
150
+ ]);
151
+ return {
152
+ kind,
153
+ commandId: decodeId(o.commandId),
154
+ epoch: decodeId(o.epoch),
155
+ taskId: decodeId(o.taskId),
156
+ conversationId: decodeId(o.conversationId),
157
+ };
158
+ }
159
+ if (kind === "send") {
160
+ const o = closed(v, [
161
+ "kind",
162
+ "commandId",
163
+ "conversationId",
164
+ "head",
165
+ "text",
166
+ "model",
167
+ ]);
168
+ return {
169
+ kind,
170
+ commandId: decodeId(o.commandId),
171
+ conversationId: decodeId(o.conversationId),
172
+ head: nullable(o.head, decodeId),
173
+ text: text(o.text, 16 * 1024, true),
174
+ model: decodeChoiceVersion(o.model),
175
+ };
176
+ }
177
+ if (kind === "interrupt") {
178
+ const o = closed(v, [
179
+ "kind",
180
+ "commandId",
181
+ "conversationId",
182
+ "turnId",
183
+ "epoch",
184
+ ]);
185
+ return {
186
+ kind,
187
+ commandId: decodeId(o.commandId),
188
+ conversationId: decodeId(o.conversationId),
189
+ turnId: decodeId(o.turnId),
190
+ epoch: decodeId(o.epoch),
191
+ };
192
+ }
193
+ const o = closed(v, ["kind", "commandId", "path", "epoch"]);
194
+ return {
195
+ kind: oneOf(o.kind, ["open-project"]),
196
+ commandId: decodeId(o.commandId),
197
+ path: text(o.path, 4096, true),
198
+ epoch: decodeId(o.epoch),
199
+ };
200
+ }
201
+ export function decodeChatTurn(v: unknown): ChatTurn {
202
+ const o = closed(v, ["id", "epoch", "status", "items", "notice"]);
203
+ return {
204
+ id: decodeId(o.id),
205
+ epoch: decodeId(o.epoch),
206
+ status: oneOf(o.status, statuses),
207
+ notice: text(o.notice, 1024),
208
+ items: list(o.items, 256, (v) => {
209
+ const i = closed(v, ["id", "role", "text", "name", "args", "error"]);
210
+ return {
211
+ id: text(i.id, 160, true),
212
+ role: oneOf(i.role, ["user", "assistant", "tool"]),
213
+ text: text(i.text, 256 * 1024),
214
+ name: text(i.name, 128),
215
+ args: text(i.args, 64 * 1024),
216
+ error: bool(i.error),
217
+ };
218
+ }),
219
+ };
220
+ }
221
+ export function decodeChatSummary(v: unknown): ChatSummary {
222
+ const o = closed(v, ["id", "title", "head", "status", "updatedAt"]);
223
+ return {
224
+ id: decodeId(o.id),
225
+ title: text(o.title, 512, true),
226
+ head: decodeId(o.head),
227
+ status: oneOf(o.status, statuses),
228
+ updatedAt: integer(o.updatedAt, 0),
229
+ };
230
+ }
231
+ export function decodeWorkbenchResult(v: unknown): WorkbenchResult {
232
+ // A complete finite reply fits the existing page ceiling. No arbitrary file body.
233
+ canonicalJson(v, 512 * 1024);
234
+ const kind = Object.getOwnPropertyDescriptor(v ?? {}, "kind")?.value;
235
+ if (kind === "task-origin") {
236
+ const o = closed(v, ["kind", "taskId", "conversationId"]);
237
+ return {
238
+ kind,
239
+ taskId: decodeId(o.taskId),
240
+ conversationId: nullable(o.conversationId, decodeId),
241
+ };
242
+ }
243
+ if (kind === "conversations") {
244
+ const o = closed(v, ["kind", "entries"]);
245
+ return { kind, entries: list(o.entries, 200, decodeChatSummary) };
246
+ }
247
+ if (kind === "conversation") {
248
+ const o = closed(v, ["kind", "summary", "turns", "before"]);
249
+ return {
250
+ kind,
251
+ summary: nullable(o.summary, decodeChatSummary),
252
+ turns: list(o.turns, 20, decodeChatTurn),
253
+ before: nullable(o.before, decodeId),
254
+ };
255
+ }
256
+ if (kind === "files" || kind === "directories") {
257
+ const o = closed(v, ["kind", "path", "parent", "entries", "limited"]);
258
+ return {
259
+ kind,
260
+ path: text(o.path, 4096),
261
+ parent: nullable(o.parent, (p) => text(p, 4096)),
262
+ limited: bool(o.limited),
263
+ entries: list(o.entries, 200, (v) => {
264
+ const e = closed(v, ["name", "path", "directory"]);
265
+ return {
266
+ name: text(e.name, 1024, true),
267
+ path: text(e.path, 4096, true),
268
+ directory: bool(e.directory),
269
+ };
270
+ }),
271
+ };
272
+ }
273
+ if (kind === "file") {
274
+ const o = closed(v, ["kind", "path", "text", "limited"]);
275
+ return {
276
+ kind,
277
+ path: text(o.path, 4096),
278
+ text: text(o.text, 256 * 1024),
279
+ limited: bool(o.limited),
280
+ };
281
+ }
282
+ if (kind === "projects") {
283
+ const o = closed(v, ["kind", "entries"]);
284
+ return {
285
+ kind,
286
+ entries: list(o.entries, 128, (v) => {
287
+ const e = closed(v, ["path", "current"]);
288
+ return { path: text(e.path, 4096, true), current: bool(e.current) };
289
+ }),
290
+ };
291
+ }
292
+ if (kind === "accepted") {
293
+ const o = closed(v, ["kind", "commandId"]);
294
+ return { kind, commandId: decodeId(o.commandId) };
295
+ }
296
+ const o = closed(v, ["kind", "commandId", "path", "link"]);
297
+ return {
298
+ kind: oneOf(o.kind, ["project"]),
299
+ commandId: decodeId(o.commandId),
300
+ path: text(o.path, 4096, true),
301
+ link: text(o.link, 8192, true),
302
+ };
303
+ }
@@ -295,6 +295,7 @@ export class ServiceCoordination {
295
295
  "access.json",
296
296
  "host-config.json",
297
297
  "history-v2",
298
+ "conversations-v1",
298
299
  ]);
299
300
  this.files.directory(join(this.directory, "history-v2"), false, walks);
300
301
  }