@team-harness/memory-algorithms 0.1.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.
Files changed (69) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +203 -0
  3. package/dist/contracts.d.ts +178 -0
  4. package/dist/contracts.js +1 -0
  5. package/dist/index.d.ts +14 -0
  6. package/dist/index.js +6 -0
  7. package/dist/runtime/documents.d.ts +15 -0
  8. package/dist/runtime/documents.js +209 -0
  9. package/dist/runtime/l1.d.ts +10 -0
  10. package/dist/runtime/l1.js +172 -0
  11. package/dist/runtime/run.d.ts +44 -0
  12. package/dist/runtime/run.js +191 -0
  13. package/dist/runtime/skill-workspace.d.ts +70 -0
  14. package/dist/runtime/skill-workspace.js +156 -0
  15. package/dist/runtime/skills.d.ts +9 -0
  16. package/dist/runtime/skills.js +48 -0
  17. package/dist/runtime/telemetry.d.ts +10 -0
  18. package/dist/runtime/telemetry.js +5 -0
  19. package/dist/runtime/tools.d.ts +15 -0
  20. package/dist/runtime/tools.js +5 -0
  21. package/dist/upstream/config.d.ts +1 -0
  22. package/dist/upstream/config.js +1 -0
  23. package/dist/upstream/core/conversation/l0-recorder.d.ts +6 -0
  24. package/dist/upstream/core/conversation/l0-recorder.js +1 -0
  25. package/dist/upstream/core/memory-prompt/composer.d.ts +6 -0
  26. package/dist/upstream/core/memory-prompt/composer.js +33 -0
  27. package/dist/upstream/core/memory-prompt/types.d.ts +103 -0
  28. package/dist/upstream/core/memory-prompt/types.js +21 -0
  29. package/dist/upstream/core/prompts/l1-dedup.d.ts +33 -0
  30. package/dist/upstream/core/prompts/l1-dedup.js +202 -0
  31. package/dist/upstream/core/prompts/l1-extraction.d.ts +24 -0
  32. package/dist/upstream/core/prompts/l1-extraction.js +400 -0
  33. package/dist/upstream/core/prompts/persona-generation.d.ts +29 -0
  34. package/dist/upstream/core/prompts/persona-generation.js +284 -0
  35. package/dist/upstream/core/prompts/scene-extraction.d.ts +40 -0
  36. package/dist/upstream/core/prompts/scene-extraction.js +534 -0
  37. package/dist/upstream/core/record/l1-dedup.d.ts +10 -0
  38. package/dist/upstream/core/record/l1-dedup.js +108 -0
  39. package/dist/upstream/core/record/l1-extractor.d.ts +33 -0
  40. package/dist/upstream/core/record/l1-extractor.js +128 -0
  41. package/dist/upstream/core/record/l1-writer.d.ts +95 -0
  42. package/dist/upstream/core/record/l1-writer.js +1 -0
  43. package/dist/upstream/core/scene/filename-normalizer.d.ts +6 -0
  44. package/dist/upstream/core/scene/filename-normalizer.js +30 -0
  45. package/dist/upstream/core/scene/scene-format.d.ts +26 -0
  46. package/dist/upstream/core/scene/scene-format.js +53 -0
  47. package/dist/upstream/core/scene/scene-index.d.ts +7 -0
  48. package/dist/upstream/core/scene/scene-index.js +1 -0
  49. package/dist/upstream/core/scene/scene-navigation.d.ts +66 -0
  50. package/dist/upstream/core/scene/scene-navigation.js +107 -0
  51. package/dist/upstream/core/skill/conversation-add/message-compressor.d.ts +47 -0
  52. package/dist/upstream/core/skill/conversation-add/message-compressor.js +58 -0
  53. package/dist/upstream/core/skill/conversation-add/oversize-strategy.d.ts +41 -0
  54. package/dist/upstream/core/skill/conversation-add/oversize-strategy.js +100 -0
  55. package/dist/upstream/core/skill/prompts/skill-review-prompt.d.ts +39 -0
  56. package/dist/upstream/core/skill/prompts/skill-review-prompt.js +197 -0
  57. package/dist/upstream/core/skill/skill-extractor.d.ts +146 -0
  58. package/dist/upstream/core/skill/skill-extractor.js +432 -0
  59. package/dist/upstream/core/skill/skill-format.d.ts +46 -0
  60. package/dist/upstream/core/skill/skill-format.js +191 -0
  61. package/dist/upstream/core/skill/skill-tools.d.ts +75 -0
  62. package/dist/upstream/core/skill/skill-tools.js +193 -0
  63. package/dist/upstream/core/skill/types.d.ts +324 -0
  64. package/dist/upstream/core/skill/types.js +7 -0
  65. package/dist/upstream/utils/sanitize.d.ts +96 -0
  66. package/dist/upstream/utils/sanitize.js +359 -0
  67. package/package.json +28 -0
  68. package/upstream/baseline.json +426 -0
  69. package/upstream/changes.md +81 -0
