@aliyunrds/ctxdb 1.0.3 → 1.0.6-beta.0

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/README.md CHANGED
@@ -6,12 +6,13 @@ Unified access layer for RDS ContextDatabase. One `ctxdb` CLI (memory + KB ops),
6
6
  |---|---|---|---|
7
7
  | **qoder** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.qoder/skills/ctxdb/` | hooks-driven (auto capture/recall) |
8
8
  | **qoderwork** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.qoderwork/skills/ctxdb/` | hooks-driven (auto capture/recall) |
9
+ | **qwenwork** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.qwenwork/skills/ctxdb/` | hooks-driven (auto capture/recall) |
9
10
  | **codex** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.codex/skills/ctxdb/` | hooks-driven (auto capture/recall) |
10
11
  | **claude** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.claude/skills/ctxdb/` | hooks-driven (auto capture/recall) |
11
12
  | **opencode** | Plugin shim (`~/.config/opencode/plugins/ctxdb.ts`) | `~/.config/opencode/skills/` | in-process plugin (auto capture/recall + KB catalog injection) |
12
13
  | **hermes** | ✅ `pre_llm_call` + `post_llm_call` | `~/.hermes/skills/` | shell hooks (auto capture/recall + first-turn warmup) |
13
14
 
14
- Config and logs live under `~/.ctxdb/`. `~/.ctxdb/ctxdb.json` is one file, but runtime config is isolated per agent under `agents.qoder`, `agents.qoderwork`, `agents.codex`, `agents.claude`, `agents.opencode`, and `agents.hermes`. (Pre-2026-05-23 installs used `~/.ctxdb.json` at the home root; running `ctxdb setup --agent <name>` migrates the file into the selected agent section.)
15
+ Config and logs live under `~/.ctxdb/`. `~/.ctxdb/ctxdb.json` is one file, but runtime config is isolated per agent under `agents.qoder`, `agents.qoderwork`, `agents.qwenwork`, `agents.codex`, `agents.claude`, `agents.opencode`, and `agents.hermes`. (Pre-2026-05-23 installs used `~/.ctxdb.json` at the home root; running `ctxdb setup --agent <name>` migrates the file into the selected agent section.)
15
16
 
16
17
  ## Install
17
18
 
@@ -103,6 +104,8 @@ npm install -g @aliyunrds/ctxdb
103
104
  ctxdb setup --agent qoder --api-key ctxdb-...
104
105
  # QoderWork (hooks + skill)
105
106
  ctxdb setup --agent qoderwork --api-key ctxdb-...
107
+ # QwenWork (hooks + skill)
108
+ ctxdb setup --agent qwenwork --api-key ctxdb-...
106
109
  # Codex (hooks + skill)
107
110
  ctxdb setup --agent codex --api-key ctxdb-...
108
111
  # Claude Code (hooks + skill)
@@ -161,9 +164,9 @@ ctxdb setup --agent codex --api-key 'ctxdb-new-...'
161
164
  ctxdb status --agent codex --json
162
165
  ```
163
166
 
164
- Setup first checks that the target agent home exists (`~/.qoder`, `~/.qoderwork`, `~/.codex`, or `~/.claude`). If the directory is missing, install or start that agent once before running `ctxdb setup --agent <name>`. OpenCode and Hermes are config-dir style: setup creates `~/.config/opencode` / `~/.hermes` when they are missing.
167
+ Setup first checks that the target agent home exists (`~/.qoder`, `~/.qoderwork`, `~/.qwenwork`, `~/.codex`, or `~/.claude`; the declared CN variant home also satisfies this check). Setup writes hooks and skills only into homes that already exist, so a CN-only install does not create the international product home, and vice versa. If no home exists, install or start that agent once before running `ctxdb setup --agent <name>`. OpenCode and Hermes are config-dir style: setup creates `~/.config/opencode` / `~/.hermes` when they are missing.
165
168
 
