@aliyunrds/ctxdb 1.0.9-beta.1 → 1.0.10-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 +93 -2
- package/dist/{chunk-U4DRH4NZ.js → chunk-5HPYYOPH.js} +2 -3
- package/dist/chunk-LASAMQQY.js +259 -0
- package/dist/{chunk-3MEBQDGU.js → chunk-QUPLZ7KF.js} +8 -8
- package/dist/{chunk-FD3IPF6R.js → chunk-SG4LQTOF.js} +4 -4
- package/dist/{chunk-T5ZJEPR4.js → chunk-SZ3AZDMU.js} +5 -5
- package/dist/chunk-TZFDXKLJ.js +3278 -0
- package/dist/{chunk-V7IZ2UYJ.js → chunk-VMBCSFGF.js} +4 -4
- package/dist/{chunk-7TPYIT63.js → chunk-XWJAZAMN.js} +3 -3
- package/dist/cli/main.js +1189 -212
- package/dist/hooks/hermes-post-llm-call.js +25 -6
- package/dist/hooks/hermes-pre-llm-call.js +46 -13
- package/dist/hooks/session-end.js +35 -0
- package/dist/hooks/session-start.js +20 -10
- package/dist/hooks/stop.js +15 -5
- package/dist/hooks/user-prompt-submit.js +20 -9
- package/dist/opencode/index.js +22792 -1663
- package/dist/setup/skills/contextdb-knowledge/SKILL.md +9 -0
- package/dist/setup/skills/contextdb-skills/SKILL.md +100 -0
- package/dist/workers/version-check.js +3 -3
- package/package.json +11 -2
- package/dist/chunk-2ROMQSPA.js +0 -569
- package/dist/chunk-2U7G6UXE.js +0 -1205
package/README.md
CHANGED
|
@@ -251,7 +251,7 @@ ctxdb setup --agent qwenwork --api-key ctxdb-...
|
|
|
251
251
|
ctxdb setup --agent codex --api-key ctxdb-...
|
|
252
252
|
# Claude Code (hooks + skill)
|
|
253
253
|
ctxdb setup --agent claude --api-key ctxdb-...
|
|
254
|
-
# WorkBuddy (
|
|
254
|
+
# WorkBuddy (JSON hooks, full shell command + skill)
|
|
255
255
|
ctxdb setup --agent workbuddy --api-key ctxdb-...
|
|
256
256
|
# OpenCode (plugin shim + skill)
|
|
257
257
|
ctxdb setup --agent opencode --api-key ctxdb-...
|
|
@@ -309,7 +309,7 @@ ctxdb status --agent codex --json
|
|
|
309
309
|
|
|
310
310
|
Setup first checks that the target agent home exists (`~/.qoder`, `~/.qoderwork`, `~/.qwenwork`, `~/.codex`, `~/.claude`, or either WorkBuddy home: `~/.workbuddy` / `~/.workbuddy-ai`; other declared CN variant homes also satisfy this check). Setup writes hooks and skills into every existing home, 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.
|
|
311
311
|
|
|
312
|
-
For **qoder**, **qoderwork**, **qwenwork**, **codex**, **claude**, and **workbuddy**, 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
|
|
312
|
+
For **qoder**, **qoderwork**, **qwenwork**, **codex**, **claude**, and **workbuddy**, 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) — Current Codex versions enable hooks by default; the explicit setting also supports older installs. Review and trust each installed or changed hook definition in `/hooks` before use. 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.
|
|
313
313
|
|
|
314
314
|
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:
|
|
315
315
|
|
|
@@ -364,6 +364,8 @@ For **qoder**, **qoderwork**, **qwenwork**, **codex**, **claude**, and **workbud
|
|
|
364
364
|
|
|
365
365
|
Implementation note: qoder/Claude/WorkBuddy consume the JSON `hookSpecificOutput.additionalContext` shape; Codex has been verified on this machine through `~/.codex/hooks.json`, `[features].hooks = true`, and hook audit logs, where non-empty stdout from `UserPromptSubmit`/`SessionStart` is treated as injected context. The same hook core is shared; setup passes `--agent=<name>` so each hook reads its own config section.
|
|
366
366
|
|
|
367
|
+
WorkBuddy 5.5.2 on macOS requires the full shell command in `command`; its runner ignores `args`. Native Auto (default routing label; underlying model unknown) and explicitly selected GPT-5.6-Sol completed automatic Skill Evaluate, suggestion delivery, generation, Upload/Complete, and exact Load. The final package also passed cold-cache Load for both versions. Sol required one native Retry after a platform error; Auto's extra execution was not proven fully isolated. Windows runner behavior and Memory capture/recall were not tested in this acceptance run. See the [Agent/model acceptance report](../../docs/skill-agent-acceptance-20260912.md) and [WorkBuddy evidence](../../docs/workbuddy-acceptance-20260912.md) for version scope, retained failures, and cleanup.
|
|
368
|
+
|
|
367
369
|
For **opencode**, `ctxdb setup --agent opencode` copies the self-contained plugin bundle to `~/.config/opencode/plugins/ctxdb-bundle.js` and writes a re-export shim at `~/.config/opencode/plugins/ctxdb.ts` referencing it via the relative path `./ctxdb-bundle.js` (Bun on Windows can't resolve absolute-path specifiers from a .ts shim). `ctxdb upgrade --agent opencode` re-copies the bundle + refreshes the shim.
|
|
368
370
|
|
|
369
371
|
For **hermes**, `ctxdb setup --agent hermes` writes two shell hooks under `~/.hermes/config.yaml`:
|
|
@@ -653,6 +655,63 @@ Output semantics worth knowing:
|
|
|
653
655
|
incomplete by design (deleted endpoints are omitted); relation items always
|
|
654
656
|
keep denormalized endpoint names.
|
|
655
657
|
|
|
658
|
+
### Knowledge graph create, update and delete
|
|
659
|
+
|
|
660
|
+
These six commands operate on entities and relations **inside a knowledge base**.
|
|
661
|
+
`kb create/update/delete` manages the knowledge base itself; `memory entity`
|
|
662
|
+
manages memory entities. Graph mutations use the existing DATA API credentials
|
|
663
|
+
selected by `--agent` / `CTXDB_AGENT`, and require graph write permission.
|
|
664
|
+
|
|
665
|
+
| Command | DATA API |
|
|
666
|
+
|---|---|
|
|
667
|
+
| `kb entity-create` / `kb entity-update` / `kb entity-delete` | POST / PUT / DELETE `/v1/knowledge/entities` |
|
|
668
|
+
| `kb relation-create` / `kb relation-update` / `kb relation-delete` | POST / PUT / DELETE `/v1/knowledge/entity-relations` |
|
|
669
|
+
|
|
670
|
+
Every command requires `--kb=<kb-id>` (an ID, not a name). Updates and deletes
|
|
671
|
+
require `--entity-id` or `--relation-id`; names, positional locators and bulk
|
|
672
|
+
selectors are not accepted. Find IDs with `entities` and `relations --verbose`.
|
|
673
|
+
Creates return the new ID; entity renaming preserves that ID and updates incident
|
|
674
|
+
relation names. Relation updates cannot change the endpoints or relation ID.
|
|
675
|
+
|
|
676
|
+
```sh
|
|
677
|
+
ctxdb kb entity-create --kb=<kb-id> --name=ContextDB --type=Product \
|
|
678
|
+
--description='Context database' --aliases-json='["ctxdb"]' --json
|
|
679
|
+
ctxdb kb entity-get --kb=<kb-id> --entity-id=<id> --verbose --json
|
|
680
|
+
ctxdb kb entity-update --kb=<kb-id> --entity-id=<id> --name=ContextDB --type=Product \
|
|
681
|
+
--description='' --aliases-json='[]' --json
|
|
682
|
+
|
|
683
|
+
ctxdb kb relation-create --kb=<kb-id> --source-entity-id=<a> --target-entity-id=<b> \
|
|
684
|
+
--relation-type=USES --description='' --keywords-json='[]' --weight=1 --json
|
|
685
|
+
ctxdb kb relations --kb=<kb-id> --verbose --json
|
|
686
|
+
ctxdb kb relation-update --kb=<kb-id> --relation-id=<id> \
|
|
687
|
+
--relation-type=CALLS --description='' --keywords-json='["api"]' --weight=0.5 --json
|
|
688
|
+
|
|
689
|
+
# Irreversible: delete a relation; both endpoint entities remain.
|
|
690
|
+
ctxdb kb relation-delete --kb=<kb-id> --relation-id=<id> --yes --json
|
|
691
|
+
# Irreversible: delete an entity and ALL its incoming/outgoing relations.
|
|
692
|
+
ctxdb kb entity-delete --kb=<kb-id> --entity-id=<id> --yes --json
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
Create and update require every editable field shown above. **Update is a full
|
|
696
|
+
replacement**, so read the current values before editing. Explicitly use
|
|
697
|
+
`--description=''` or `--aliases-json='[]'` / `--keywords-json='[]'` to clear a
|
|
698
|
+
field. Arrays must contain strings; `--weight` must be finite, with the server
|
|
699
|
+
enforcing its 0–1 range and other business limits.
|
|
700
|
+
|
|
701
|
+
Deletion requires the bare `--yes` switch in both terminals and scripts; without
|
|
702
|
+
it, or with `--yes=false`, the CLI exits 2 without sending a delete request.
|
|
703
|
+
The CLI does not prompt interactively. Entity deletion returns the server's
|
|
704
|
+
`deleted`, `deleted_entity_count` and `deleted_relation_count`. Relation deletion
|
|
705
|
+
returns `deleted` and its IDs; its API does not return counts. A `deleted: false`
|
|
706
|
+
result is printed and exits 1, never reported as successful deletion.
|
|
707
|
+
|
|
708
|
+
Default output preserves the complete mutation `data`; `--json` formats that as
|
|
709
|
+
JSON and `--raw` retains the Box envelope. HTTP and business errors (including
|
|
710
|
+
HTTP 200 with a failing `errorCode`) exit nonzero even with `--raw`. Missing IDs,
|
|
711
|
+
malformed lists and incomplete arguments exit 2 with usage. A second delete of
|
|
712
|
+
an already deleted ID returns the server's not-found error; mutations are not
|
|
713
|
+
automatically retried after an uncertain response.
|
|
714
|
+
|
|
656
715
|
### Knowledge review commands
|
|
657
716
|
|
|
658
717
|
`kb review` covers the Knowledge Review DATA APIs: review-queue handling
|
|
@@ -776,6 +835,7 @@ Lives at `~/.ctxdb/ctxdb.json` (co-located with logs at `~/.ctxdb/logs/`). Schem
|
|
|
776
835
|
"base_url": "https://api.cn-hangzhou.agentcontext.aliyuncs.com",
|
|
777
836
|
"user_id": "default",
|
|
778
837
|
"auto_capture": true,
|
|
838
|
+
"auto_skill_evolution": true,
|
|
779
839
|
"auto_recall": true,
|
|
780
840
|
"warmup_recall": false,
|
|
781
841
|
"recall_knowledge": false,
|
|
@@ -804,6 +864,7 @@ Field reference:
|
|
|
804
864
|
| `agent_id` | str | null | Optional scope dimension on every capture/recall call. **Default null = not sent** — the server counts `agent_id` as scope on the search path, so emitting it by default would narrow every existing user's recall. Set only when you want per-agent isolation: `CTXDB_AGENT_ID=<id>` (env-only; no setup flag) |
|
|
805
865
|
| `app_id` | str | null | Optional app-scope dimension, same contract as `agent_id` (default null = not sent). Set via `CTXDB_APP_ID=<id>` (env-only) |
|
|
806
866
|
| `auto_capture` | bool | `true` | End-of-turn hook/plugin captures each turn into long-term memory for the selected agent |
|
|
867
|
+
| `auto_skill_evolution` | bool | `true` | Evidence collection and automatic evolution through the selected Agent hooks/plugin. Hermes delivers ready suggestions on the next genuine user turn. Disabling it keeps private Skill discovery and explicit commands available. |
|
|
807
868
|
| `auto_recall` | bool | `true` | UserPromptSubmit hook recalls memory on each user prompt for the selected agent |
|
|
808
869
|
| `warmup_recall` | bool | `false` | SessionStart hook recalls cwd/git-related memories for the selected agent. **Default is off** — open it per-agent when you want session-start warmup (adds one bounded, circuit-protected recall call on session start) |
|
|
809
870
|
| `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 |
|
|
@@ -909,6 +970,36 @@ Maintainer-facing docs (source layout, dev/test workflows, server-config archive
|
|
|
909
970
|
|
|
910
971
|
Apache-2.0
|
|
911
972
|
|
|
973
|
+
|
|
974
|
+
## Private Skills and evolution
|
|
975
|
+
|
|
976
|
+
`ctxdb skill` provides `manifest`, `sync`, `search`, `load`, `validate`, `upload`, `evaluate`, `disable`, `rollback`, `delete`, `config`, `status` and `purge`. Generic entry templates are installed by `skill install`; private packages remain under the authenticated scope in `~/.ctxdb/skills/`.
|
|
977
|
+
|
|
978
|
+
Codex startup and Resume refresh a stable private Skill directory. Compact restores it from disk; `sync` returns refreshed context as well as updating disk. The Agent selects relevant Skills and calls Load when needed. Search runs on the server. Skill lifecycle adapters also cover Claude/Qoder/WorkBuddy, Hermes and OpenCode; see the Agent acceptance report for the tested versions and host limitations.
|
|
979
|
+
|
|
980
|
+
Normal business Stops accumulate projected evidence across turns. At 10 distinct tool calls or 40,960 buffered UTF-8 bytes, a complete checkpoint submits an asynchronous Evaluate. Raw tool bodies and hidden thinking are excluded; no local LLM summarizes the trace. SessionEnd does not force a submission. Resume and genuine user prompts query results. Suggestion delivery follows each host adapter: Codex and Hermes defer it to a genuine user turn. The current Agent reviews evidence, edits a separate draft, validates it and publishes within existing authorization. Maintenance Stops skip collection, queries and delivery. There is no background Author or completion receipt.
|
|
981
|
+
|
|
982
|
+
```sh
|
|
983
|
+
ctxdb skill bind --agent codex --session-id <host-session-id> --json
|
|
984
|
+
ctxdb skill sync --session <binding-id> --json
|
|
985
|
+
ctxdb skill search "SQL diagnostics" --session <binding-id> --json
|
|
986
|
+
ctxdb skill load --skill-id <id> --editable --session <binding-id> --json
|
|
987
|
+
ctxdb skill validate <draft-directory> --json
|
|
988
|
+
ctxdb skill upload <draft-directory> --source evolution --skill-id <id> --base-version-id <base> --session <binding-id> --json
|
|
989
|
+
ctxdb skill status --resume --session <binding-id> --json
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
Bindings pin endpoint and credential reference; the server verifies Workspace/Member. Credential rotation must resolve to the same identity. Binding files contain no API Key. Conversations use `--session <binding_id>`; terminal commands can select an existing `--agent` connection.
|
|
993
|
+
|
|
994
|
+
Packages are limited to 50 MiB compressed, 50 MiB expanded, 200 files and 256 KiB for root SKILL.md. Load reauthorizes cache access and checks ZIP plus extracted files. Upload has its own frozen ZIP and request ID, independent of Evaluate. Before submitting changed draft contents, resolve any previous unknown upload outcome. Exact Version/Hash readback confirms publication; a failed Manifest refresh is retried separately without republishing.
|
|
995
|
+
|
|
996
|
+
For private Skills, `disable --skill-id <id>` retains history but blocks discovery, Load and new publication; this release has no re-enable command. `rollback --skill-id <id> --version-id <target> --base-version-id <current>` requires an available historical version and the expected Active version; `--revoke-reason <reason>` also revokes the previous Active. `delete --skill-id <id>` revokes and cleans up the Skill, including disabled Skills.
|
|
997
|
+
|
|
998
|
+
New automatic collection and Evaluate submissions require both local `auto_skill_evolution` and the effective server switch. Server switches cover global, cloud-account and Workspace scopes with a 30-second cache. Disabling a server switch stops new automatic Evaluate admissions; accepted tasks keep polling and may deliver results and publish through Upload/Complete. Local `auto_skill_evolution=false` still pauses automatic delivery and evolution uploads on that client. Manual evaluation and explicit imports retain normal authorization and package checks. `status --resume` retries frozen submissions/uploads and queries results; it does not generate files. `delivery_uncertain` requires inspecting the conversation, without automatic redelivery.
|
|
999
|
+
|
|
1000
|
+
The 256 MiB cache never evicts automatically. `skill purge --cache` previews cache cleanup; stop related sessions/transfers before adding `--apply`. Default purge removes only verified upload payloads, keeping small idempotency receipts. Drafts and unresolved uploads are retained. Evidence/artifacts have a separate 100 MiB budget: pending tails expire after 7 days, successful/consumed payloads after 7 days, failed/oversized evidence after 30 days. Resume and status check expiry while preserving cursors and unresolved recovery bytes.
|
|
1001
|
+
|
|
1002
|
+
Codex 0.153.4 has completed the automatic lifecycle against a local test center/object store, including draft generation, upload and exact Version/Hash readback. The deployed ContextDB → XA → OSS path and real central model still require separate acceptance. Claude Code, Qoder, Qwen Code and Hermes automatic adapters are deferred. Codex tools must have authorized network access to the configured ContextDB and object-store endpoints; Hook connectivity alone is insufficient.
|
|
912
1003
|
上传时可用 `ctxdb kb upload-text` 或 `ctxdb kb upload-file` 的 `--review-enabled` / `--no-review-enabled` 覆盖本次文档评审开关;不传时沿用服务端默认,不修改知识库策略。两标志互斥,分片上传保留同一选择。上传回执不代表评审或入库完成,应查询实际状态。
|
|
913
1004
|
|
|
914
1005
|
`memory get <fact-id> --verbose` 通过原生 Fact 详情接口读取状态、关联实体 ID、来源及参考/记录时间;默认 `memory get` 的 mem0 输出保持兼容。召回统计可从同一身份的 `memory list` 中按 ID 对应读取。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
fetchKbCatalogBlock,
|
|
4
4
|
recallTurn
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-VMBCSFGF.js";
|
|
6
6
|
|
|
7
7
|
// src/lib/user-prompt-submit-compose.ts
|
|
8
8
|
async function composeUserPromptSubmit(cfg, agent, client, prompt, sessionId = null) {
|
|
@@ -20,8 +20,7 @@ async function composeUserPromptSubmit(cfg, agent, client, prompt, sessionId = n
|
|
|
20
20
|
${kbBlock}` : kbBlock;
|
|
21
21
|
return { ctx, recall, kbBlock };
|
|
22
22
|
}
|
|
23
|
-
function formatUserPromptSubmitStdout(
|
|
24
|
-
if (agent === "codex") return ctx + "\n";
|
|
23
|
+
function formatUserPromptSubmitStdout(_agent, ctx) {
|
|
25
24
|
const out = {
|
|
26
25
|
hookSpecificOutput: {
|
|
27
26
|
hookEventName: "UserPromptSubmit",
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
isCircuitOpen,
|
|
4
|
+
isConnectionError,
|
|
5
|
+
resetCircuit,
|
|
6
|
+
tripCircuit
|
|
7
|
+
} from "./chunk-XWJAZAMN.js";
|
|
8
|
+
import {
|
|
9
|
+
CtxdbError,
|
|
10
|
+
debug,
|
|
11
|
+
extractSkillSignals,
|
|
12
|
+
isDataApiReady,
|
|
13
|
+
isDebug,
|
|
14
|
+
readTranscript,
|
|
15
|
+
selectTurnRows,
|
|
16
|
+
toKbDetectionMessages,
|
|
17
|
+
toParsedMessages
|
|
18
|
+
} from "./chunk-TZFDXKLJ.js";
|
|
19
|
+
|
|
20
|
+
// src/lib/capture-orchestrator.ts
|
|
21
|
+
import {
|
|
22
|
+
filterMessagesForExtraction,
|
|
23
|
+
isKnowledgeBaseUploadTurn,
|
|
24
|
+
selectTurnMessages
|
|
25
|
+
} from "@aliyunrds/ctxdb-shared";
|
|
26
|
+
|
|
27
|
+
// src/lib/git-context.ts
|
|
28
|
+
import { execSync } from "child_process";
|
|
29
|
+
function getGitContext() {
|
|
30
|
+
try {
|
|
31
|
+
const raw = execSync("git rev-parse --show-toplevel --abbrev-ref HEAD", {
|
|
32
|
+
encoding: "utf-8",
|
|
33
|
+
timeout: 3e3,
|
|
34
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
35
|
+
}).trim();
|
|
36
|
+
const lines = raw.split("\n");
|
|
37
|
+
if (lines.length < 2) return null;
|
|
38
|
+
const project = lines[0].split(/[/\\]/).pop();
|
|
39
|
+
if (!project) return null;
|
|
40
|
+
const branch = lines[1];
|
|
41
|
+
return { project, branch };
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/lib/capture-orchestrator.ts
|
|
48
|
+
async function captureTurn(transcriptPath, cfg, client, agent = "default", sessionId) {
|
|
49
|
+
const preflight = capturePreflight(cfg, agent);
|
|
50
|
+
if (preflight) return preflight;
|
|
51
|
+
const rows = readTranscript(transcriptPath);
|
|
52
|
+
if (rows.length === 0) {
|
|
53
|
+
return { captured: false, reason: "empty_transcript", messageCount: 0 };
|
|
54
|
+
}
|
|
55
|
+
const dbg = isDebug();
|
|
56
|
+
if (dbg) debug("capture", "transcript rows", summarizeTranscriptRows(rows));
|
|
57
|
+
const parsed = toParsedMessages(rows);
|
|
58
|
+
if (parsed.length === 0) {
|
|
59
|
+
return {
|
|
60
|
+
captured: false,
|
|
61
|
+
reason: `transcript_schema_mismatch: rows=${rows.length} parsed=0`,
|
|
62
|
+
messageCount: 0
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const turnRows = selectTurnRows(rows);
|
|
66
|
+
if (dbg) debug("capture", "current turn rows", summarizeTranscriptRows(turnRows));
|
|
67
|
+
return captureParsedMessages(parsed, cfg, client, agent, sessionId, {
|
|
68
|
+
kbDetectionMessages: toKbDetectionMessages(turnRows),
|
|
69
|
+
skills: extractSkillSignals(rows)
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async function captureParsedMessages(parsed, cfg, client, agent = "default", sessionId, options = {}) {
|
|
73
|
+
const preflight = capturePreflight(cfg, agent);
|
|
74
|
+
if (preflight) return preflight;
|
|
75
|
+
if (parsed.length === 0) {
|
|
76
|
+
return { captured: false, reason: "empty_messages", messageCount: 0 };
|
|
77
|
+
}
|
|
78
|
+
const dbg = isDebug();
|
|
79
|
+
if (dbg) debug("capture", "parsed messages", summarizeTextMessages(parsed));
|
|
80
|
+
const turn = selectTurnMessages(parsed);
|
|
81
|
+
if (dbg) debug("capture", "selected turn", summarizeTextMessages(turn));
|
|
82
|
+
if (turn.length === 0) {
|
|
83
|
+
return { captured: false, reason: "empty_turn_slice", messageCount: 0 };
|
|
84
|
+
}
|
|
85
|
+
if (!turn.some((m) => m.role === "user")) {
|
|
86
|
+
return { captured: false, reason: "no_user_in_turn", messageCount: 0 };
|
|
87
|
+
}
|
|
88
|
+
const AUTOMATED_REVIEW_PREFIX = "[SYSTEM: This is an automated background review task. It is NOT a user message.";
|
|
89
|
+
const userMsgs = turn.filter((m) => m.role === "user");
|
|
90
|
+
if (userMsgs.every((m) => m.content.startsWith(AUTOMATED_REVIEW_PREFIX))) {
|
|
91
|
+
return { captured: false, reason: "automated_review_skip", messageCount: 0 };
|
|
92
|
+
}
|
|
93
|
+
const kb = isKnowledgeBaseUploadTurn(options.kbDetectionMessages);
|
|
94
|
+
if (kb.detected) {
|
|
95
|
+
return {
|
|
96
|
+
captured: false,
|
|
97
|
+
reason: `b3c_skip: ${kb.reason ?? "(no reason)"}`,
|
|
98
|
+
messageCount: 0
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const rawMessages = turn.map((m) => ({ role: m.role, content: m.content }));
|
|
102
|
+
const filtered = filterMessagesForExtraction(rawMessages);
|
|
103
|
+
if (dbg) debug("capture", "filtered messages", summarizeTextMessages(filtered));
|
|
104
|
+
if (filtered.length === 0) {
|
|
105
|
+
return { captured: false, reason: "all_filtered", messageCount: 0 };
|
|
106
|
+
}
|
|
107
|
+
const git = getGitContext();
|
|
108
|
+
const skills = options.skills ?? [];
|
|
109
|
+
const bg = {};
|
|
110
|
+
if (git) bg.git = git;
|
|
111
|
+
if (skills.length > 0) bg.skills = skills.map(
|
|
112
|
+
({ skill, trigger, args }) => args ? { skill, trigger, args } : { skill, trigger }
|
|
113
|
+
);
|
|
114
|
+
const hasBackground = Boolean(bg.git || bg.skills);
|
|
115
|
+
if (hasBackground) debug("capture", "background", bg);
|
|
116
|
+
const messages = hasBackground ? [{ role: "background", content: JSON.stringify(bg) }, ...filtered] : filtered;
|
|
117
|
+
const payload = {
|
|
118
|
+
messages,
|
|
119
|
+
user_id: cfg.userId,
|
|
120
|
+
async_mode: true
|
|
121
|
+
};
|
|
122
|
+
if (cfg.agentId) payload.agent_id = cfg.agentId;
|
|
123
|
+
if (cfg.appId) payload.app_id = cfg.appId;
|
|
124
|
+
if (sessionId) payload.run_id = sessionId;
|
|
125
|
+
let resp;
|
|
126
|
+
try {
|
|
127
|
+
resp = await client.postJson("/v3/memories/add/", payload);
|
|
128
|
+
resetCircuit(agent);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
const msg = err instanceof CtxdbError ? err.message : String(err);
|
|
131
|
+
if (isConnectionError(err)) tripCircuit(agent, cfg.baseUrl, msg);
|
|
132
|
+
return {
|
|
133
|
+
captured: false,
|
|
134
|
+
reason: `http_error: ${msg}`,
|
|
135
|
+
messageCount: filtered.length
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
captured: true,
|
|
140
|
+
reason: "ok",
|
|
141
|
+
serverResponse: resp,
|
|
142
|
+
messageCount: filtered.length
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function capturePreflight(cfg, agent) {
|
|
146
|
+
if (!isDataApiReady(cfg)) {
|
|
147
|
+
return { captured: false, reason: "config_incomplete", messageCount: 0 };
|
|
148
|
+
}
|
|
149
|
+
if (!cfg.autoCapture) {
|
|
150
|
+
return { captured: false, reason: "auto_capture_disabled", messageCount: 0 };
|
|
151
|
+
}
|
|
152
|
+
if (isCircuitOpen(agent, cfg.baseUrl)) {
|
|
153
|
+
return { captured: false, reason: `circuit_open: ${cfg.baseUrl}`, messageCount: 0 };
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
var DEBUG_TAIL_LIMIT = 25;
|
|
158
|
+
var DEBUG_TEXT_LIMIT = 240;
|
|
159
|
+
var DEBUG_BLOCK_LIMIT = 8;
|
|
160
|
+
function summarizeTextMessages(messages) {
|
|
161
|
+
return {
|
|
162
|
+
count: messages.length,
|
|
163
|
+
roles: countRoles(messages),
|
|
164
|
+
tail: tailWithIndex(messages).map(({ index, item }) => ({
|
|
165
|
+
index,
|
|
166
|
+
parsedIndex: typeof item.index === "number" ? item.index : void 0,
|
|
167
|
+
role: item.role ?? "(unknown)",
|
|
168
|
+
content: summarizeText(item.content)
|
|
169
|
+
}))
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function summarizeTranscriptRows(rows) {
|
|
173
|
+
return {
|
|
174
|
+
count: rows.length,
|
|
175
|
+
tail: tailWithIndex(rows).map(({ index, item: row }) => ({
|
|
176
|
+
index,
|
|
177
|
+
type: row.type,
|
|
178
|
+
role: rowRole(row),
|
|
179
|
+
isMeta: row.isMeta === true || void 0,
|
|
180
|
+
payloadType: row.payload?.type,
|
|
181
|
+
content: summarizeRowContent(row)
|
|
182
|
+
}))
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function tailWithIndex(items) {
|
|
186
|
+
const start = Math.max(0, items.length - DEBUG_TAIL_LIMIT);
|
|
187
|
+
return items.slice(start).map((item, offset) => ({ index: start + offset, item }));
|
|
188
|
+
}
|
|
189
|
+
function countRoles(messages) {
|
|
190
|
+
const counts = {};
|
|
191
|
+
for (const msg of messages) {
|
|
192
|
+
const role = msg.role ?? "(unknown)";
|
|
193
|
+
counts[role] = (counts[role] ?? 0) + 1;
|
|
194
|
+
}
|
|
195
|
+
return counts;
|
|
196
|
+
}
|
|
197
|
+
function rowRole(row) {
|
|
198
|
+
const msgRole = row.message && typeof row.message === "object" ? row.message.role : void 0;
|
|
199
|
+
if (typeof msgRole === "string") return msgRole;
|
|
200
|
+
const payloadRole = row.payload && typeof row.payload === "object" ? row.payload.role : void 0;
|
|
201
|
+
return typeof payloadRole === "string" ? payloadRole : void 0;
|
|
202
|
+
}
|
|
203
|
+
function summarizeRowContent(row) {
|
|
204
|
+
if (row.type === "response_item") {
|
|
205
|
+
const p = row.payload;
|
|
206
|
+
if (!p || typeof p !== "object") return { kind: "missing" };
|
|
207
|
+
if (p.type === "function_call") {
|
|
208
|
+
return {
|
|
209
|
+
kind: "function_call",
|
|
210
|
+
name: p.name,
|
|
211
|
+
arguments: summarizeText(p.arguments)
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return summarizeContent(p.content);
|
|
215
|
+
}
|
|
216
|
+
return summarizeContent(row.message?.content);
|
|
217
|
+
}
|
|
218
|
+
function summarizeContent(content) {
|
|
219
|
+
if (typeof content === "string") return summarizeText(content);
|
|
220
|
+
if (!Array.isArray(content)) {
|
|
221
|
+
return { kind: content === void 0 ? "missing" : typeof content };
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
kind: "blocks",
|
|
225
|
+
count: content.length,
|
|
226
|
+
blocks: content.slice(0, DEBUG_BLOCK_LIMIT).map((block) => summarizeBlock(block)),
|
|
227
|
+
truncated: content.length > DEBUG_BLOCK_LIMIT || void 0
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function summarizeBlock(block) {
|
|
231
|
+
if (!block || typeof block !== "object") return { kind: typeof block };
|
|
232
|
+
const b = block;
|
|
233
|
+
const summary = { type: b.type };
|
|
234
|
+
if (typeof b.name === "string") summary.name = b.name;
|
|
235
|
+
if (typeof b.text === "string") summary.text = summarizeText(b.text);
|
|
236
|
+
if (typeof b.thinking === "string") summary.thinking = summarizeText(b.thinking);
|
|
237
|
+
if (typeof b.content === "string") summary.content = summarizeText(b.content);
|
|
238
|
+
const input = b.input;
|
|
239
|
+
if (input && typeof input === "object" && !Array.isArray(input)) {
|
|
240
|
+
const i = input;
|
|
241
|
+
summary.inputKeys = Object.keys(i).sort();
|
|
242
|
+
if (typeof i.command === "string") summary.command = summarizeText(i.command);
|
|
243
|
+
}
|
|
244
|
+
return summary;
|
|
245
|
+
}
|
|
246
|
+
function summarizeText(text) {
|
|
247
|
+
if (typeof text !== "string") return { kind: text === void 0 ? "missing" : typeof text };
|
|
248
|
+
const normalized = text.replace(/\s+/g, " ").trim();
|
|
249
|
+
return {
|
|
250
|
+
kind: "text",
|
|
251
|
+
length: text.length,
|
|
252
|
+
snippet: normalized.length > DEBUG_TEXT_LIMIT ? `${normalized.slice(0, DEBUG_TEXT_LIMIT)}...` : normalized
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export {
|
|
257
|
+
captureTurn,
|
|
258
|
+
captureParsedMessages
|
|
259
|
+
};
|
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
} from "./chunk-5ITT5GSP.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/self-update.ts
|
|
7
|
-
import { spawnSync } from "child_process";
|
|
8
|
-
import { fileURLToPath } from "url";
|
|
9
|
-
import { dirname } from "path";
|
|
7
|
+
import { spawnSync } from "node:child_process";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { dirname } from "node:path";
|
|
10
10
|
import semver from "semver";
|
|
11
11
|
var NPM_VIEW_TIMEOUT_MS = 1e4;
|
|
12
12
|
function npmCommand(platform = process.platform) {
|
|
@@ -149,8 +149,8 @@ function runSelfUpdate(currentVersion, passthroughArgs = [], options = {}) {
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
// src/lib/update-notifier.ts
|
|
152
|
-
import { randomUUID } from "crypto";
|
|
153
|
-
import { spawn } from "child_process";
|
|
152
|
+
import { randomUUID } from "node:crypto";
|
|
153
|
+
import { spawn } from "node:child_process";
|
|
154
154
|
import {
|
|
155
155
|
mkdirSync,
|
|
156
156
|
readFileSync,
|
|
@@ -159,9 +159,9 @@ import {
|
|
|
159
159
|
statSync,
|
|
160
160
|
unlinkSync,
|
|
161
161
|
writeFileSync
|
|
162
|
-
} from "fs";
|
|
163
|
-
import { homedir } from "os";
|
|
164
|
-
import { dirname as dirname2, join } from "path";
|
|
162
|
+
} from "node:fs";
|
|
163
|
+
import { homedir } from "node:os";
|
|
164
|
+
import { dirname as dirname2, join } from "node:path";
|
|
165
165
|
import semver2 from "semver";
|
|
166
166
|
var VERSION_CHECK_SCHEMA = 1;
|
|
167
167
|
var SUCCESS_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
fetchKbCatalogBlock,
|
|
4
4
|
recallTurn
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-VMBCSFGF.js";
|
|
6
6
|
import {
|
|
7
7
|
debug
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-TZFDXKLJ.js";
|
|
9
9
|
|
|
10
10
|
// src/lib/warmup-recall.ts
|
|
11
|
-
import { execSync } from "child_process";
|
|
12
|
-
import { basename } from "path";
|
|
11
|
+
import { execSync } from "node:child_process";
|
|
12
|
+
import { basename } from "node:path";
|
|
13
13
|
function collectGitSignals(cwd) {
|
|
14
14
|
const result = { branch: "", recentCommits: [] };
|
|
15
15
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
CtxdbError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TZFDXKLJ.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/kb.ts
|
|
7
7
|
import {
|
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
existsSync,
|
|
11
11
|
openAsBlob,
|
|
12
12
|
statSync
|
|
13
|
-
} from "fs";
|
|
14
|
-
import { basename, extname } from "path";
|
|
15
|
-
import { homedir } from "os";
|
|
16
|
-
import { resolve } from "path";
|
|
13
|
+
} from "node:fs";
|
|
14
|
+
import { basename, extname } from "node:path";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
import { resolve } from "node:path";
|
|
17
17
|
import {
|
|
18
18
|
buildNormalizedGraphContext,
|
|
19
19
|
extractKnowledgeBaseIds,
|