@mycadet/assistant 0.12.8 → 0.12.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/node_modules/@mycadet/ces-client/package.json +1 -1
- package/node_modules/@mycadet/ces-client/src/__tests__/ces-client.test.ts +14 -14
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/node_modules/@mycadet/service-contracts/package.json +1 -1
- package/openapi.yaml +2 -18
- package/package.json +2 -2
- package/src/__tests__/cloud-profile-guard.test.ts +21 -19
- package/src/__tests__/config-set-route.test.ts +12 -8
- package/src/__tests__/credential-health-service.test.ts +4 -2
- package/src/__tests__/get-skill-detail-audit.test.ts +134 -289
- package/src/__tests__/mcp-abort-signal.test.ts +0 -1
- package/src/__tests__/mcp-client-auth.test.ts +0 -1
- package/src/__tests__/mcp-config-secret-boundary.test.ts +44 -40
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +0 -1
- package/src/__tests__/plugin-api-store-credential.test.ts +4 -1
- package/src/__tests__/skills-file-content-endpoint.test.ts +221 -535
- package/src/__tests__/skills-files-catalog-fallback.test.ts +258 -576
- package/src/api/events/show-cloud-login.ts +2 -2
- package/src/cli/commands/cloud/__tests__/callback-routes-list.test.ts +2 -2
- package/src/cli/commands/cloud/index.ts +1 -1
- package/src/cli/commands/config.ts +1 -1
- package/src/config/__tests__/substrate-twin-shadowing.test.ts +36 -26
- package/src/config/env-registry.ts +2 -9
- package/src/config/schemas/ingress.ts +30 -0
- package/src/config/schemas/llm.ts +0 -9
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/substrate-twin-shadowing.ts +12 -17
- package/src/config/sync-gated-profiles.ts +3 -4
- package/src/credential-execution/prompted-credential.ts +1 -10
- package/src/credential-health/credential-health-service.ts +8 -4
- package/src/daemon/credential-transcript-scrub.ts +11 -0
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -0
- package/src/providers/inference/backfill.ts +2 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +100 -57
- package/src/runtime/routes/cloud-routes.ts +5 -5
- package/src/runtime/routes/conversation-query-routes.ts +275 -397
- package/src/runtime/routes/secret-routes.ts +2 -17
- package/src/tools/credentials/store.ts +1 -10
- package/src/util/layout.ts +479 -0
- package/src/util/platform.ts +11 -467
- package/src/workspace/migrations/127-backfill-default-provider.ts +2 -2
package/src/util/platform.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Platform detection and environment-shaping helpers.
|
|
3
|
+
*
|
|
4
|
+
* Directory-layout helpers (every canonical path under the workspace and
|
|
5
|
+
* its data dir) live in `util/layout.ts` — they were never platform
|
|
6
|
+
* concerns. This module re-exports that module for backward compatibility;
|
|
7
|
+
* new code should import layout helpers from `./layout.js` directly.
|
|
8
|
+
*/
|
|
9
|
+
|
|
2
10
|
import { homedir } from "node:os";
|
|
3
|
-
import { delimiter,
|
|
11
|
+
import { delimiter, join } from "node:path";
|
|
4
12
|
|
|
5
|
-
|
|
6
|
-
AVATAR_IMAGE_FILENAME,
|
|
7
|
-
AVATAR_MANIFEST_FILENAME,
|
|
8
|
-
resolveAvatarDir,
|
|
9
|
-
} from "@mycadet/avatar-manifest";
|
|
10
|
-
import { assertTestPathIsEphemeral as assertEphemeralInTests } from "@mycadet/environments/test-path-guard";
|
|
13
|
+
export * from "./layout.js";
|
|
11
14
|
|
|
12
15
|
export function isMacOS(): boolean {
|
|
13
16
|
return process.platform === "darwin";
|
|
@@ -108,462 +111,3 @@ export function normalizeAssistantId(assistantId: string): string {
|
|
|
108
111
|
|
|
109
112
|
return assistantId;
|
|
110
113
|
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Returns the internal data directory ($CADET_WORKSPACE_DIR/data). Runtime
|
|
114
|
-
* databases, logs, memory indices, and other internal state live here.
|
|
115
|
-
*/
|
|
116
|
-
export function getDataDir(): string {
|
|
117
|
-
return join(getWorkspaceDir(), "data");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Returns the path to the config-quarantine notice sentinel
|
|
122
|
-
* (`<workspace>/data/config-quarantine-notice.json`).
|
|
123
|
-
*
|
|
124
|
-
* Written by the config loader when a corrupt `config.json` is quarantined and
|
|
125
|
-
* read by the per-turn `config-quarantine-notice` injector. Lives under the
|
|
126
|
-
* internal data dir (runtime state, config-free to resolve) rather than the
|
|
127
|
-
* user-facing workspace root because it is daemon-written bookkeeping, not a
|
|
128
|
-
* file the user edits. The path resolves without loading config, so it is safe
|
|
129
|
-
* to call during early-boot config load before the DB or `getConfig().dataDir`
|
|
130
|
-
* exist.
|
|
131
|
-
*/
|
|
132
|
-
export function getConfigQuarantineNoticePath(): string {
|
|
133
|
-
return join(getDataDir(), "config-quarantine-notice.json");
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Returns the path to the config-validation-reset notice sentinel
|
|
138
|
-
* (`<workspace>/data/config-validation-reset-notice.json`).
|
|
139
|
-
*
|
|
140
|
-
* Written by the config loader when `config.json` parses as JSON but fails
|
|
141
|
-
* schema validation so hard that the loader falls back to *full* defaults
|
|
142
|
-
* (e.g. an unknown key that masks a `superRefine` violation until the offending
|
|
143
|
-
* key is stripped). Unlike a quarantine, the on-disk file is left untouched —
|
|
144
|
-
* the user's customized values are still present but inactive until the invalid
|
|
145
|
-
* entries are fixed. Read by the per-turn `config-validation-reset-notice`
|
|
146
|
-
* injector so the agent can explain a settings/connection change the user did
|
|
147
|
-
* not make. Lives beside the quarantine sentinel under the internal data dir
|
|
148
|
-
* for the same reasons (daemon-written bookkeeping; resolves without loading
|
|
149
|
-
* config, so it is safe during early-boot config load).
|
|
150
|
-
*/
|
|
151
|
-
export function getConfigValidationResetNoticePath(): string {
|
|
152
|
-
return join(getDataDir(), "config-validation-reset-notice.json");
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Returns the embedding models directory ($CADET_WORKSPACE_DIR/embedding-models).
|
|
157
|
-
* Downloaded embedding runtime (onnxruntime-node, transformers bundle, model weights)
|
|
158
|
-
* is stored here, downloaded post-hatch rather than shipped with the app.
|
|
159
|
-
*/
|
|
160
|
-
export function getEmbeddingModelsDir(): string {
|
|
161
|
-
return join(getWorkspaceDir(), "embedding-models");
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Returns the sandbox root directory (<workspace>/data/sandbox).
|
|
166
|
-
* Global sandbox state lives under this directory.
|
|
167
|
-
*/
|
|
168
|
-
export function getSandboxRootDir(): string {
|
|
169
|
-
return join(getDataDir(), "sandbox");
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Returns the default sandbox working directory ($CADET_WORKSPACE_DIR).
|
|
174
|
-
* This is the workspace root — tool working directories should use this
|
|
175
|
-
* path unless explicitly overridden.
|
|
176
|
-
*/
|
|
177
|
-
export function getSandboxWorkingDir(): string {
|
|
178
|
-
return getWorkspaceDir();
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Returns the sounds directory ($CADET_WORKSPACE_DIR/data/sounds).
|
|
183
|
-
* Custom sound files and sound configuration live here.
|
|
184
|
-
*/
|
|
185
|
-
export function getSoundsDir(): string {
|
|
186
|
-
return join(getWorkspaceDir(), "data", "sounds");
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/** Returns the avatar directory ($CADET_WORKSPACE_DIR/data/avatar). */
|
|
190
|
-
export function getAvatarDir(): string {
|
|
191
|
-
return resolveAvatarDir(getWorkspaceDir());
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/** Returns the canonical avatar image path ($CADET_WORKSPACE_DIR/data/avatar/avatar-image.png). */
|
|
195
|
-
export function getAvatarImagePath(): string {
|
|
196
|
-
return join(getAvatarDir(), AVATAR_IMAGE_FILENAME);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/** Returns the canonical avatar manifest path ($CADET_WORKSPACE_DIR/data/avatar/avatar.json). */
|
|
200
|
-
export function getAvatarManifestPath(): string {
|
|
201
|
-
return join(getAvatarDir(), AVATAR_MANIFEST_FILENAME);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
export function getPidPath(): string {
|
|
205
|
-
return join(getWorkspaceDir(), "cadet.pid");
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export function getDbPath(): string {
|
|
209
|
-
return join(getDataDir(), "db", "assistant.db");
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Returns the directory where logs live: `<dataDir>/logs/`. Files rotate
|
|
214
|
-
* daily (`assistant-YYYY-MM-DD.log`), so callers ask for the directory and
|
|
215
|
-
* let the logger own the filename.
|
|
216
|
-
*/
|
|
217
|
-
export function getLogsDir(): string {
|
|
218
|
-
return join(getDataDir(), "logs");
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export function getHistoryPath(): string {
|
|
222
|
-
return join(getDataDir(), "history");
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/** Returns $CADET_WORKSPACE_DIR/signals — the directory for IPC signal files. */
|
|
226
|
-
export function getSignalsDir(): string {
|
|
227
|
-
return join(getWorkspaceDir(), "signals");
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// These expose specific root-level file paths so callers don't need to
|
|
231
|
-
// import getRootDir() directly. getRootDir() is intentionally unexported.
|
|
232
|
-
|
|
233
|
-
/** Returns the path to the daemon stderr log ($CADET_WORKSPACE_DIR/logs/daemon-stderr.log). */
|
|
234
|
-
export function getDaemonStderrLogPath(): string {
|
|
235
|
-
return join(getWorkspaceDir(), "logs", "daemon-stderr.log");
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/** Returns the path to the daemon startup lock file ($CADET_WORKSPACE_DIR/daemon-startup.lock). */
|
|
239
|
-
export function getDaemonStartupLockPath(): string {
|
|
240
|
-
return join(getWorkspaceDir(), "daemon-startup.lock");
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/** Returns the directory for externally-installed packages ($CADET_WORKSPACE_DIR/external). */
|
|
244
|
-
export function getExternalDir(): string {
|
|
245
|
-
return join(getWorkspaceDir(), "external");
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/** Returns the directory for installed binaries ($CADET_WORKSPACE_DIR/bin). */
|
|
249
|
-
export function getBinDir(): string {
|
|
250
|
-
return join(getWorkspaceDir(), "bin");
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Returns the path to the dot-env file (`<cadetRoot>/.env`, the parent of
|
|
255
|
-
* the workspace directory). Stays outside the workspace sandbox boundary
|
|
256
|
-
* because it contains secrets.
|
|
257
|
-
*/
|
|
258
|
-
export function getDotEnvPath(): string {
|
|
259
|
-
return join(dirname(getWorkspaceDir()), ".env");
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/** Returns the path to the embed-worker PID file ($CADET_WORKSPACE_DIR/embed-worker.pid). */
|
|
263
|
-
export function getEmbedWorkerPidPath(): string {
|
|
264
|
-
return join(getWorkspaceDir(), "embed-worker.pid");
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/** Returns the path to the memory-worker PID file ($CADET_WORKSPACE_DIR/memory-worker.pid). */
|
|
268
|
-
export function getMemoryWorkerPidPath(): string {
|
|
269
|
-
return join(getWorkspaceDir(), "memory-worker.pid");
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/** Returns the path to the schedule-worker PID file ($CADET_WORKSPACE_DIR/schedule-worker.pid). */
|
|
273
|
-
export function getScheduleWorkerPidPath(): string {
|
|
274
|
-
return join(getWorkspaceDir(), "schedule-worker.pid");
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Returns the directory where the resource monitor persists its forensics
|
|
279
|
-
* ($CADET_WORKSPACE_DIR/data/monitoring). Lives on the workspace volume (the
|
|
280
|
-
* PVC) so the sample ring buffer and high-memory snapshots survive an OOM
|
|
281
|
-
* SIGKILL that resets all in-process state. Git-ignored from the workspace
|
|
282
|
-
* tree (see `data/monitoring/` in git-service.ts) so the assistant's own
|
|
283
|
-
* telemetry is not auto-committed as user changes.
|
|
284
|
-
*/
|
|
285
|
-
export function getMonitoringDataDir(): string {
|
|
286
|
-
return join(getDataDir(), "monitoring");
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/** Returns the path to the monitoring PID file, under the monitor data dir. */
|
|
290
|
-
export function getMonitoringPidPath(): string {
|
|
291
|
-
return join(getMonitoringDataDir(), "monitoring.pid");
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Root holding every daemon-managed subprocess's runtime directory
|
|
296
|
-
* ($CADET_WORKSPACE_DIR/procs). Each managed subprocess keeps its IPC socket,
|
|
297
|
-
* PID file, and per-process scratch under `procs/<name>/`, so `ls procs` is a
|
|
298
|
-
* census of managed subprocesses and cleanup is one `rm -rf` of the subdir.
|
|
299
|
-
*/
|
|
300
|
-
export function getProcsDir(): string {
|
|
301
|
-
return join(getWorkspaceDir(), "procs");
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/** The runtime directory for one managed subprocess: `procs/<name>/`. */
|
|
305
|
-
export function getProcDir(name: string): string {
|
|
306
|
-
return join(getProcsDir(), name);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/** Create (if needed) and return a cloud subprocess's runtime directory. */
|
|
310
|
-
export function ensureProcDir(name: string): string {
|
|
311
|
-
const dir = getProcDir(name);
|
|
312
|
-
mkdirSync(dir, { recursive: true });
|
|
313
|
-
return dir;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* The IPC socket a cloud subprocess binds and the daemon connects to
|
|
318
|
-
* (`procs/<name>/ipc.sock`). The basename is fixed — the directory already
|
|
319
|
-
* carries the subprocess name. Keep it short: Unix `sun_path` is ~104–108 bytes.
|
|
320
|
-
*/
|
|
321
|
-
export function getProcSocketPath(name: string): string {
|
|
322
|
-
return join(getProcDir(name), "ipc.sock");
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/** The PID file a cloud subprocess writes on readiness (`procs/<name>/<name>.pid`). */
|
|
326
|
-
export function getProcPidPath(name: string): string {
|
|
327
|
-
return join(getProcDir(name), `${name}.pid`);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// --- Live-workspace guard for test processes --------------------------------
|
|
331
|
-
//
|
|
332
|
-
// A test process must never resolve the workspace (or the cadet root) to a
|
|
333
|
-
// real, non-temp directory: production code exercised by a test would then
|
|
334
|
-
// read and destructively write live state. The containment assertion is
|
|
335
|
-
// shared with the gateway via @mycadet/environments/test-path-guard;
|
|
336
|
-
// src/__tests__/assert-not-live-db.ts keeps its own containment check
|
|
337
|
-
// because test machinery must not import production dependencies.
|
|
338
|
-
|
|
339
|
-
function assertTestPathIsEphemeral(dir: string): void {
|
|
340
|
-
assertEphemeralInTests(dir, {
|
|
341
|
-
// Shared with assertTestDbIsIsolated() in persistence/db-connection.ts.
|
|
342
|
-
allowEnvVar: "CADET_ALLOW_REAL_WORKSPACE_IN_TESTS",
|
|
343
|
-
runHint: "Run tests from the assistant package root.",
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Returns the workspace root for user-facing state.
|
|
349
|
-
*
|
|
350
|
-
* When the CADET_WORKSPACE_DIR env var is set, returns that value (used in
|
|
351
|
-
* containerized deployments where the workspace is a separate volume).
|
|
352
|
-
* Otherwise falls back to `<cwd>/workspace`.
|
|
353
|
-
*
|
|
354
|
-
* In test processes the resolved directory must live under `os.tmpdir()`
|
|
355
|
-
* (see the live-workspace guard above); anything else throws.
|
|
356
|
-
*/
|
|
357
|
-
export function getWorkspaceDir(): string {
|
|
358
|
-
const dir =
|
|
359
|
-
process.env.CADET_WORKSPACE_DIR?.trim() || join(process.cwd(), "workspace");
|
|
360
|
-
assertTestPathIsEphemeral(dir);
|
|
361
|
-
return dir;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Returns a display-friendly workspace path for embedding in agent-facing text
|
|
366
|
-
* (skill bodies, tool descriptions). Replaces the home directory prefix with `~`
|
|
367
|
-
* so paths stay concise and portable across machines.
|
|
368
|
-
*
|
|
369
|
-
* Examples:
|
|
370
|
-
* /Users/alice/.local/share/cadet/workspace → ~/.local/share/cadet/workspace
|
|
371
|
-
* /workspace → /workspace
|
|
372
|
-
*/
|
|
373
|
-
export function getWorkspaceDirDisplay(): string {
|
|
374
|
-
return formatHomeRelativePath(getWorkspaceDir(), homedir());
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
interface PathOperations {
|
|
378
|
-
isAbsolute(path: string): boolean;
|
|
379
|
-
relative(from: string, to: string): string;
|
|
380
|
-
sep: string;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
export function formatHomeRelativePath(
|
|
384
|
-
absolutePath: string,
|
|
385
|
-
homePath: string,
|
|
386
|
-
pathOperations: PathOperations = { isAbsolute, relative, sep },
|
|
387
|
-
): string {
|
|
388
|
-
const relativeToHome = pathOperations.relative(homePath, absolutePath);
|
|
389
|
-
if (relativeToHome === "") {
|
|
390
|
-
return "~";
|
|
391
|
-
}
|
|
392
|
-
if (
|
|
393
|
-
relativeToHome !== ".." &&
|
|
394
|
-
!relativeToHome.startsWith(`..${pathOperations.sep}`) &&
|
|
395
|
-
!pathOperations.isAbsolute(relativeToHome)
|
|
396
|
-
) {
|
|
397
|
-
return `~${pathOperations.sep}${relativeToHome}`;
|
|
398
|
-
}
|
|
399
|
-
return absolutePath;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/** Returns $CADET_WORKSPACE_DIR/config.json */
|
|
403
|
-
export function getWorkspaceConfigPath(): string {
|
|
404
|
-
return join(getWorkspaceDir(), "config.json");
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/** Filename for the user-owned Agent Plugins MCP document. */
|
|
408
|
-
export const WORKSPACE_MCP_FILENAME = "mcp.json";
|
|
409
|
-
|
|
410
|
-
/** Returns $CADET_WORKSPACE_DIR/mcp.json */
|
|
411
|
-
export function getWorkspaceMcpConfigPath(): string {
|
|
412
|
-
return join(getWorkspaceDir(), WORKSPACE_MCP_FILENAME);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/** Returns $CADET_WORKSPACE_DIR/skills */
|
|
416
|
-
export function getWorkspaceSkillsDir(): string {
|
|
417
|
-
return join(getWorkspaceDir(), "skills");
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/** Returns $CADET_WORKSPACE_DIR/hooks */
|
|
421
|
-
export function getWorkspaceHooksDir(): string {
|
|
422
|
-
return join(getWorkspaceDir(), "hooks");
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Returns `<workspaceDir>/plugins` — the directory scanned by the user plugin
|
|
427
|
-
* loader at daemon startup. Writes here are security-sensitive: any
|
|
428
|
-
* `register.{ts,js}` will be dynamic-imported on next restart, so the file
|
|
429
|
-
* risk classifier escalates writes under this path to High.
|
|
430
|
-
*/
|
|
431
|
-
export function getWorkspacePluginsDir(): string {
|
|
432
|
-
return join(getWorkspaceDir(), "plugins");
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Returns $CADET_WORKSPACE_DIR/tools — user-defined tool overrides.
|
|
437
|
-
*
|
|
438
|
-
* Each subdirectory `<name>/` provides either an override of a core tool of
|
|
439
|
-
* the same name or a net-new tool. The single canonical location removes
|
|
440
|
-
* the "which plugin wins" ambiguity that would arise if multiple plugins
|
|
441
|
-
* could register competing overrides for the same tool.
|
|
442
|
-
*
|
|
443
|
-
* Files under this directory are dynamic-imported by the workspace-tool
|
|
444
|
-
* loader on daemon start; the file risk classifier escalates writes under
|
|
445
|
-
* this path to High for the same reason `plugins/` is escalated.
|
|
446
|
-
*/
|
|
447
|
-
export function getWorkspaceToolsDir(): string {
|
|
448
|
-
return join(getWorkspaceDir(), "tools");
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Returns $CADET_WORKSPACE_DIR/routes — user-defined HTTP route handlers.
|
|
453
|
-
*
|
|
454
|
-
* Handler modules under this directory are dynamic-imported by the user-route
|
|
455
|
-
* dispatcher and their exported HTTP-method functions are executed on the
|
|
456
|
-
* next matching request, so the file risk classifier escalates writes under
|
|
457
|
-
* this path to High for the same reason `plugins/` and `tools/` are escalated.
|
|
458
|
-
*/
|
|
459
|
-
export function getWorkspaceRoutesDir(): string {
|
|
460
|
-
return join(getWorkspaceDir(), "routes");
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Returns $CADET_WORKSPACE_DIR/workflows — saved (named) workflow scripts.
|
|
465
|
-
*
|
|
466
|
-
* A file here becomes a saved workflow whose source is executed (in the sandbox,
|
|
467
|
-
* and unattended when triggered by a schedule), so the file risk classifier
|
|
468
|
-
* escalates writes under this path to High like `tools/` and `routes/`.
|
|
469
|
-
*/
|
|
470
|
-
export function getWorkspaceWorkflowsDir(): string {
|
|
471
|
-
return join(getWorkspaceDir(), "workflows");
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/** Returns $CADET_WORKSPACE_DIR/deprecated — transitional files slated for removal. */
|
|
475
|
-
export function getDeprecatedDir(): string {
|
|
476
|
-
return join(getWorkspaceDir(), "deprecated");
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/** Returns $CADET_WORKSPACE_DIR/conversations */
|
|
480
|
-
export function getConversationsDir(): string {
|
|
481
|
-
return join(getWorkspaceDir(), "conversations");
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/** Returns the workspace path for a prompt file (e.g. IDENTITY.md, SOUL.md). */
|
|
485
|
-
export function getWorkspacePromptPath(file: string): string {
|
|
486
|
-
return join(getWorkspaceDir(), file);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Returns `<workspaceDir>/prompts/system` — the workspace override layer for
|
|
491
|
-
* system prompt sections. Layout: `prompts/system/<NN-name>.md`.
|
|
492
|
-
*
|
|
493
|
-
* The bundled section registry (`prompts/templates/system-sections.ts`) is
|
|
494
|
-
* the source of default truth; a file here with the same id replaces the
|
|
495
|
-
* bundled body (or, stripped to nothing, silences it), and a brand-new
|
|
496
|
-
* `<NN-name>` adds a workspace-only section. Because that includes the
|
|
497
|
-
* security-policy sections, writes under this directory are gated as a
|
|
498
|
-
* control-plane prompt surface (`permissions/workspace-policy.ts`).
|
|
499
|
-
*/
|
|
500
|
-
export function getWorkspaceSystemPromptDir(): string {
|
|
501
|
-
return join(getWorkspaceDir(), "prompts", "system");
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// Cloud profiler runs live under <workspace>/data/profiler/. These
|
|
505
|
-
// helpers enforce a single canonical layout so every runtime caller
|
|
506
|
-
// resolves the same paths.
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* Returns the profiler root directory (<workspace>/data/profiler).
|
|
510
|
-
* All profiler state (runs directory, global metadata) lives here.
|
|
511
|
-
*/
|
|
512
|
-
export function getProfilerRootDir(): string {
|
|
513
|
-
return join(getDataDir(), "profiler");
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Returns the profiler runs directory (<workspace>/data/profiler/runs).
|
|
518
|
-
* Each completed or active profiler run gets its own sub-directory here.
|
|
519
|
-
*/
|
|
520
|
-
export function getProfilerRunsDir(): string {
|
|
521
|
-
return join(getProfilerRootDir(), "runs");
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Returns the directory for a specific profiler run by ID
|
|
526
|
-
* (<workspace>/data/profiler/runs/<runId>).
|
|
527
|
-
*/
|
|
528
|
-
export function getProfilerRunDir(runId: string): string {
|
|
529
|
-
return join(getProfilerRunsDir(), runId);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
export function ensureDataDir(): void {
|
|
533
|
-
const workspace = getWorkspaceDir();
|
|
534
|
-
const wsData = join(workspace, "data");
|
|
535
|
-
const dirs = [
|
|
536
|
-
// Workspace dirs
|
|
537
|
-
workspace,
|
|
538
|
-
join(workspace, "signals"),
|
|
539
|
-
join(workspace, "skills"),
|
|
540
|
-
join(workspace, "routes"),
|
|
541
|
-
join(workspace, "embedding-models"),
|
|
542
|
-
join(workspace, "conversations"),
|
|
543
|
-
join(workspace, "logs"),
|
|
544
|
-
join(workspace, "external"),
|
|
545
|
-
join(workspace, "bin"),
|
|
546
|
-
// Data sub-dirs under workspace
|
|
547
|
-
wsData,
|
|
548
|
-
join(wsData, "db"),
|
|
549
|
-
join(wsData, "qdrant"),
|
|
550
|
-
join(wsData, "logs"),
|
|
551
|
-
join(wsData, "memory"),
|
|
552
|
-
join(wsData, "memory", "knowledge"),
|
|
553
|
-
join(wsData, "apps"),
|
|
554
|
-
join(wsData, "attachments"),
|
|
555
|
-
join(wsData, "sounds"),
|
|
556
|
-
];
|
|
557
|
-
for (const dir of dirs) {
|
|
558
|
-
if (!existsSync(dir)) {
|
|
559
|
-
mkdirSync(dir, { recursive: true });
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
// Lock down the root directory so only the owner can traverse it.
|
|
563
|
-
// Runtime files (socket, session token, PID) live directly under root.
|
|
564
|
-
try {
|
|
565
|
-
chmodSync(dirname(workspace), 0o700);
|
|
566
|
-
} catch {
|
|
567
|
-
// Non-fatal: some filesystems don't support Unix permissions
|
|
568
|
-
}
|
|
569
|
-
}
|
|
@@ -83,7 +83,7 @@ export const backfillDefaultProviderMigration: WorkspaceMigration = {
|
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
const provider =
|
|
86
|
+
const provider = isCloud() ? "cadet" : resolveProviderSignal(llm);
|
|
87
87
|
if (provider === undefined) {
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
@@ -117,7 +117,7 @@ function isValidDefaultProvider(value: unknown): boolean {
|
|
|
117
117
|
|
|
118
118
|
// Frozen copy of config/env-registry.ts's IS_CLOUD read — migration
|
|
119
119
|
// modules must not import from config modules (workspace/migrations/AGENTS.md).
|
|
120
|
-
function
|
|
120
|
+
function isCloud(): boolean {
|
|
121
121
|
return process.env.IS_CLOUD === "true" || process.env.IS_CLOUD === "1";
|
|
122
122
|
}
|
|
123
123
|
|