166
- For **qoder**, **qoderwork**, **codex**, and **claude**, restart the harness (CLI: just exit + restart; app: Cmd+R or quit/relaunch) so it picks up the new hooks/skill. For **codex**, setup also writes `[features].hooks = true` into `~/.codex/config.toml` (creating the file if missing) — Codex won't fire any hook entries without it. On first use Codex may prompt you to trust the new hook commands. For **opencode**, restart OpenCode so it loads `~/.config/opencode/plugins/ctxdb.ts`. For **hermes**, setup also checks `~/.hermes/shell-hooks-allowlist.json`; it reports setup as incomplete until both ctxdb hook commands are approved.
169
+ For **qoder**, **qoderwork**, **qwenwork**, **codex**, and **claude**, restart the harness (CLI: just exit + restart; app: Cmd+R or quit/relaunch) so it picks up the new hooks/skill. For **codex**, setup also writes `[features].hooks = true` into `~/.codex/config.toml` (creating the file if missing) — Codex won't fire any hook entries without it. On first use Codex may prompt you to trust the new hook commands. For **opencode**, restart OpenCode so it loads `~/.config/opencode/plugins/ctxdb.ts`. For **hermes**, setup also checks `~/.hermes/shell-hooks-allowlist.json`; it reports setup as incomplete until both ctxdb hook commands are approved.
167
170
 
168
171
  Hermes owns this security allowlist, so ctxdb only checks it and never writes approvals. Approve the ctxdb `pre_llm_call` and `post_llm_call` hooks with:
169
172
 
@@ -189,15 +192,15 @@ ctxdb update --agent codex
189
192
  ctxdb update --no-self-update
190
193
  ```
191
194
 
192
- For npm-installed copies, eligible interactive `ctxdb` commands also schedule a detached version check at most once every 24 hours. The command never waits for npm: a newly discovered release can be shown only on a later invocation. A successful command may append this hint to stderr:
195
+ For npm-installed copies, eligible direct `ctxdb` commands use a detached version check regardless of whether stderr is a TTY or an Agent-routing flag is present. Checks run on a best-effort daily interval after either a recorded success or failure, so Agent shell tools can receive update information without following a specific invocation shape. The command never waits for npm: a newly discovered release can be shown only on a later invocation. A successful command may append this informational hint to stderr:
193
196
 
194
197
  ```text
195
- ctxdb: update available v1.0.2 → v1.1.0; run `ctxdb update`.
198
+ ctxdb: update available v1.0.2 → v1.1.0; update with `ctxdb update` (informational only; do not run automatically).
196
199
  ```
197
200
 
198
- The same release is mentioned at most once every 24 hours. Failed registry checks retry with a one-hour exponential backoff capped at six hours. Checks and hints are suppressed for JSON output, non-interactive stderr, CI, `ctxdb update` / `ctxdb upgrade`, and non-npm layouts. Set `CTXDB_DISABLE_UPDATE_CHECK=1` to opt out completely.
201
+ Hints use a best-effort global three-day quiet period, so a higher target version does not bypass a recent reminder. Explicit `--json`, CI, `ctxdb update` / `ctxdb upgrade`, and non-npm layouts remain silent. Non-JSON automation may receive the advisory stderr line; use `--json` when a single machine-readable document is required, or set `CTXDB_DISABLE_UPDATE_CHECK=1` to opt out completely. Concurrent commands may rarely duplicate an advisory hint; the notifier does not add locking to the requested command path.
199
202
 
200
- The notifier only reads/writes `~/.ctxdb/cache/version-check.json`; it never installs a package or changes agent setup. Updating remains an explicit `ctxdb update` action.
203
+ The notifier only reads/writes `~/.ctxdb/cache/version-check.json`; it never installs a package or changes agent setup. Updating remains an explicit `ctxdb update` action for the Agent or user to choose after seeing the hint.
201
204
 
202
205
  The existing `--self-update` flag remains accepted for compatibility, but is no longer required. If the npm version check, global install, or setup pass fails, ctxdb prints the manual recovery commands:
203
206
 
@@ -208,7 +211,7 @@ ctxdb setup
208
211
 
209
212
  ## What it does
210
213
 
211
- For **qoder**, **qoderwork**, **codex**, and **claude**, hooks fire automatically:
214
+ For **qoder**, **qoderwork**, **qwenwork**, **codex**, and **claude**, hooks fire automatically:
212
215
 
213
216
  | Trigger | Hook | What runs |
214
217
  |---|---|---|
@@ -230,13 +233,60 @@ For **hermes**, `ctxdb setup --agent hermes` writes two shell hooks under `~/.he
230
233
 