@@ -0,0 +1,75 @@
1
+ /**
2
+ * SkillToolsV2 — 给 Review Agent 的工具集(绑定到 SkillCore)
3
+ *
4
+ * 暴露 4 个写动作 + 2 个读动作,覆盖 SkillExtractor 的需要:
5
+ * - skill_list 列出团队内可见 skill
6
+ * - skill_view 查看单个 skill 详情
7
+ * - skill_create 新建 skill
8
+ * - skill_update 全量替换 SKILL.md
9
+ * - skill_patch 单点串替
10
+ * - skill_files_write 增/改资源
11
+ *
12
+ * 不暴露 delete / files_remove —— 抽取流程不应能销毁团队 skill。
13
+ * 工具错误以 JSON.stringify({error}) 返回,让 LLM 能 self-correct。
14
+ *
15
+ * 每次成功的写操作都 push 一条 ExtractedSkillCandidate 到 auditSink,
16
+ * SkillExtractor 把它作为 candidates 返回给调用方。
17
+ */
18
+ import { type SkillCore } from "../../../runtime/skill-workspace.js";
19
+ export type ExtractedAction = "create" | "update" | "patch" | "files_write";
20
+ export interface ExtractedSkillCandidate {
21
+ action: ExtractedAction;
22
+ name: string;
23
+ skill_id?: string;
24
+ version?: number;
25
+ description?: string;
26
+ }
27
+ export interface CreateSkillToolsOptions {
28
+ core: SkillCore;
29
+ /** 调用方身份(owner 校验依据)。 */
30
+ user_id: string;
31
+ team_id: string;
32
+ agent_id: string;
33
+ task_id?: string;
34
+ auditSink: ExtractedSkillCandidate[];
35
+ logger?: {
36
+ info(msg: string): void;
37
+ warn(msg: string): void;
38
+ error(msg: string): void;
39
+ };
40
+ }
41
+ export declare function createSkillTools(opts: CreateSkillToolsOptions): {
42
+ skill_list: import("../../../runtime/tools.js").LocalTool<{
43
+ query?: string;
44
+ top_k?: number;
45
+ }>;
46
+ skill_view: import("../../../runtime/tools.js").LocalTool<{
47
+ skill_id: string;
48
+ version?: number;
49
+ }>;
50
+ skill_create: import("../../../runtime/tools.js").LocalTool<{
51
+ name: string;
52
+ content: string;
53
+ }>;
54
+ skill_update: import("../../../runtime/tools.js").LocalTool<{
55
+ skill_id: string;
56
+ content: string;
57
+ expected_version: number;
58
+ }>;
59
+ skill_patch: import("../../../runtime/tools.js").LocalTool<{
60
+ skill_id: string;
61
+ old_string: string;
62
+ new_string: string;
63
+ replace_all?: boolean;
64
+ expected_version: number;
65
+ }>;
66
+ skill_files_write: import("../../../runtime/tools.js").LocalTool<{
67
+ skill_id: string;
68
+ path: string;
69
+ content: string;
70
+ encoding?: "utf-8" | "base64";
71
+ mime_type?: string;
72
+ is_executable?: boolean;
73
+ expected_version: number;
74
+ }>;
75
+ };
@@ -0,0 +1,193 @@
1
+ /**
2
+ * SkillToolsV2 — 给 Review Agent 的工具集(绑定到 SkillCore)
3
+ *
4
+ * 暴露 4 个写动作 + 2 个读动作,覆盖 SkillExtractor 的需要:
5
+ * - skill_list 列出团队内可见 skill
6
+ * - skill_view 查看单个 skill 详情
7
+ * - skill_create 新建 skill
8
+ * - skill_update 全量替换 SKILL.md
9
+ * - skill_patch 单点串替
10
+ * - skill_files_write 增/改资源
11
+ *
12
+ * 不暴露 delete / files_remove —— 抽取流程不应能销毁团队 skill。
13
+ * 工具错误以 JSON.stringify({error}) 返回,让 LLM 能 self-correct。
14
+ *
15
+ * 每次成功的写操作都 push 一条 ExtractedSkillCandidate 到 auditSink,
16
+ * SkillExtractor 把它作为 candidates 返回给调用方。
17
+ */
18
+ import { tool, jsonSchema } from "../../../runtime/tools.js";
19
+ import { SkillCoreError } from "../../../runtime/skill-workspace.js";
20
+ function jsonError(e) {
21
+ if (e instanceof SkillCoreError) {
22
+ return JSON.stringify({ error: e.code, message: e.message });
23
+ }
24
+ return JSON.stringify({ error: "INTERNAL", message: e.message });
25
+ }
26
+ export function createSkillTools(opts) {
27
+ const { core, user_id, team_id, agent_id, task_id, auditSink, logger } = opts;
28
+ // Read 路径:不带 task_id — audit 字段不参与检索。skill-core.ts:list/search
29
+ // 内部已经再兜底 undefine 掉 task_id, 但工具层依然显式区分以让意图清晰、
30
+ // 并防止未来 core 侧回退时又把 bug 引回来。
31
+ // Write 路径:带 task_id 落审计列 (记 skill 首次落库时的对话上下文)。
32
+ const readIds = { user_id, team_id, agent_id };
33
+ const writeIds = { user_id, team_id, agent_id, task_id };
34
+ return {
35
+ skill_list: tool({
36
+ description: "List or search your agent's skills. Use this FIRST to see what already exists. "
37
+ + "Pass `query` to rank results by keyword/semantic relevance; omit `query` to browse "
38
+ + "the most-recently-updated skills.",
39
+ inputSchema: jsonSchema({
40
+ type: "object",
41
+ properties: {
42
+ query: {
43
+ type: "string",
44
+ description: "Optional. When set, ranks skills by relevance to this query using BM25 "
45
+ + "keyword search over name + description + content (Chinese tokenized with jieba); "
46
+ + "hybrid embedding retrieval is planned but not yet enabled — same field, will "
47
+ + "transparently upgrade. Write 2-5 relevant keywords for best recall. "
48
+ + "When omitted, returns the most-recently-updated skills ordered by updated_at DESC.",
49
+ },
50
+ top_k: { type: "number", description: "Max results (default 10)" },
51
+ },
52
+ }),
53
+ execute: async ({ query, top_k }) => {
54
+ try {
55
+ if (query && query.trim()) {
56
+ const hits = await core.search({ ...readIds, query, top_k });
57
+ return JSON.stringify(hits.map((h) => ({
58
+ skill_id: h.skill.skill_id, name: h.skill.name, description: h.skill.description,
59
+ version: h.skill.version, score: h.score,
60
+ })));
61
+ }
62
+ const r = await core.list({ ...readIds, pagination: { limit: top_k ?? 50, offset: 0 } });
63
+ return JSON.stringify(r.items.map((s) => ({
64
+ skill_id: s.skill_id, name: s.name, description: s.description, version: s.version,
65
+ })));
66
+ }
67
+ catch (e) {
68
+ return jsonError(e);
69
+ }
70
+ },
71
+ }),
72
+ skill_view: tool({
73
+ description: "Read a skill's full SKILL.md and resource manifest.",
74
+ inputSchema: jsonSchema({
75
+ type: "object",
76
+ properties: {
77
+ skill_id: { type: "string" },
78
+ version: { type: "number", description: "Optional historical version (default head)" },
79
+ },
80
+ required: ["skill_id"],
81
+ }),
82
+ execute: async ({ skill_id, version }) => {
83
+ try {
84
+ const r = await core.get({ ...readIds, skill_id, version });
85
+ return JSON.stringify({
86
+ skill_id: r.skill_id, version: r.version, name: r.name, description: r.description,
87
+ content: r.content, manifest: r.manifest,
88
+ });
89
+ }
90
+ catch (e) {
91
+ return jsonError(e);
92
+ }
93
+ },
94
+ }),
95
+ skill_create: tool({
96
+ description: "Create a new skill. The frontmatter `name` MUST equal the `name` parameter.",
97
+ inputSchema: jsonSchema({
98
+ type: "object",
99
+ properties: {
100
+ name: { type: "string", description: "Skill name (lowercase letters/digits/hyphen)" },
101
+ content: { type: "string", description: "Full SKILL.md text including frontmatter" },
102
+ },
103
+ required: ["name", "content"],
104
+ }),
105
+ execute: async ({ name, content }) => {
106
+ try {
107
+ const r = await core.create({ ...writeIds, name, content });
108
+ auditSink.push({ action: "create", name, skill_id: r.skill_id, version: r.version, description: r.description });
109
+ logger?.info(`[skill-tools] created ${r.skill_id}`);
110
+ return JSON.stringify({ ok: true, skill_id: r.skill_id, version: r.version });
111
+ }
112
+ catch (e) {
113
+ return jsonError(e);
114
+ }
115
+ },
116
+ }),
117
+ skill_update: tool({
118
+ description: "Replace the entire SKILL.md of an existing skill (you must own it).",
119
+ inputSchema: jsonSchema({
120
+ type: "object",
121
+ properties: {
122
+ skill_id: { type: "string" },
123
+ content: { type: "string", description: "New full SKILL.md text" },
124
+ expected_version: { type: "number", description: "Required optimistic lock — the version you just read (skill_list/skill_view). After a successful write use the returned version for the next edit." },
125
+ },
126
+ required: ["skill_id", "content", "expected_version"],
127
+ }),
128
+ execute: async ({ skill_id, content, expected_version }) => {
129
+ try {
130
+ const r = await core.update({ ...writeIds, skill_id, content, expected_version });
131
+ auditSink.push({ action: "update", name: r.name, skill_id, version: r.version });
132
+ return JSON.stringify({ ok: true, version: r.version });
133
+ }
134
+ catch (e) {
135
+ return jsonError(e);
136
+ }
137
+ },
138
+ }),
139
+ skill_patch: tool({
140
+ description: "Replace a unique substring in the SKILL.md. If old_string occurs >1 times you must pass replace_all.",
141
+ inputSchema: jsonSchema({
142
+ type: "object",
143
+ properties: {
144
+ skill_id: { type: "string" },
145
+ old_string: { type: "string" },
146
+ new_string: { type: "string" },
147
+ replace_all: { type: "boolean", description: "Default false" },
148
+ expected_version: { type: "number", description: "Required optimistic lock — the version you just read (skill_list/skill_view). After a successful write use the returned version for the next edit." },
149
+ },
150
+ required: ["skill_id", "old_string", "new_string", "expected_version"],
151
+ }),
152
+ execute: async ({ skill_id, old_string, new_string, replace_all, expected_version }) => {
153
+ try {
154
+ const r = await core.patch({ ...writeIds, skill_id, old_string, new_string, replace_all, expected_version });
155
+ auditSink.push({ action: "patch", name: r.name, skill_id, version: r.version });
156
+ return JSON.stringify({ ok: true, version: r.version });
157
+ }
158
+ catch (e) {
159
+ return jsonError(e);
160
+ }
161
+ },
162
+ }),
163
+ skill_files_write: tool({
164
+ description: "Write or overwrite a resource file in a skill's files/ directory.",
165
+ inputSchema: jsonSchema({
166
+ type: "object",
167
+ properties: {
168
+ skill_id: { type: "string" },
169
+ path: { type: "string", description: "Relative path under files/, e.g. 'scripts/run.sh'" },
170
+ content: { type: "string" },
171
+ encoding: { type: "string", enum: ["utf-8", "base64"] },
172
+ mime_type: { type: "string" },
173
+ is_executable: { type: "boolean" },
174
+ expected_version: { type: "number", description: "Required optimistic lock — the version you just read (skill_list/skill_view). After a successful write use the returned version for the next edit." },
175
+ },
176
+ required: ["skill_id", "path", "content", "expected_version"],
177
+ }),
178
+ execute: async ({ skill_id, path, content, encoding, mime_type, is_executable, expected_version }) => {
179
+ try {
180
+ const r = await core.writeFiles({
181
+ ...writeIds, skill_id, expected_version,
182
+ files: [{ path, content, encoding: encoding ?? "utf-8", mime_type, is_executable }],
183
+ });
184
+ auditSink.push({ action: "files_write", name: r.name, skill_id, version: r.version });
185
+ return JSON.stringify({ ok: true, version: r.version });
186
+ }
187
+ catch (e) {
188
+ return jsonError(e);
189
+ }
190
+ },
191
+ }),
192
+ };
193
+ }
@@ -0,0 +1,324 @@
1
+ /**
2
+ * Type definitions for the Skill module — v2 redesign (2026-06-17).
3
+ *
4
+ * Host-neutral. No imports from openclaw / hermes / automation projects.
5
+ * 设计文档:docs/design/2026-06-17-skill-redesign-v2.md
6
+ */
7
+ /**
8
+ * User-facing skill configuration. All fields optional; defaults applied
9
+ * by `resolveSkillConfig`.
10
+ */
11
+ export interface SkillConfigInput {
12
+ enabled?: boolean;
13
+ /** Override for skill metadata + vector store backend. Falls back to outer storeBackend, then 'sqlite'. */
14
+ storeBackend?: "sqlite" | "tcvdb" | "mongodb";
15
+ /** Override for skill content (SKILL.md + resources) backend. Falls back to env probe → 'local'. */
16
+ contentBackend?: "local" | "cos";
17
+ routing?: {
18
+ mode?: "bm25" | "embedding" | "hybrid";
19
+ hybridAlpha?: number;
20
+ searchTopK?: number;
21
+ charBudgetPercent?: number;
22
+ fastPathMinNameLength?: number;
23
+ };
24
+ extraction?: {
25
+ enabled?: boolean;
26
+ toolCallThreshold?: number;
27
+ model?: string;
28
+ maxIterations?: number;
29
+ /**
30
+ * 单一"归档尺寸"旋钮(字节)。默认 40960 (40KB)。派生 7 个内部字段:
31
+ * • Handler 的 bytesThreshold / requestCompressThresholdBytes = archiveBytes
32
+ * • Oversize 兜底的 chunkMaxBytes = 2 × archiveBytes
33
+ * • Oversize 兜底的 headKeepBytes / tailKeepBytes = archiveBytes
34
+ * • Extractor transcript 截断的 headChars / tailChars = archiveBytes
35
+ * 语义: 归档 payload 目标大小 = archiveBytes,上限 = 2 × archiveBytes。
36
+ */
37
+ archiveBytes?: number;
38
+ /** Skill review 单次 LLM 调用输出 token 上限。不填 → 继承顶层 llm.maxTokens。 */
39
+ maxTokens?: number;
40
+ /**
41
+ * Extractor 在把 transcript 交给 review LLM 之前, 会先注入一段"预先检索"的
42
+ * skill 列表 (由抽取器自己代跑 skill_list 得到)。本字段控制这段的最大条数,
43
+ * 同时是 relevant 检索 (LLM 生成 query + BM25 search) 与 recent 兜底
44
+ * (按 updated_at DESC 分页) 共用的上限。默认 20; <=0 或非整数 warn 落回 20。
45
+ */
46
+ prefixSkillsLimit?: number;
47
+ };
48
+ /**
49
+ * 单条大 tool 消息头尾压缩规则。只影响 tool_call / tool_result 单条 content
50
+ * 超阈值时的头尾切分;user/assistant/system 永不压缩。
51
+ */
52
+ compress?: {
53
+ /** 单条 tool 消息 content 超过多少字节才压缩。默认 2048 (2KB)。 */
54
+ toolContentThresholdBytes?: number;
55
+ /** 压缩后保留的头字节。默认 1024 (1KB)。 */
56
+ headBytes?: number;
57
+ /** 压缩后保留的尾字节。默认 1024 (1KB)。 */
58
+ tailBytes?: number;
59
+ };
60
+ resources?: {
61
+ maxResourceSizeBytes?: number;
62
+ downloadDir?: string;
63
+ allowExecutable?: boolean;
64
+ };
65
+ /** 旧版本 TTL 天数。默认 0(关闭)。设 7 = 非 head 版本创建 7 天后过期。 */
66
+ versionTtlDays?: number;
67
+ /**
68
+ * Skill 抽取 worker 池 (2026-07-30 引入)。整个进程一个池, 全 instance
69
+ * 共享一条 skill agent 队列, 池里 N 条无状态 worker loop 从队列拿活。
70
+ * 详见 docs/design/2026-07-30-skill-worker-instance-decoupling.md。
71
+ */
72
+ worker?: {
73
+ /** 池里 worker 数, 全进程 skill 抽取并发上限。默认 60。可被 env TDAI_SKILL_WORKER_CONCURRENCY 覆盖。 */
74
+ concurrency?: number;
75
+ /** dequeueAgent 单次自旋 deadline (ms)。默认 5000。 */
76
+ brpopBlockMs?: number;
77
+ /** extract-lock TTL (ms), 保护同 (instance, agent) 串行。默认 600_000 (10 min)。 */
78
+ extractLockTtlMs?: number;
79
+ /** extract-lock 续约间隔 (ms), 默认 ttl / 4。 */
80
+ extractLockRenewIntervalMs?: number;
81
+ };
82
+ }
83
+ export interface ResolvedSkillConfig {
84
+ enabled: true;
85
+ storeBackend: "sqlite" | "tcvdb" | "mongodb";
86
+ contentBackend: "local" | "cos";
87
+ routing: {
88
+ mode: "bm25" | "embedding" | "hybrid";
89
+ hybridAlpha: number;
90
+ searchTopK: number;
91
+ charBudgetPercent: number;
92
+ fastPathMinNameLength: number;
93
+ };
94
+ extraction: {
95
+ enabled: boolean;
96
+ toolCallThreshold: number;
97
+ model?: string;
98
+ maxIterations: number;
99
+ /** 归档尺寸旋钮 (字节)。用户可见配置源;下面 7 个字段由它派生。 */
100
+ archiveBytes: number;
101
+ /** Skill review 单次 LLM 调用输出 token 上限;不填 → 由 runner 继承 llm.maxTokens。 */
102
+ maxTokens?: number;
103
+ /**
104
+ * Extractor 预检索 skill 列表条数上限 (relevant BM25 检索 & recent 兜底共用)。
105
+ * 默认 20。
106
+ */
107
+ prefixSkillsLimit: number;
108
+ /** Handler: buffer 累计字节 ≥ 触发归档。= archiveBytes。 */
109
+ bytesThreshold: number;
110
+ /** Handler: 单次 add 请求 ≥ 强制走压缩路径。= archiveBytes。 */
111
+ requestCompressThresholdBytes: number;
112
+ /** Oversize 兜底: 归档 payload > 触发切分。= 2 × archiveBytes。 */
113
+ chunkMaxBytes: number;
114
+ /** Oversize 兜底: 切完保留的头字节。= archiveBytes。 */
115
+ headKeepBytes: number;
116
+ /** Oversize 兜底: 切完保留的尾字节。= archiveBytes。 */
117
+ tailKeepBytes: number;
118
+ /** Extractor transcript 截断: 保留头字符。= archiveBytes (字节数近似当字符数)。 */
119
+ headChars: number;
120
+ /** Extractor transcript 截断: 保留尾字符。= archiveBytes。 */
121
+ tailChars: number;
122
+ };
123
+ /** 单条大 tool 消息头尾压缩,参数与 CompressOptions 对齐。 */
124
+ compress: {
125
+ toolContentThresholdBytes: number;
126
+ headBytes: number;
127
+ tailBytes: number;
128
+ };
129
+ resources: {
130
+ maxResourceSizeBytes: number;
131
+ downloadDir: string;
132
+ allowExecutable: boolean;
133
+ };
134
+ /** 旧版本 TTL 秒数。0 = 关闭。 */
135
+ versionTtlSeconds: number;
136
+ /** Skill 抽取 worker 池配置 (2026-07-30)。见 SkillConfigInput.worker 注释。 */
137
+ worker: {
138
+ concurrency: number;
139
+ brpopBlockMs: number;
140
+ extractLockTtlMs: number;
141
+ extractLockRenewIntervalMs: number;
142
+ };
143
+ /** Records of automatic downgrades made during resolution. */
144
+ degradations: SkillDegradation[];
145
+ }
146
+ export interface SkillDegradation {
147
+ field: string;
148
+ from: string;
149
+ to: string;
150
+ reason: string;
151
+ level: "info" | "warn";
152
+ }
153
+ /**
154
+ * Information about ambient capabilities that resolveSkillConfig uses
155
+ * to make downgrade decisions. Keep this minimal and explicit; no
156
+ * implicit env/process reads inside resolveSkillConfig itself.
157
+ */
158
+ export interface SkillEnvProbe {
159
+ /** Outer storeBackend from MemoryTdaiConfig. */
160
+ outerStoreBackend?: "sqlite" | "tcvdb" | "mongodb";
161
+ /** TCVDB credentials present (url + apiKey + database all set). */
162
+ hasTcvdbCredentials: boolean;
163
+ /** COS credentials present (secretId + secretKey + bucket all set). */
164
+ hasCosCredentials: boolean;
165
+ /** Embedding subsystem usable (enabled + provider valid + dimensions > 0). */
166
+ embeddingAvailable: boolean;
167
+ /**
168
+ * Whether the host provides an LLMRunnerFactory. When false and
169
+ * extraction.enabled=true, we mark extraction as degraded (it stays
170
+ * "enabled" but will return [] at runtime).
171
+ */
172
+ llmRunnerAvailable: boolean;
173
+ }
174
+ /**
175
+ * 业务身份四元组。全部可选。
176
+ * team_id 和 agent_id 要么都传要么都不传(由 gateway schema 层 cross-field 校验保证)。
177
+ */
178
+ export interface IdFields {
179
+ user_id?: string;
180
+ team_id?: string;
181
+ agent_id?: string;
182
+ task_id?: string;
183
+ }
184
+ /** skill 状态。与 interface.yaml 对齐:active 或 archived。 */
185
+ export type SkillStatus = "active" | "archived";
186
+ /** manifest_json 列里的单个资源元信息。字节不在此类型中。 */
187
+ export interface SkillManifestEntry {
188
+ /** 相对 `files/` 的路径,UNIX 风格,禁 `..` / 绝对路径。例 "scripts/run.sh" */
189
+ path: string;
190
+ size_bytes: number;
191
+ mime_type: string;
192
+ is_executable: boolean;
193
+ }
194
+ export interface SkillSimilarityResult {
195
+ name: string;
196
+ description: string;
197
+ similarity: number;
198
+ }
199
+ export interface SkillProposeResult {
200
+ propose_id: string;
201
+ proposed: {
202
+ name: string;
203
+ description: string;
204
+ };
205
+ similar_skills: SkillSimilarityResult[];
206
+ }
207
+ /**
208
+ * skill 主表的一行。每行 = (skill_id, version) 一个不可变快照。
209
+ *
210
+ * - 字段对应 `skills` 表(见 skill-store-ddl.ts SKILLS_DDL)
211
+ * - `manifest` 是 `manifest_json` 列反序列化后的结构化形式
212
+ * - `is_head` 是 boolean(DB 中是 0/1 INTEGER)
213
+ */
214
+ export interface Skill {
215
+ row_id: string;
216
+ skill_id: string;
217
+ version: number;
218
+ is_head: boolean;
219
+ user_id: string;
220
+ owner_agent_id: string;
221
+ team_id: string;
222
+ task_id: string;
223
+ name: string;
224
+ description: string;
225
+ content: string;
226
+ content_hash: string;
227
+ manifest: SkillManifestEntry[];
228
+ storage_dir: string;
229
+ status: SkillStatus;
230
+ metadata_json: string;
231
+ created_at_ms: number;
232
+ updated_at_ms: number;
233
+ }
234
+ /** `appendVersion` 的入参。store 内部基于 head 推导 version+1。 */
235
+ export interface AppendVersionInput {
236
+ /** 写入身份。不传则写 "default"。 */
237
+ user_id?: string;
238
+ team_id?: string;
239
+ agent_id?: string;
240
+ task_id?: string;
241
+ /** 业务主键 — 首次创建由调用方生成;同 skill 的后续版本同 skill_id。 */
242
+ skill_id: string;
243
+ name: string;
244
+ description: string;
245
+ content: string;
246
+ content_hash: string;
247
+ manifest: SkillManifestEntry[];
248
+ storage_dir: string;
249
+ /** 仅 create 时由调用方指定为 owner_agent_id;后续版本由 store 校验后从 head 继承。 */
250
+ owner_agent_id?: string;
251
+ metadata_json?: string;
252
+ }
253
+ /** `listSkills` 的查询参数。仅返回 head + (status 满足) 的行。四个 ID 全部可选,传了就过滤。 */
254
+ export interface ListSkillsOptions {
255
+ team_id?: string;
256
+ owner_agent_id?: string;
257
+ user_id?: string;
258
+ task_id?: string;
259
+ name_prefix?: string;
260
+ status?: SkillStatus[];
261
+ limit?: number;
262
+ offset?: number;
263
+ }
264
+ /** `searchSkills` 的查询参数。仅命中 head + active 行。四个 ID 全部可选,传了就过滤。 */
265
+ export interface SearchSkillsOptions {
266
+ team_id?: string;
267
+ query: string;
268
+ queryEmbedding?: Float32Array;
269
+ topK?: number;
270
+ /**
271
+ * 检索模式(设计 §3.5.7)。
272
+ * - 'bm25' : 仅 FTS5 BM25
273
+ * - 'embedding' : 仅 vec0 KNN(需 queryEmbedding)
274
+ * - 'hybrid' : BM25 + KNN RRF 融合(需 queryEmbedding)
275
+ * 不传 / 未配置 embedding 时降级为 bm25。
276
+ */
277
+ mode?: "bm25" | "embedding" | "hybrid";
278
+ /** 可选:按 owner agent 过滤搜索结果。 */
279
+ agent_id?: string;
280
+ /** 可选:按 task 过滤搜索结果。 */
281
+ task_id?: string;
282
+ /** 可选:按 user 过滤搜索结果。 */
283
+ user_id?: string;
284
+ }
285
+ /** 抽取接口的结构化对话消息。对齐 interface.yaml §SkillImportMessage。 */
286
+ export interface ExtractMessage {
287
+ role: "user" | "assistant" | "tool_call" | "tool_result";
288
+ content: string;
289
+ timestamp?: string;
290
+ }
291
+ /**
292
+ * Skill Review Agent 用的 LLM runner 形状。boot 端构造后注入 SkillExtractor。
293
+ * 与 src/adapters/standalone/llm-runner.ts 的 StandaloneLLMRunner.run 兼容。
294
+ */
295
+ export interface ExtractorLLMRunner {
296
+ run(params: {
297
+ prompt: string;
298
+ systemPrompt?: string;
299
+ /** Tool dict (Vercel AI SDK shape). 当 enableTools=true 时驱动 tool-call 循环。 */
300
+ tools?: Record<string, unknown>;
301
+ enableTools?: boolean;
302
+ maxIterations?: number;
303
+ taskId: string;
304
+ timeoutMs?: number;
305
+ /** Worker 在锁失效时通过 abort 信号取消 LLM 调用。 */
306
+ signal?: AbortSignal;
307
+ }): Promise<string>;
308
+ }
309
+ export interface SkillFile {
310
+ frontmatter: {
311
+ name: string;
312
+ description: string;
313
+ category?: string;
314
+ created_at?: string;
315
+ updated_at?: string;
316
+ source?: "auto" | "manual";
317
+ resources?: Array<{
318
+ path: string;
319
+ type: "text" | "executable" | "binary";
320
+ }>;
321
+ };
322
+ body: string;
323
+ raw: string;
324
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Type definitions for the Skill module — v2 redesign (2026-06-17).
3
+ *
4
+ * Host-neutral. No imports from openclaw / hermes / automation projects.
5
+ * 设计文档:docs/design/2026-06-17-skill-redesign-v2.md
6
+ */
7
+ export {};