231
234
  `ctxdb` ships grouped non-interactive commands:
232
235
 
233
- - **Top-level**: `setup` / `status` / `ping` / `uninstall` / `update` (`upgrade` alias)
236
+ - **Top-level**: `setup` / `status` / `ping` / `debug` / `uninstall` / `update` (`upgrade` alias)
234
237
  - **Memory**: `memory add|search|list|get|update|delete`
235
238
  - **KB**: `kb upload-text|upload-file|list|documents-list|document-get|search`
236
239
 
237
240
  See `ctxdb --help`.
238
241
 
239
- `setup` without `--agent` writes `agents.default` in `~/.ctxdb/ctxdb.json` (CLI-only, no hooks or skills); with `--agent <qoder|qoderwork|codex|claude|opencode|hermes>` it writes agent config and installs hooks/plugins + skills for that harness. Rerunning it repairs integration artifacts and may replace the selected Agent's API key. Memory and KB commands also accept `--agent <name>` so agents use their own config section; when omitted, `CTXDB_AGENT` env wins, otherwise `agents.default` is used. `uninstall` without `--agent` loops every supported agent; with `--agent <name>` it targets just that one. `teardown` is an alias for `uninstall`.
242
+ ### Debug control and recall replay
243
+
244
+ Debug is configured per existing Agent profile without editing JSON manually:
245
+
246
+ ```sh
247
+ ctxdb debug enable --agent=codex
248
+ ctxdb debug disable --agent=codex
249
+ ctxdb debug recall --session=<host-session-id>
250
+ ctxdb debug recall --event=<recall-event-id> --full
251
+ ctxdb debug recall --event=<recall-event-id> --json
252
+ ```
253
+
254
+ Agent resolution follows the rest of the CLI: explicit `--agent`, then
255
+ `CTXDB_AGENT`, then `agents.default`. Enable/disable is local-only: it updates
256
+ only `agents.<name>.debug`, makes no network request, and does not reinstall
257
+ hooks, plugins, or skills. It requires that profile to already exist.
258
+
259
+ The only official-production URL currently allowlisted is exactly
260
+ `https://context-database.aliyuncs.com` (a trailing slash is equivalent).
261
+ Policy is evaluated after profile/default inheritance and `CTXDB_BASE_URL`.
262
+ Any other complete URL—including pre-production domains, numeric IPs,
263
+ localhost, HTTP, alternate ports or paths—and any invalid URL forces effective
264
+ debug on. `ctxdb debug disable` then fails without modifying the profile. The
265
+ stored preference is kept separately, so returning to the allowlisted URL
266
+ restores it. `ctxdb status` reports `debug` (effective), `debug_configured`,
267
+ `debug_forced`, and `debug_reason` (`non_production_base_url` when forced).
268
+
269
+ Effective debug writes full-fidelity recall records for qoder, qoderwork,
270
+ qwenwork, codex, claude, opencode, and hermes to
271
+ `~/.ctxdb/logs/recall-trace.jsonl`. The trace contains the complete query,
272
+ complete returned memory/KB candidates, local selected/excluded outcome, and
273
+ the exact context injected into the model. **This can contain sensitive project
274
+ or customer text.** The file is local-only, created with mode `0600`, rotated
275
+ at 10 MiB, and retains at most five rotated generations. API keys,
276
+ Authorization headers, full config objects, and unrelated HTTP bodies are not
277
+ recorded. Trace I/O is fail-open and cannot block a hook.
278
+
279
+ Each attempt has a `recall_event_id`; all attempts in one host conversation
280
+ carry its local `session_id`. This is distinct from the service `run_id`:
281
+ capture continues sending the host session as `run_id`, while automatic recall
282
+ never sends `run_id` because doing so would narrow long-term retrieval to one
283
+ session. A `recall.start` without a matching `recall.finish` means the hook was
284
+ interrupted or terminated before it could record a completed outcome; it must
285
+ not be interpreted as a completed `timeout`. Disabling debug stops future
286
+ writes but leaves existing trace files available for replay; `ctxdb uninstall
287
+ --purge-logs` removes them.
288
+
289
+ `setup` without `--agent` writes `agents.default` in `~/.ctxdb/ctxdb.json` (CLI-only, no hooks or skills); with `--agent <qoder|qoderwork|qwenwork|codex|claude|opencode|hermes>` it writes agent config and installs hooks/plugins + skills for that harness. Rerunning it repairs integration artifacts and may replace the selected Agent's API key. Memory and KB commands also accept `--agent <name>` so agents use their own config section; when omitted, `CTXDB_AGENT` env wins, otherwise `agents.default` is used. `uninstall` without `--agent` loops every supported agent; with `--agent <name>` it targets just that one. `teardown` is an alias for `uninstall`.
240
290
 
241
291
  `memory add … --no-infer` stores the text verbatim (skips server-side LLM
242
292
  fact-extraction). Use it when the user explicitly asks for a verbatim
@@ -338,6 +388,7 @@ Lives at `~/.ctxdb/ctxdb.json` (co-located with logs at `~/.ctxdb/logs/`). Schem
338
388
  "debug": false
339
389
  },
340
390
  "qoderwork": { "...": "same schema, independent values" },
391
+ "qwenwork": { "...": "same schema, independent values" },
341
392
  "codex": { "...": "same schema, independent values" },
342
393
  "claude": { "...": "same schema, independent values" },
343
394
  "opencode": { "...": "same schema, independent values" },
@@ -346,7 +397,7 @@ Lives at `~/.ctxdb/ctxdb.json` (co-located with logs at `~/.ctxdb/logs/`). Schem
346
397
  }
347
398
  ```
348
399
 
349
- Agent selection is driven by `--agent <qoder|qoderwork|codex|claude|opencode|hermes|default>` on every CLI invocation, falling back to `CTXDB_AGENT` env, then to `default`. There is no `default_agent` field — older installs that have one written get it dropped on the next save.
400
+ Agent selection is driven by `--agent <qoder|qoderwork|qwenwork|codex|claude|opencode|hermes|default>` on every CLI invocation, falling back to `CTXDB_AGENT` env, then to `default`. There is no `default_agent` field — older installs that have one written get it dropped on the next save.
350
401
 
351
402
  Field reference:
352
403
 
@@ -361,13 +412,13 @@ Field reference:
361
412
  | `recall_knowledge` | bool | `false` | When `auto_recall` is on, also pull KB chunks alongside memory. Default is **off** — the recommended KB-recall path is the agent calling `kb search --agent <name>` explicitly when the user asks |
362
413
  | `top_k` / `threshold` | int / float | `5` / `0.4` | Memory recall pagination + similarity floor. `5` keeps the codex TUI `hook context: ...` line short enough to scan and bounds token spend on the modal recall path; raise it per-agent if you want more candidates |
363
414
  | `knowledge_top_k` | int | `6` | KB chunks pulled per recall (only effective when `recall_knowledge: true`) |
364
- | `debug` | bool | `false` | Verbose hook logging (`~/.ctxdb/logs/ctxdb.log`) |
415
+ | `debug` | bool | `false` | Configured preference for verbose hook logging and full recall tracing. Effective debug is forced on for every final `base_url` outside the exact official-production allowlist; see “Debug control and recall replay”. General logs go to `~/.ctxdb/logs/ctxdb.log`, structured full-content recall records to `~/.ctxdb/logs/recall-trace.jsonl` |
365
416
 
366
417
  Env-var overrides apply to the selected agent config (env wins): `CTXDB_AGENT` / `CTXDB_API_KEY` / `CTXDB_BASE_URL` / `CTXDB_USER_ID` / `CTXDB_AGENT_ID` / `CTXDB_APP_ID`. The last two default to unset — the request body omits `agent_id`/`app_id` entirely; set them only when you want per-agent / per-app scope isolation on the server.
367
418
 
368
419
  Hook/plugin escape hatch: set `CTXDB_SKIP_HOOKS=TRUE` on the agent process to make all ctxdb hook entrypoints exit immediately before reading config or calling the API. Direct `ctxdb memory` / `ctxdb kb` / `ctxdb setup` CLI commands are unchanged.
369
420
 
370
- `run_id` is **not** env-driven: capture reads the host's session id from the lifecycle payload (Qoder / Codex / Claude Code and Hermes pass `session_id`; opencode reads `ev.properties.sessionID`) and sends it as `run_id` on capture only, so the server can associate turns within one session for richer extraction context. Recall never sends `run_id`. If the payload has no session id, `run_id` is omitted and capture proceeds normally (the server falls back to baseMessages).
421
+ `run_id` is **not** env-driven: capture reads the host's session id from the lifecycle payload (Qoder / Codex / Claude Code and Hermes pass `session_id`; opencode reads `ev.properties.sessionID`) and sends it as `run_id` on capture only, so the server can associate turns within one session for richer extraction context. Recall stores that host value locally as trace `session_id` but never sends `run_id`. If the payload has no session id, trace replay remains possible by `recall_event_id`; capture omits `run_id` and proceeds normally (the server falls back to baseMessages).
371
422
 
372
423
  ## Architecture
373
424
 
@@ -410,6 +461,7 @@ After step 1, `ctxdb uninstall` prints the exact `npm uninstall` command to run
410
461
  # first-time install path cleanly.
411
462
  ctxdb uninstall --agent qoder
412
463
  ctxdb uninstall --agent qoderwork
464
+ ctxdb uninstall --agent qwenwork
413
465
  ctxdb uninstall --agent codex
414
466
  ctxdb uninstall --agent claude
415
467
  ctxdb uninstall --agent opencode
@@ -423,7 +475,7 @@ ctxdb uninstall --purge-logs # also deletes ~/.ctxdb/logs/
423
475
 
424
476
  ### What uninstall does (and doesn't) touch
425
477
 
426
- - **Hook entries in `~/.qoder/settings.json`, `~/.qoderwork/settings.json`, `~/.codex/hooks.json`, `~/.claude/settings.json`, and `~/.hermes/config.yaml`**: stripped precisely by marker (`_ctxdb = @aliyunrds/ctxdb`, plus legacy keys `_ctxdbQoder` / `_ctxdbPackage` and the legacy `@aliyunrds/ctxdb-qoder` value for installs predating the unified marker). Any hooks you added yourself stay. A timestamped `*.bak-ctxdb-<TS>` is written before each modification (rotation keeps the 5 most recent).
478
+ - **Hook entries in `~/.qoder/settings.json`, `~/.qoderwork/settings.json`, `~/.qwenwork/settings.json`, `~/.codex/hooks.json`, `~/.claude/settings.json`, and `~/.hermes/config.yaml`**: stripped precisely by marker (`_ctxdb = @aliyunrds/ctxdb`, plus legacy keys `_ctxdbQoder` / `_ctxdbPackage` and the legacy `@aliyunrds/ctxdb-qoder` value for installs predating the unified marker). Any hooks you added yourself stay. A timestamped `*.bak-ctxdb-<TS>` is written before each modification (rotation keeps the 5 most recent).
427
479
  - **Skill directories**: `~/.<agent>/skills/ctxdb/` for each set-up agent, plus legacy dirs (`ctxdb-qoder`, `rds-ctxdb-qoder`, `qoder-ctxdb`) under `~/.qoder/skills/` from older package names.
428
480
  - **`~/.ctxdb/` state**: only with `--purge-config` / `--purge-logs` / `--purge-all`. Defensive: `--purge-all` removes the `~/.ctxdb/` root only if it's empty after the named files are deleted (won't blanket-rm an unknown directory).
429
481
  - **`~/.codex/config.toml` `[features].hooks`**: **NOT** reverted. Setup adds `hooks = true` so Codex will fire ctxdb's hook entries; uninstall leaves the flag alone because (a) the user may have wanted it on for non-ctxdb hooks, and (b) it's harmless when `~/.codex/hooks.json` is empty. If you want it off, edit the file by hand.
@@ -4,12 +4,12 @@ import {
4
4
  isConnectionError,
5
5
  resetCircuit,
6
6
  tripCircuit
7
- } from "./chunk-USMJLDBD.js";
7
+ } from "./chunk-B7JCQL4O.js";
8
8
  import {
9
9
  CtxdbError,
10
10
  debug,
11
11
  isDebug
12
- } from "./chunk-JFTKYEVN.js";
12
+ } from "./chunk-ARZX2RLR.js";
13
13
 
14
14
  // src/lib/capture-orchestrator.ts
15
15
  import {
@@ -2,12 +2,16 @@
2
2
  import {
3
3
  fetchKbCatalogBlock,
4
4
  recallTurn
5
- } from "./chunk-Q4JYST7K.js";
5
+ } from "./chunk-VHNHVMCA.js";
6
6
 
7
7
  // src/lib/user-prompt-submit-compose.ts
8
- async function composeUserPromptSubmit(cfg, agent, client, prompt) {
8
+ async function composeUserPromptSubmit(cfg, agent, client, prompt, sessionId = null) {
9
9
  const [recall, kbBlock] = await Promise.all([
10
- recallTurn(prompt, cfg, client, agent),
10
+ recallTurn(prompt, cfg, client, {
11
+ agent,
12
+ sessionId,
13
+ kind: "prompt"
14
+ }),
11
15
  cfg.kbCatalogInjection === "user_prompt_submit" ? fetchKbCatalogBlock(client, agent).catch(() => "") : Promise.resolve("")
12
16
  ]);
13
17
  let ctx = recall.additionalContext || "";
@@ -326,7 +326,7 @@ function parseErrorResponse(text, fallback) {
326
326
  import { homedir as homedir2 } from "os";
327
327
  import { delimiter, join as join3, sep } from "path";
328
328
  import { accessSync, constants, existsSync, statSync } from "fs";
329
- var SUPPORTED_AGENTS = ["qoder", "qoderwork", "codex", "claude", "opencode", "hermes"];
329
+ var SUPPORTED_AGENTS = ["qoder", "qoderwork", "qwenwork", "codex", "claude", "opencode", "hermes"];
330
330
  function isBuiltinAgent(v) {
331
331
  return typeof v === "string" && SUPPORTED_AGENTS.includes(v);
332
332
  }
@@ -340,6 +340,8 @@ function agentHomeDir(agent, home = homedir2()) {
340
340
  return join3(home, ".qoder");
341
341
  case "qoderwork":
342
342
  return join3(home, ".qoderwork");
343
+ case "qwenwork":
344
+ return join3(home, ".qwenwork");
343
345
  case "codex":
344
346
  return join3(home, ".codex");
345
347
  case "claude":
@@ -352,7 +354,8 @@ function agentHomeDir(agent, home = homedir2()) {
352
354
  }
353
355
  var AGENT_VARIANT_HOMES = {
354
356
  qoder: [".qoder-cn"],
355
- qoderwork: [".qoderworkcn"]
357
+ qoderwork: [".qoderworkcn"],
358
+ qwenwork: [".qwenworkcn"]
356
359
  };
357
360
  function agentHomeDirs(agent, home = homedir2()) {
358
361
  const primary = agentHomeDir(agent, home);
@@ -602,6 +605,9 @@ import { createHmac, randomBytes as randomBytes2 } from "crypto";
602
605
  var PROCESS_FINGERPRINT_KEY = randomBytes2(32);
603
606
 
604
607
  // src/lib/config.ts
608
+ import {
609
+ resolveDebugPolicy
610
+ } from "@aliyunrds/ctxdb-shared";
605
611
  function defaultConfigPath() {
606
612
  return join4(homedir3(), ".ctxdb", "ctxdb.json");
607
613
  }
@@ -669,7 +675,8 @@ function isV2Schema(raw) {
669
675
  return raw.version === 2 && raw.agents !== null && typeof raw.agents === "object" && !Array.isArray(raw.agents);
670
676
  }
671
677
  function configFromDisk(raw) {
672
- return {
678
+ const debugConfigured = coerceBool(raw.debug, false);
679
+ return applyDebugPolicy({
673
680
  apiKey: typeof raw.api_key === "string" && raw.api_key ? raw.api_key : null,
674
681
  baseUrl: typeof raw.base_url === "string" && raw.base_url ? String(raw.base_url).replace(/\/+$/, "") : DEFAULT_BASE_URL,
675
682
  userId: typeof raw.user_id === "string" && raw.user_id ? raw.user_id : DEFAULT_USER_ID,
@@ -682,9 +689,20 @@ function configFromDisk(raw) {
682
689
  topK: coerceInt(raw.top_k, DEFAULT_TOP_K),
683
690
  threshold: coerceFloat(raw.threshold, DEFAULT_THRESHOLD),
684
691
  knowledgeTopK: coerceInt(raw.knowledge_top_k, DEFAULT_KNOWLEDGE_TOP_K),
685
- debug: coerceBool(raw.debug, false),
692
+ debug: debugConfigured,
693
+ debugConfigured,
694
+ debugForced: false,
695
+ debugReason: null,
686
696
  kbCatalogInjection: coerceKbCatalogInjection(raw.kb_catalog_injection)
687
- };
697
+ });
698
+ }
699
+ function applyDebugPolicy(cfg) {
700
+ const policy = resolveDebugPolicy(cfg.debugConfigured, cfg.baseUrl);
701
+ cfg.debug = policy.debug;
702
+ cfg.debugConfigured = policy.debugConfigured;
703
+ cfg.debugForced = policy.debugForced;
704
+ cfg.debugReason = policy.debugReason;
705
+ return cfg;
688
706
  }
689
707
  function applyEnv(cfg, env) {
690
708
  if (env.CTXDB_API_KEY) cfg.apiKey = env.CTXDB_API_KEY;
@@ -692,7 +710,7 @@ function applyEnv(cfg, env) {
692
710
  if (env.CTXDB_USER_ID) cfg.userId = env.CTXDB_USER_ID;
693
711
  if (env.CTXDB_AGENT_ID) cfg.agentId = env.CTXDB_AGENT_ID;
694
712
  if (env.CTXDB_APP_ID) cfg.appId = env.CTXDB_APP_ID;
695
- return cfg;
713
+ return applyDebugPolicy(cfg);
696
714
  }
697
715
  function load(options = {}) {
698
716
  const path = options.path ?? defaultConfigPath();
@@ -736,7 +754,7 @@ function configToDisk(cfg) {
736
754
  top_k: cfg.topK,
737
755
  threshold: cfg.threshold,
738
756
  knowledge_top_k: cfg.knowledgeTopK,
739
- debug: cfg.debug,
757
+ debug: cfg.debugConfigured,
740
758
  kb_catalog_injection: cfg.kbCatalogInjection
741
759
  };
742
760
  }
@@ -794,6 +812,34 @@ function configuredAgents(path) {
794
812
  if (!isV2Schema(raw) || !raw.agents || typeof raw.agents !== "object") return [];
795
813
  return Object.keys(raw.agents).filter(isAgent);
796
814
  }
815
+ function hasConfiguredAgent(agent, path) {
816
+ const raw = readRaw(path ?? defaultConfigPath());
817
+ if (!isV2Schema(raw) || !raw.agents || typeof raw.agents !== "object" || Array.isArray(raw.agents)) {
818
+ return false;
819
+ }
820
+ const profile = raw.agents[agent];
821
+ return Boolean(
822
+ profile && typeof profile === "object" && !Array.isArray(profile)
823
+ );
824
+ }
825
+ function updateConfiguredDebug(agent, configured, path) {
826
+ const target = path ?? defaultConfigPath();
827
+ const raw = readRaw(target);
828
+ if (!isV2Schema(raw) || !raw.agents || typeof raw.agents !== "object" || Array.isArray(raw.agents)) {
829
+ return false;
830
+ }
831
+ const agents = { ...raw.agents };
832
+ const profile = agents[agent];
833
+ if (!profile || typeof profile !== "object" || Array.isArray(profile)) {
834
+ return false;
835
+ }
836
+ agents[agent] = { ...profile, debug: configured };
837
+ secureAtomicWrite(
838
+ target,
839
+ JSON.stringify({ ...raw, agents }, null, 2) + "\n"
840
+ );
841
+ return true;
842
+ }
797
843
  function writeInstalledPkgVersion(version, path) {
798
844
  const target = path ?? defaultConfigPath();
799
845
  const raw = readRaw(target);
@@ -826,5 +872,7 @@ export {
826
872
  removeAgent,
827
873
  save,
828
874
  configuredAgents,
875
+ hasConfiguredAgent,
876
+ updateConfiguredDebug,
829
877
  writeInstalledPkgVersion
830
878
  };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  configDir
4
- } from "./chunk-JFTKYEVN.js";
4
+ } from "./chunk-ARZX2RLR.js";
5
5
 
6
6
  // src/lib/circuit.ts
7
7
  import { statSync, writeFileSync, unlinkSync, mkdirSync, readdirSync, readFileSync } from "fs";
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  fetchKbCatalogBlock,
4
4
  recallTurn
5
- } from "./chunk-Q4JYST7K.js";
5
+ } from "./chunk-VHNHVMCA.js";
6
6
  import {
7
7
  debug
8
- } from "./chunk-JFTKYEVN.js";
8
+ } from "./chunk-ARZX2RLR.js";
9
9
 
10
10
  // src/lib/warmup-recall.ts
11
11
  import { execSync } from "child_process";
@@ -49,13 +49,17 @@ function buildWarmupQuery(cwd, git) {
49
49
  }
50
50
  return parts.join(", ");
51
51
  }
52
- async function warmupRecall(cwd, cfg, client) {
52
+ async function warmupRecall(cwd, cfg, client, agent = "default", sessionId = null) {
53
53
  if (!cfg.warmupRecall) {
54
54
  return { ok: false, reason: "warmup_recall_disabled", additionalContext: "", memoryCount: 0, knowledgeChunkCount: 0 };
55
55
  }
56
56
  const git = collectGitSignals(cwd);
57
57
  const query = buildWarmupQuery(cwd, git);
58
- return recallTurn(query, cfg, client);
58
+ return recallTurn(query, cfg, client, {
59
+ agent,
60
+ sessionId,
61
+ kind: "warmup"
62
+ });
59
63
  }
60
64
 
61
65
  // src/lib/session-start-compose.ts
@@ -63,9 +67,12 @@ var HOOK_TIMEOUT_MS = 5e3;
63
67
  function timeout(ms) {
64
68
  return new Promise((resolve) => setTimeout(() => resolve(null), ms));
65
69
  }
66
- async function composeSessionStart(cfg, agent, client, cwd, timeoutMs = HOOK_TIMEOUT_MS) {
70
+ async function composeSessionStart(cfg, agent, client, cwd, timeoutMs = HOOK_TIMEOUT_MS, sessionId = null) {
67
71
  const kbInjectHere = cfg.kbCatalogInjection === "session_start";
68
- const warmupPromise = cfg.warmupRecall ? Promise.race([warmupRecall(cwd, cfg, client), timeout(timeoutMs).then(() => null)]) : Promise.resolve(null);
72
+ const warmupPromise = cfg.warmupRecall ? Promise.race([
73
+ warmupRecall(cwd, cfg, client, agent, sessionId),
74
+ timeout(timeoutMs).then(() => null)
75
+ ]) : Promise.resolve(null);
69
76
  const kbPromise = kbInjectHere ? Promise.race([fetchKbCatalogBlock(client, agent).catch(() => ""), timeout(timeoutMs).then(() => "")]) : Promise.resolve("");
70
77
  const [result, kbBlock] = await Promise.all([warmupPromise, kbPromise]);
71
78
  const warmupTimedOut = cfg.warmupRecall && result === null;
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CtxdbError
4
- } from "./chunk-JFTKYEVN.js";
4
+ } from "./chunk-ARZX2RLR.js";
5
5
 
6
6
  // src/lib/kb.ts
7
7
  import {
@@ -243,12 +243,13 @@ async function uploadFileInChunks(client, options) {
243
243
  "document_id",
244
244
  "chunk complete"
245
245
  );
246
- if (completedDocumentId !== documentId) {
246
+ const reusedSucceededDocument = completedDocumentId !== documentId && completed.status === "succeeded";
247
+ if (completedDocumentId !== documentId && !reusedSucceededDocument) {
247
248
  throw new CtxdbError(
248
249
  `chunk complete returned mismatched document_id ${completedDocumentId}`
249
250
  );
250
251
  }
251
- return getDocument(client, options.kbName, documentId);
252
+ return getDocument(client, options.kbName, completedDocumentId);
252
253
  } catch (error) {
253
254
  if (!completeStarted) {
254
255
  try {