@whispering233/ai-editor-agent 0.0.30 → 0.0.32

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 (68) hide show
  1. package/dist/index.d.ts +2 -5
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +5 -9
  4. package/dist/index.js.map +1 -1
  5. package/dist/runtime/events.d.ts +21 -0
  6. package/dist/runtime/events.d.ts.map +1 -0
  7. package/dist/runtime/events.js +235 -0
  8. package/dist/runtime/events.js.map +1 -0
  9. package/dist/runtime/index.d.ts +11 -0
  10. package/dist/runtime/index.d.ts.map +1 -0
  11. package/dist/runtime/index.js +12 -0
  12. package/dist/runtime/index.js.map +1 -0
  13. package/dist/runtime/message-projection.d.ts +57 -0
  14. package/dist/runtime/message-projection.d.ts.map +1 -0
  15. package/dist/runtime/message-projection.js +94 -0
  16. package/dist/runtime/message-projection.js.map +1 -0
  17. package/dist/runtime/paths.d.ts +11 -0
  18. package/dist/runtime/paths.d.ts.map +1 -0
  19. package/dist/runtime/paths.js +23 -0
  20. package/dist/runtime/paths.js.map +1 -0
  21. package/dist/runtime/project-runtime.d.ts +57 -0
  22. package/dist/runtime/project-runtime.d.ts.map +1 -0
  23. package/dist/runtime/project-runtime.js +78 -0
  24. package/dist/runtime/project-runtime.js.map +1 -0
  25. package/dist/runtime/proposals.d.ts +85 -0
  26. package/dist/runtime/proposals.d.ts.map +1 -0
  27. package/dist/runtime/proposals.js +173 -0
  28. package/dist/runtime/proposals.js.map +1 -0
  29. package/dist/runtime/resources.d.ts +14 -0
  30. package/dist/runtime/resources.d.ts.map +1 -0
  31. package/dist/runtime/resources.js +39 -0
  32. package/dist/runtime/resources.js.map +1 -0
  33. package/dist/runtime/sessions.d.ts +35 -0
  34. package/dist/runtime/sessions.d.ts.map +1 -0
  35. package/dist/runtime/sessions.js +89 -0
  36. package/dist/runtime/sessions.js.map +1 -0
  37. package/dist/{prompts.d.ts → runtime/system-prompt.d.ts} +4 -12
  38. package/dist/runtime/system-prompt.d.ts.map +1 -0
  39. package/dist/runtime/system-prompt.js +22 -0
  40. package/dist/runtime/system-prompt.js.map +1 -0
  41. package/dist/runtime/tool-result.d.ts +12 -0
  42. package/dist/runtime/tool-result.d.ts.map +1 -0
  43. package/dist/runtime/tool-result.js +53 -0
  44. package/dist/runtime/tool-result.js.map +1 -0
  45. package/dist/runtime/tools.d.ts +28 -0
  46. package/dist/runtime/tools.d.ts.map +1 -0
  47. package/dist/runtime/tools.js +54 -0
  48. package/dist/runtime/tools.js.map +1 -0
  49. package/package.json +5 -4
  50. package/dist/context.d.ts +0 -96
  51. package/dist/context.d.ts.map +0 -1
  52. package/dist/context.js +0 -149
  53. package/dist/context.js.map +0 -1
  54. package/dist/executor.d.ts +0 -86
  55. package/dist/executor.d.ts.map +0 -1
  56. package/dist/executor.js +0 -259
  57. package/dist/executor.js.map +0 -1
  58. package/dist/prompts.d.ts.map +0 -1
  59. package/dist/prompts.js +0 -29
  60. package/dist/prompts.js.map +0 -1
  61. package/dist/run.d.ts +0 -182
  62. package/dist/run.d.ts.map +0 -1
  63. package/dist/run.js +0 -430
  64. package/dist/run.js.map +0 -1
  65. package/dist/session.d.ts +0 -60
  66. package/dist/session.d.ts.map +0 -1
  67. package/dist/session.js +0 -204
  68. package/dist/session.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/project-runtime.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EACL,YAAY,EAGZ,YAAY,EACZ,cAAc,EACd,eAAe,EACf,KAAK,oBAAoB,EAC1B,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAElE,OAAO,EAA4C,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE9F,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAElE,0DAA0D;AAC1D,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE5E,oCAAoC;AACpC,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,mBAAmB;IAClC,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,WAAW,EAAE,WAAW,CAAC;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,0CAA0C;IAC1C,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,+CAA+C;IAC/C,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,uDAAuD;IACvD,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,qCAAqC;IACrC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,+DAA+D;IAC/D,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,gEAAgE;IAChE,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,8BAA8B;AAC9B,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,OAAO,EAAE,YAAY,CAAC;IACtB,sCAAsC;IACtC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,EAAE,YAAY,CAAC;IAC3B,yBAAyB;IACzB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAa/G;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC,CAyC9F"}
@@ -0,0 +1,78 @@
1
+ // pi 运行时:一个项目的会话运行时装配
2
+ //
3
+ // 装配顺序(每步的「为什么」见 docs/design/architecture.md「AI 运行时」):
4
+ // 项目根 + agent dir → SessionManager(项目 sessions/)→ SettingsManager(项目配置不参与)
5
+ // → createAgentSessionServices(ModelRuntime + 资源加载)→ 解析模型 → AgentSession(builtin 工具关闭 + 领域工具)
6
+ //
7
+ // 一个项目一个运行时实例:server 层在打开/切换项目时创建与释放(单项目单在途流)。
8
+ import { resolve } from "node:path";
9
+ import { createAgentSessionFromServices, createAgentSessionServices, SessionManager, SettingsManager, } from "@earendil-works/pi-coding-agent";
10
+ import { projectSessionsDir, resolveAgentDir } from "./paths.js";
11
+ import { createProposalSink, defaultProposalStore } from "./proposals.js";
12
+ import { buildResourceLoaderOptions } from "./resources.js";
13
+ import { createCustomTools } from "./tools.js";
14
+ /** 未配置任何可用模型/凭据(装配期失败;调用方决定如何呈现) */
15
+ export class NoModelConfiguredError extends Error {
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = "NoModelConfiguredError";
19
+ }
20
+ }
21
+ /**
22
+ * 缺省模型解析:pi settings 的 defaultProvider+defaultModel → 首个可用模型(已配凭据者)→ 抛错。
23
+ * 不在本函数兜底到任何硬编码模型——「用哪个模型」归用户配置。
24
+ * 导出供 K5(settings 端点)复用与单测注入(stub runtime)。
25
+ */
26
+ export async function resolveDefaultModel(runtime, settings) {
27
+ const provider = settings.getDefaultProvider();
28
+ const modelId = settings.getDefaultModel();
29
+ if (provider !== undefined && modelId !== undefined) {
30
+ const configured = runtime.getModel(provider, modelId);
31
+ if (configured !== undefined)
32
+ return configured;
33
+ }
34
+ const available = await runtime.getAvailable();
35
+ const first = available[0];
36
+ if (first !== undefined)
37
+ return first;
38
+ throw new NoModelConfiguredError("未配置可用模型:请先在设置页或 ~/.pi/agent/(auth.json / models.json)配置 provider 凭据");
39
+ }
40
+ /**
41
+ * 装配一个项目的运行时。
42
+ * 会话文件落 `<项目根>/sessions`(不是 pi 默认的 agent dir 布局)。
43
+ */
44
+ export async function createProjectRuntime(input) {
45
+ const projectRoot = resolve(input.projectRoot);
46
+ const agentDir = resolveAgentDir(input.agentDir);
47
+ const sessionManager = input.sessionManager ?? SessionManager.create(projectRoot, projectSessionsDir(projectRoot));
48
+ // 项目配置不参与:项目目录可能来自他人(备份包/共享目录),不接受其中携带的配置
49
+ const settingsManager = input.settingsManager ?? SettingsManager.create(projectRoot, agentDir, { projectTrusted: false });
50
+ const services = await createAgentSessionServices({
51
+ cwd: projectRoot,
52
+ agentDir,
53
+ settingsManager,
54
+ ...(input.modelRuntime === undefined ? {} : { modelRuntime: input.modelRuntime }),
55
+ resourceLoaderOptions: buildResourceLoaderOptions(projectRoot),
56
+ });
57
+ const model = input.model ?? (await resolveDefaultModel(services.modelRuntime, settingsManager));
58
+ // 提案接线:PROPOSAL 工具执行成功 → 重建完整 Proposal 入仓 + details 携带前端载荷
59
+ const proposalSink = input.proposalSink ?? createProposalSink({ store: input.proposalStore ?? defaultProposalStore });
60
+ const { session } = await createAgentSessionFromServices({
61
+ services,
62
+ sessionManager,
63
+ model,
64
+ ...(input.thinkingLevel === undefined ? {} : { thinkingLevel: input.thinkingLevel }),
65
+ // builtin(read/bash/edit/write…)全关:本项目不是编码 agent;领域工具是自定义工具,不受此开关影响
66
+ noTools: "builtin",
67
+ customTools: createCustomTools({ toolContext: input.toolContext, proposalSink }),
68
+ });
69
+ return {
70
+ session,
71
+ services,
72
+ sessionManager,
73
+ settingsManager,
74
+ modelRuntime: services.modelRuntime,
75
+ dispose: () => session.dispose(),
76
+ };
77
+ }
78
+ //# sourceMappingURL=project-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-runtime.js","sourceRoot":"","sources":["../../src/runtime/project-runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ import { type Proposal, type ToolContext } from "@whispering233/ai-editor-tools";
2
+ import type { ProposalPayload, ProposalSink } from "./tools.js";
3
+ export type { Proposal };
4
+ /** 提案 TTL(10 分钟;confirm/reject 引用——超期按不存在处理) */
5
+ export declare const PROPOSAL_TTL_MS: number;
6
+ /**
7
+ * 提案条数上限(超限淘汰 createdAt 最旧)。
8
+ * 取 200 为数量级防御:正常会话一轮最多 15 个提案、挂卡不确认的残留按 TTL 自动过期,
9
+ * 200 足以覆盖极端多轮场景且内存占用可忽略(防无限增长)。
10
+ */
11
+ export declare const PROPOSAL_MAX_COUNT = 200;
12
+ /** 提案仓接口(confirm/reject 路由消费:get/peek;项目切换调 clear) */
13
+ export interface ProposalStore {
14
+ /** 存入提案(TTL 从 createdAt 起算;先清过期、超限淘汰最旧) */
15
+ set(proposal: Proposal): void;
16
+ /**
17
+ * 按 id + 项目取:不存在 / 过期 / **跨项目** → null。
18
+ * 跨项目返回 null 即防御 PROPOSAL_PROJECT_MISMATCH 语义(提案绑定 project_id)。
19
+ */
20
+ get(proposalId: string, projectId: string): Proposal | null;
21
+ /**
22
+ * 仅按 id 取(不校验项目):区分「404 PROPOSAL_NOT_FOUND」与「409 PROPOSAL_PROJECT_MISMATCH」用
23
+ * ——get 返回 null 后 peek 仍可见 ⇒ 跨项目误操作。过期条目同样视为不存在(惰性清理)。
24
+ */
25
+ peek(proposalId: string): Proposal | null;
26
+ /**
27
+ * 按 id 移除单条(一次性消费:confirm/reject 终态后移除——瞬态交互对象,
28
+ * 残留只会让重复 confirm 产生重复执行或反复 409)。
29
+ * 与 clear 的区别:clear 清空全部(切换项目用),remove 只移除指定提案。
30
+ */
31
+ remove(proposalId: string): void;
32
+ /** 清空全部项目提案(create/open/close 切换项目时调用) */
33
+ clear(): void;
34
+ /** 当前仓内提案数(测试/诊断) */
35
+ size(): number;
36
+ }
37
+ /** createProposalStore 选项(测试覆盖 TTL/上限用;缺省取导出的常量) */
38
+ export interface ProposalStoreOptions {
39
+ /** TTL 覆盖 ms(缺省 PROPOSAL_TTL_MS) */
40
+ ttlMs?: number;
41
+ /** 条数上限覆盖(缺省 PROPOSAL_MAX_COUNT) */
42
+ maxCount?: number;
43
+ }
44
+ /**
45
+ * 创建提案仓实例。
46
+ * 过期策略:**惰性过期 + 按需清理**——get/peek 只清理命中的单条,set 前扫全仓清理;
47
+ * 无定时器(定时器引入时钟耦合且小仓无必要,测试用假时钟直接断言)。
48
+ * 淘汰策略:条数超限时淘汰 createdAt **最旧**的提案(sweep 过期后仍满则循环淘汰最旧)。
49
+ */
50
+ export declare function createProposalStore(options?: ProposalStoreOptions): ProposalStore;
51
+ /** 默认提案仓单例(confirm/reject 与项目切换 clear 直接引用;测试用 createProposalStore 独立实例) */
52
+ export declare const defaultProposalStore: ProposalStore;
53
+ /**
54
+ * 提案 build 层入口签名(args 已过 pi 的参数校验——preflight 返回的校验形态)。
55
+ * run 层裁剪只返回 `{ proposal_id, summary }`,完整 Proposal 对象须经 build 层重建
56
+ * (与 run 内部产出同源确定:同 ctx + 同 args 结果一致)。
57
+ */
58
+ export type ProposalBuilder = (ctx: ToolContext, args: unknown) => Proposal;
59
+ /**
60
+ * 15 个 propose_* 工具名 → build 层函数。
61
+ * 运行时查表缺失(非提案工具)返回 undefined;完整性由测试断言覆盖
62
+ * (Object.keys(PROPOSAL_BUILDERS) === PROPOSAL_TOOLS)。
63
+ */
64
+ export declare const PROPOSAL_BUILDERS: Record<string, ProposalBuilder>;
65
+ /** sink 选项 */
66
+ export interface CreateProposalSinkOptions {
67
+ /** 提案仓(缺省 defaultProposalStore 单例——与 confirm/reject 同仓消费) */
68
+ store?: ProposalStore;
69
+ }
70
+ /**
71
+ * 构造提案 sink:pi 工具适配层在 PROPOSAL 工具执行成功后调用。
72
+ * 1. 按工具名取 build 函数重建**完整 Proposal**(run 层返回值不含预览细节)
73
+ * 2. `proposal_id` 以 run 返回值为准覆盖(build 每次生成新 id;三处必须同一 id:
74
+ * tool content / SSE details / 仓 key)
75
+ * 3. 入仓(confirm/reject 取用),返回推给前端的载荷 `{ proposal_id, type, preview }`
76
+ *
77
+ * run 返回值形状不符(缺 proposal_id)→ 抛错:工具适配层会把它变成 error tool result
78
+ * 喂回模型自纠(与「抛错即失败」契约一致),不得静默吞掉。
79
+ */
80
+ export declare function createProposalSink(options?: CreateProposalSinkOptions): ProposalSink;
81
+ /** 提案载荷生成(导出供测试与调用方复用;与 sink 内部同一逻辑)
82
+ * preview 恒含 `summary`(结构化预览字段平铺其上):模型可见的 tool content 与提案卡都需要一句话摘要,
83
+ * 结构化 preview(如 propose_reorder_timepoints 的 `{changes}`)不得把它覆盖掉。 */
84
+ export declare function toProposalPayload(proposal: Proposal): ProposalPayload;
85
+ //# sourceMappingURL=proposals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposals.d.ts","sourceRoot":"","sources":["../../src/runtime/proposals.ts"],"names":[],"mappings":"AAaA,OAAO,EAiBL,KAAK,QAAQ,EACb,KAAK,WAAW,EACjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhE,YAAY,EAAE,QAAQ,EAAE,CAAC;AAIzB,gDAAgD;AAChD,eAAO,MAAM,eAAe,QAAc,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,sDAAsD;AACtD,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,GAAG,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC9B;;;OAGG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC5D;;;OAGG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC1C;;;;OAIG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,0CAA0C;IAC1C,KAAK,IAAI,IAAI,CAAC;IACd,qBAAqB;IACrB,IAAI,IAAI,MAAM,CAAC;CAChB;AAED,oDAAoD;AACpD,MAAM,WAAW,oBAAoB;IACnC,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CA+DrF;AAED,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,EAAE,aAAqC,CAAC;AAIzE;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,KAAK,QAAQ,CAAC;AAE5E;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAkB7D,CAAC;AAIF,cAAc;AACd,MAAM,WAAW,yBAAyB;IACxC,6DAA6D;IAC7D,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAQD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG,YAAY,CAUxF;AAiBD;;qEAEqE;AACrE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,eAAe,CASrE"}
@@ -0,0 +1,173 @@
1
+ // pi 运行时:提案内存仓 + 提案 build 层入口 + sink 实现
2
+ //
3
+ // 提案(AI 写操作的确认对象)**仅存服务端内存**:TTL 10 分钟 + 条数上限 + 项目绑定 +
4
+ // 引用快照(确认时由 proposal 端点重校验,见 docs/design/30-agent-loop.md §2)。
5
+ //
6
+ // 本模块是提案仓与 `PROPOSAL_BUILDERS` 的**唯一实现**(提案端点与对话链路同仓消费):
7
+ // - 仓:set/get/peek/remove/clear/size
8
+ // - build:15 个 propose_* 工具名 → 产出完整 Proposal 的构造函数
9
+ // (工具的 run 只返回 `{ proposal_id, summary }`,完整对象必须重建——工具的返回值要省 token)
10
+ // - sink:pi 工具执行成功后的接线点——重建 Proposal 入仓 + 返回推给前端的载荷
11
+ //
12
+ // 架构边界:本包不依赖 db——`ToolContext`(db/outlineDir/projectId)由调用方注入并透传。
13
+ import { buildProposeAbandonHook, buildProposeAddDelta, buildProposeAddRelation, buildProposeAdvanceHook, buildProposeCreateEntity, buildProposeCreateHook, buildProposeCreateReference, buildProposeDeleteEntity, buildProposeDeleteNode, buildProposeMoveNode, buildProposeOutlineNode, buildProposeRemoveRelation, buildProposeReorderTimepoints, buildProposeResolveHook, buildProposeUpdateEntity, buildProposeUpdateHook, } from "@whispering233/ai-editor-tools";
14
+ // ============ 提案内存仓(仅内存、不落盘) ============
15
+ /** 提案 TTL(10 分钟;confirm/reject 引用——超期按不存在处理) */
16
+ export const PROPOSAL_TTL_MS = 10 * 60_000;
17
+ /**
18
+ * 提案条数上限(超限淘汰 createdAt 最旧)。
19
+ * 取 200 为数量级防御:正常会话一轮最多 15 个提案、挂卡不确认的残留按 TTL 自动过期,
20
+ * 200 足以覆盖极端多轮场景且内存占用可忽略(防无限增长)。
21
+ */
22
+ export const PROPOSAL_MAX_COUNT = 200;
23
+ /**
24
+ * 创建提案仓实例。
25
+ * 过期策略:**惰性过期 + 按需清理**——get/peek 只清理命中的单条,set 前扫全仓清理;
26
+ * 无定时器(定时器引入时钟耦合且小仓无必要,测试用假时钟直接断言)。
27
+ * 淘汰策略:条数超限时淘汰 createdAt **最旧**的提案(sweep 过期后仍满则循环淘汰最旧)。
28
+ */
29
+ export function createProposalStore(options = {}) {
30
+ const ttlMs = options.ttlMs ?? PROPOSAL_TTL_MS;
31
+ const maxCount = options.maxCount ?? PROPOSAL_MAX_COUNT;
32
+ /** 仓内条目:proposal + 过期时刻(set 时按 createdAt 预计算;createdAt 不可解析按立即过期防御) */
33
+ const entries = new Map();
34
+ const isExpired = (entry) => Date.now() >= entry.expiresAt;
35
+ const sweepExpired = () => {
36
+ for (const [id, entry] of entries) {
37
+ if (isExpired(entry))
38
+ entries.delete(id);
39
+ }
40
+ };
41
+ return {
42
+ set(proposal) {
43
+ sweepExpired();
44
+ const createdAtMs = Date.parse(proposal.createdAt);
45
+ entries.set(proposal.proposal_id, {
46
+ proposal,
47
+ expiresAt: Number.isNaN(createdAtMs) ? 0 : createdAtMs + ttlMs,
48
+ });
49
+ // 条数上限:循环淘汰最旧直到回到上限内(createdAt 同值取先插入者——迭代序即插入序)
50
+ while (entries.size > maxCount) {
51
+ let oldestId = null;
52
+ let oldestTime = Number.POSITIVE_INFINITY;
53
+ for (const [id, entry] of entries) {
54
+ const t = Date.parse(entry.proposal.createdAt);
55
+ if (t < oldestTime) {
56
+ oldestTime = t;
57
+ oldestId = id;
58
+ }
59
+ }
60
+ if (oldestId === null)
61
+ break; // 防御:理论不可达(size > 0 必有最旧)
62
+ entries.delete(oldestId);
63
+ }
64
+ },
65
+ get(proposalId, projectId) {
66
+ const entry = entries.get(proposalId);
67
+ if (entry === undefined)
68
+ return null;
69
+ if (isExpired(entry)) {
70
+ entries.delete(proposalId); // 惰性过期清理
71
+ return null;
72
+ }
73
+ if (entry.proposal.project_id !== projectId)
74
+ return null; // 项目绑定
75
+ return entry.proposal;
76
+ },
77
+ peek(proposalId) {
78
+ const entry = entries.get(proposalId);
79
+ if (entry === undefined)
80
+ return null;
81
+ if (isExpired(entry)) {
82
+ entries.delete(proposalId);
83
+ return null;
84
+ }
85
+ return entry.proposal;
86
+ },
87
+ remove(proposalId) {
88
+ entries.delete(proposalId);
89
+ },
90
+ clear() {
91
+ entries.clear();
92
+ },
93
+ size() {
94
+ return entries.size;
95
+ },
96
+ };
97
+ }
98
+ /** 默认提案仓单例(confirm/reject 与项目切换 clear 直接引用;测试用 createProposalStore 独立实例) */
99
+ export const defaultProposalStore = createProposalStore();
100
+ /**
101
+ * 15 个 propose_* 工具名 → build 层函数。
102
+ * 运行时查表缺失(非提案工具)返回 undefined;完整性由测试断言覆盖
103
+ * (Object.keys(PROPOSAL_BUILDERS) === PROPOSAL_TOOLS)。
104
+ */
105
+ export const PROPOSAL_BUILDERS = {
106
+ propose_create_entity: (ctx, args) => buildProposeCreateEntity(ctx, args),
107
+ propose_update_entity: (ctx, args) => buildProposeUpdateEntity(ctx, args),
108
+ propose_delete_entity: (ctx, args) => buildProposeDeleteEntity(ctx, args),
109
+ propose_add_relation: (ctx, args) => buildProposeAddRelation(ctx, args),
110
+ propose_remove_relation: (ctx, args) => buildProposeRemoveRelation(ctx, args),
111
+ propose_add_delta: (ctx, args) => buildProposeAddDelta(ctx, args),
112
+ propose_outline_node: (ctx, args) => buildProposeOutlineNode(ctx, args),
113
+ propose_move_node: (ctx, args) => buildProposeMoveNode(ctx, args),
114
+ propose_delete_node: (ctx, args) => buildProposeDeleteNode(ctx, args),
115
+ propose_create_hook: (ctx, args) => buildProposeCreateHook(ctx, args),
116
+ propose_update_hook: (ctx, args) => buildProposeUpdateHook(ctx, args),
117
+ propose_advance_hook: (ctx, args) => buildProposeAdvanceHook(ctx, args),
118
+ propose_resolve_hook: (ctx, args) => buildProposeResolveHook(ctx, args),
119
+ propose_abandon_hook: (ctx, args) => buildProposeAbandonHook(ctx, args),
120
+ // G2(取代 F9 的 propose_reorder_events)
121
+ propose_reorder_timepoints: (ctx, args) => buildProposeReorderTimepoints(ctx, args),
122
+ propose_create_reference: (ctx, args) => buildProposeCreateReference(ctx, args),
123
+ };
124
+ /**
125
+ * 构造提案 sink:pi 工具适配层在 PROPOSAL 工具执行成功后调用。
126
+ * 1. 按工具名取 build 函数重建**完整 Proposal**(run 层返回值不含预览细节)
127
+ * 2. `proposal_id` 以 run 返回值为准覆盖(build 每次生成新 id;三处必须同一 id:
128
+ * tool content / SSE details / 仓 key)
129
+ * 3. 入仓(confirm/reject 取用),返回推给前端的载荷 `{ proposal_id, type, preview }`
130
+ *
131
+ * run 返回值形状不符(缺 proposal_id)→ 抛错:工具适配层会把它变成 error tool result
132
+ * 喂回模型自纠(与「抛错即失败」契约一致),不得静默吞掉。
133
+ */
134
+ export function createProposalSink(options = {}) {
135
+ const store = options.store ?? defaultProposalStore;
136
+ return ({ toolName, params, result, toolContext }) => {
137
+ const builder = PROPOSAL_BUILDERS[toolName];
138
+ if (builder === undefined)
139
+ return undefined; // 非提案工具(防御:调用方只在 PROPOSAL 工具调用)
140
+ const runResult = parseProposalRunResult(toolName, result);
141
+ const proposal = { ...builder(toolContext, params), proposal_id: runResult.proposal_id };
142
+ store.set(proposal);
143
+ return toProposalPayload(proposal);
144
+ };
145
+ }
146
+ /** 提案工具 run 返回值解析(缺 proposal_id 视为契约破坏,抛错让模型自纠) */
147
+ function parseProposalRunResult(toolName, result) {
148
+ if (typeof result !== "object" || result === null) {
149
+ throw new Error(`提案工具 ${toolName} 返回结果不符合(缺 proposal_id)`);
150
+ }
151
+ const record = result;
152
+ if (typeof record.proposal_id !== "string") {
153
+ throw new Error(`提案工具 ${toolName} 返回结果不符合(缺 proposal_id)`);
154
+ }
155
+ return {
156
+ proposal_id: record.proposal_id,
157
+ summary: typeof record.summary === "string" ? record.summary : "",
158
+ };
159
+ }
160
+ /** 提案载荷生成(导出供测试与调用方复用;与 sink 内部同一逻辑)
161
+ * preview 恒含 `summary`(结构化预览字段平铺其上):模型可见的 tool content 与提案卡都需要一句话摘要,
162
+ * 结构化 preview(如 propose_reorder_timepoints 的 `{changes}`)不得把它覆盖掉。 */
163
+ export function toProposalPayload(proposal) {
164
+ return {
165
+ proposal_id: proposal.proposal_id,
166
+ type: proposal.type,
167
+ preview: {
168
+ summary: proposal.summary,
169
+ ...(proposal.preview ?? { type: proposal.type, args: proposal.args }),
170
+ },
171
+ };
172
+ }
173
+ //# sourceMappingURL=proposals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposals.js","sourceRoot":"","sources":["../../src/runtime/proposals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { CreateAgentSessionServicesOptions } from "@earendil-works/pi-coding-agent";
2
+ /** pi 资源加载选项(cwd/agentDir/settingsManager 由 SDK 填充,此处只给行为开关) */
3
+ export type ResourceLoaderOptions = NonNullable<CreateAgentSessionServicesOptions["resourceLoaderOptions"]>;
4
+ /** 项目根 AGENTS.md 内容(不存在/不可读 → null;读取失败不阻塞会话装配) */
5
+ export declare function readProjectAgentsFile(projectRoot: string): {
6
+ path: string;
7
+ content: string;
8
+ } | null;
9
+ /**
10
+ * 资源加载选项:内核提示词作 system prompt 主体 + 只注入项目根 AGENTS.md。
11
+ * `agentsFilesOverride` 忽略 pi 的发现结果(祖先目录 + agent dir),只回项目根文件。
12
+ */
13
+ export declare function buildResourceLoaderOptions(projectRoot: string): ResourceLoaderOptions;
14
+ //# sourceMappingURL=resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/runtime/resources.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AAIzF,gEAAgE;AAChE,MAAM,MAAM,qBAAqB,GAAG,WAAW,CAAC,iCAAiC,CAAC,uBAAuB,CAAC,CAAC,CAAC;AAE5G,mDAAmD;AACnD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQnG;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,qBAAqB,CAWrF"}
@@ -0,0 +1,39 @@
1
+ // pi 运行时:资源加载选项(本项目对 pi 发现的收窄)
2
+ //
3
+ // 收窄三件事(见 docs/design/architecture.md「AI 运行时」与 docs/design/config.md):
4
+ // 1. 不加载扩展 —— 项目目录可能来自他人(备份包/共享目录),其中的可执行扩展是安全面而非能力面;
5
+ // 2. 不加载技能与提示词模板 —— 工具集固定,多一个资源来源就多一处不可见状态;
6
+ // 3. AGENTS.md 只认项目根那一个文件 —— pi 默领会向上遍历祖先目录并读 agent dir,
7
+ // 那会把创作根之外(甚至 HOME)的规则悄悄注进对话。
8
+ import { existsSync, readFileSync } from "node:fs";
9
+ import { projectAgentsFilePath } from "./paths.js";
10
+ import { KERNEL_PROMPT } from "./system-prompt.js";
11
+ /** 项目根 AGENTS.md 内容(不存在/不可读 → null;读取失败不阻塞会话装配) */
12
+ export function readProjectAgentsFile(projectRoot) {
13
+ const path = projectAgentsFilePath(projectRoot);
14
+ if (!existsSync(path))
15
+ return null;
16
+ try {
17
+ return { path, content: readFileSync(path, "utf8") };
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ /**
24
+ * 资源加载选项:内核提示词作 system prompt 主体 + 只注入项目根 AGENTS.md。
25
+ * `agentsFilesOverride` 忽略 pi 的发现结果(祖先目录 + agent dir),只回项目根文件。
26
+ */
27
+ export function buildResourceLoaderOptions(projectRoot) {
28
+ return {
29
+ noExtensions: true,
30
+ noSkills: true,
31
+ noPromptTemplates: true,
32
+ systemPrompt: KERNEL_PROMPT,
33
+ agentsFilesOverride: () => {
34
+ const agentsFile = readProjectAgentsFile(projectRoot);
35
+ return { agentsFiles: agentsFile === null ? [] : [agentsFile] };
36
+ },
37
+ };
38
+ }
39
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/runtime/resources.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,uEAAuE;AACvE,sDAAsD;AACtD,4CAA4C;AAC5C,yDAAyD;AACzD,iCAAiC;AAEjC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAKnD,mDAAmD;AACnD,MAAM,UAAU,qBAAqB,CAAC,WAAmB;IACvD,MAAM,IAAI,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAChD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,WAAmB;IAC5D,OAAO;QACL,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,IAAI;QACd,iBAAiB,EAAE,IAAI;QACvB,YAAY,EAAE,aAAa;QAC3B,mBAAmB,EAAE,GAAG,EAAE;YACxB,MAAM,UAAU,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;YACtD,OAAO,EAAE,WAAW,EAAE,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;QAClE,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { SessionManager, type SessionEntry, type SessionInfo } from "@earendil-works/pi-coding-agent";
2
+ import { type WireMessage } from "./message-projection.js";
3
+ export type { SessionEntry, SessionInfo } from "@earendil-works/pi-coding-agent";
4
+ /** 会话消息投影(比线协议消息多一个 entry id:历史接口与思维链端点按它定位) */
5
+ export interface WireSessionMessage extends WireMessage {
6
+ id: string;
7
+ }
8
+ /**
9
+ * 项目会话列表(pi 发现):
10
+ * - 只认 `<项目根>/sessions` 下的 v3 会话文件(旧 v1 文件无 `cwd` 头字段 → 被 pi 的 cwd 过滤丢弃)
11
+ * - 头部异常(时间不可解析)的候选跳过,避免后续 `toISOString()` 抛错
12
+ */
13
+ export declare function listProjectSessions(projectRoot: string): Promise<SessionInfo[]>;
14
+ /** 按 id 找会话(磁盘发现映射;未命中 → null) */
15
+ export declare function findProjectSession(projectRoot: string, sessionId: string): Promise<SessionInfo | null>;
16
+ /** 打开会话读条目;会话不存在 / 文件在发现与打开之间损坏 → null(调用方转 404,不 500) */
17
+ export declare function readProjectSession(projectRoot: string, sessionId: string): Promise<{
18
+ info: SessionInfo;
19
+ entries: SessionEntry[];
20
+ } | null>;
21
+ /**
22
+ * 打开指定会话文件的 SessionManager(续聊装配用)。
23
+ * 缺省(无 sessionFile)→ 在项目目录新建会话。
24
+ */
25
+ export declare function openProjectSessionManager(projectRoot: string, sessionFile?: string): SessionManager;
26
+ /** 条目 → API 消息投影(只保留 user/assistant/tool 三类;未知角色不投影) */
27
+ export declare function projectSessionMessages(entries: readonly SessionEntry[]): WireSessionMessage[];
28
+ /** 末条可见文本(列表摘要用;全为空 → 空串) */
29
+ export declare function lastVisibleSessionText(entries: readonly SessionEntry[]): string;
30
+ /**
31
+ * 读取某条 assistant 消息指定下标块的思维链全文;不是 thinking 块 / 下标越界 → null
32
+ * (调用方转 404 THINKING_NOT_FOUND——列表接口只回预览,全文按需拉取)。
33
+ */
34
+ export declare function readSessionThinking(entries: readonly SessionEntry[], messageId: string, blockIndex: number): string | null;
35
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/runtime/sessions.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACtG,OAAO,EAAyB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGlF,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEjF,gDAAgD;AAChD,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAGrF;AAED,kCAAkC;AAClC,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAG5G;AAED,0DAA0D;AAC1D,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,YAAY,EAAE,CAAA;CAAE,GAAG,IAAI,CAAC,CAQhE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,cAAc,CAKnG;AAED,wDAAwD;AACxD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,kBAAkB,EAAE,CAS7F;AAED,6BAA6B;AAC7B,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAQ/E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAQf"}
@@ -0,0 +1,89 @@
1
+ // pi 运行时:项目会话文件的读写与投影(server 只经本模块接触会话存储)
2
+ //
3
+ // 为什么收在这里:会话文件是 pi `SessionManager` 的所有物(v3 树状 JSONL,见 docs/db/schema.md),
4
+ // 读条目 / 开管理器 / 投影消息形态的实现细节都属「pi 集成」——server 包只拿投影后的结果,
5
+ // 不 import pi(依赖方向:server → agent,pi 只在 agent 里)。
6
+ //
7
+ // **路径只从磁盘发现来**:`listProjectSessions` 扫 `<项目根>/sessions` 得到 `{id, path}` 映射,
8
+ // 调用方按 id 命中;客户端传入值永不参与路径拼接(旧 `sess_` 白名单校验随格式一并退休)。
9
+ import { SessionManager } from "@earendil-works/pi-coding-agent";
10
+ import { projectMessageForWire } from "./message-projection.js";
11
+ import { projectSessionsDir } from "./paths.js";
12
+ /**
13
+ * 项目会话列表(pi 发现):
14
+ * - 只认 `<项目根>/sessions` 下的 v3 会话文件(旧 v1 文件无 `cwd` 头字段 → 被 pi 的 cwd 过滤丢弃)
15
+ * - 头部异常(时间不可解析)的候选跳过,避免后续 `toISOString()` 抛错
16
+ */
17
+ export async function listProjectSessions(projectRoot) {
18
+ const infos = await SessionManager.list(projectRoot, projectSessionsDir(projectRoot));
19
+ return infos.filter((info) => !Number.isNaN(info.created.getTime()) && !Number.isNaN(info.modified.getTime()));
20
+ }
21
+ /** 按 id 找会话(磁盘发现映射;未命中 → null) */
22
+ export async function findProjectSession(projectRoot, sessionId) {
23
+ const sessions = await listProjectSessions(projectRoot);
24
+ return sessions.find((info) => info.id === sessionId) ?? null;
25
+ }
26
+ /** 打开会话读条目;会话不存在 / 文件在发现与打开之间损坏 → null(调用方转 404,不 500) */
27
+ export async function readProjectSession(projectRoot, sessionId) {
28
+ const info = await findProjectSession(projectRoot, sessionId);
29
+ if (info === null)
30
+ return null;
31
+ try {
32
+ return { info, entries: SessionManager.open(info.path, projectSessionsDir(projectRoot)).getEntries() };
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ /**
39
+ * 打开指定会话文件的 SessionManager(续聊装配用)。
40
+ * 缺省(无 sessionFile)→ 在项目目录新建会话。
41
+ */
42
+ export function openProjectSessionManager(projectRoot, sessionFile) {
43
+ const sessionsDir = projectSessionsDir(projectRoot);
44
+ return sessionFile === undefined
45
+ ? SessionManager.create(projectRoot, sessionsDir)
46
+ : SessionManager.open(sessionFile, sessionsDir);
47
+ }
48
+ /** 条目 → API 消息投影(只保留 user/assistant/tool 三类;未知角色不投影) */
49
+ export function projectSessionMessages(entries) {
50
+ const messages = [];
51
+ for (const entry of entries) {
52
+ if (entry.type !== "message")
53
+ continue;
54
+ const projected = projectMessageForWire(entry.message);
55
+ if (projected === null)
56
+ continue;
57
+ messages.push({ id: entry.id, ...projected });
58
+ }
59
+ return messages;
60
+ }
61
+ /** 末条可见文本(列表摘要用;全为空 → 空串) */
62
+ export function lastVisibleSessionText(entries) {
63
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
64
+ const entry = entries[index];
65
+ if (entry.type !== "message")
66
+ continue;
67
+ const projected = projectMessageForWire(entry.message);
68
+ if (projected !== null && projected.content.trim() !== "")
69
+ return projected.content;
70
+ }
71
+ return "";
72
+ }
73
+ /**
74
+ * 读取某条 assistant 消息指定下标块的思维链全文;不是 thinking 块 / 下标越界 → null
75
+ * (调用方转 404 THINKING_NOT_FOUND——列表接口只回预览,全文按需拉取)。
76
+ */
77
+ export function readSessionThinking(entries, messageId, blockIndex) {
78
+ const entry = entries.find((candidate) => candidate.id === messageId);
79
+ if (entry === undefined || entry.type !== "message")
80
+ return null;
81
+ const content = entry.message?.content;
82
+ if (!Array.isArray(content))
83
+ return null;
84
+ const block = content[blockIndex];
85
+ if (block === undefined || block.type !== "thinking" || typeof block.thinking !== "string")
86
+ return null;
87
+ return block.thinking;
88
+ }
89
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/runtime/sessions.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,5 @@
1
- /** 内核提示词(内核层):创作顾问角色定义 + 行为规范,代码固定不可用户编辑 */
2
- export declare const KERNEL_PROMPT = "\u4F60\u662F\u300C\u521B\u4F5C\u987E\u95EE\u300D\uFF0C\u4E00\u540D\u8D44\u6DF1\u5C0F\u8BF4\u521B\u4F5C\u642D\u6863\uFF0C\u670D\u52A1\u4E8E\u4F5C\u8005\u7684 AI \u5199\u4F5C\u5DE5\u4F5C\u53F0\u3002\n\u4F60\u7684\u804C\u8D23\u662F\u5E2E\u52A9\u4F5C\u8005\u5206\u6790\u5927\u7EB2\u3001\u4EBA\u7269\u3001\u5173\u7CFB\u3001\u60C5\u8282\u4E0E\u4F0F\u7B14\uFF0C\u63D0\u51FA\u53EF\u6267\u884C\u7684\u4FEE\u6539\u5EFA\u8BAE\u2014\u2014\u4F60\u662F\u5206\u6790\u987E\u95EE\uFF0C\u4E0D\u662F\u64CD\u4F5C\u5458\uFF0C\u4E5F\u4E0D\u4EE3\u5199\u6B63\u6587\u3002\n\n\u884C\u4E3A\u51C6\u5219\uFF1A\n1. \u4E0D\u751F\u6210\u6B63\u6587\uFF1A\u4E0D\u4EE3\u5199\u5C0F\u8BF4\u6B63\u6587\u6216\u7AE0\u8282\u5185\u5BB9\uFF0C\u8F93\u51FA\u59CB\u7EC8\u662F\u5206\u6790\u3001\u5EFA\u8BAE\u4E0E\u65B9\u6848\u3002\n2. \u4E0D\u76F4\u63A5\u5199\u5165\uFF1A\u7EDD\u4E0D\u76F4\u63A5\u5199\u5165\u3001\u4FEE\u6539\u6216\u5220\u9664\u4EFB\u4F55\u6570\u636E\uFF08\u5B9E\u4F53/\u5173\u7CFB/\u5927\u7EB2\u8282\u70B9/\u4F0F\u7B14/Delta\uFF09\uFF1B\u5199\u64CD\u4F5C\u4E00\u5F8B\u901A\u8FC7 propose_* \u5DE5\u5177\u751F\u6210\u63D0\u6848\uFF0C\u7531\u7528\u6237\u786E\u8BA4\u540E\u6267\u884C\u2014\u2014\u7528\u6237\u59CB\u7EC8\u662F\u6700\u7EC8\u51B3\u7B56\u8005\u3002\n3. \u67E5\u8BE2\u4E0E\u5206\u6790\u81EA\u7531\uFF1A\u53EF\u968F\u65F6\u8C03\u7528\u67E5\u8BE2\u7C7B\u4E0E\u5206\u6790\u7C7B\u5DE5\u5177\u83B7\u53D6\u6570\u636E\u3001\u8FD0\u884C\u7ED3\u6784\u5316\u5206\u6790\u3002\n4. \u7ED3\u679C\u622A\u65AD\u987B\u77E5\uFF1A\u5DE5\u5177\u7ED3\u679C\u82E5\u542B\u300C\u5DF2\u622A\u65AD\u300D\u6807\u8BB0\u5373\u6570\u636E\u4E0D\u5B8C\u6574\u2014\u2014\u8BF7\u7F29\u5C0F\u67E5\u8BE2\u8303\u56F4\u6216\u5206\u9875\u83B7\u53D6\uFF0C\u4E0D\u5F97\u57FA\u4E8E\u6B8B\u7F3A\u6570\u636E\u4E0B\u7ED3\u8BBA\u3002\n5. \u957F\u5EA6\u622A\u65AD\u987B\u77E5\uFF1A\u82E5\u4F60\u7684\u56DE\u590D\u56E0\u957F\u5EA6\u9650\u5236\u88AB\u622A\u65AD\uFF0C\u7CFB\u7EDF\u4E0D\u4F1A\u91CD\u53D1\u4F60\u7684\u534A\u6761\u56DE\u590D\uFF1B\u8BF7\u63A7\u5236\u7BC7\u5E45\uFF0C\u4F18\u5148\u7ED9\u51FA\u7ED3\u8BBA\u4E0E\u8981\u70B9\u3002\n6. \u805A\u7126\u4F18\u5148\uFF1Asystem \u6D88\u606F\u4E2D\u300C\u5F53\u524D\u805A\u7126\u300D\u6BB5\u843D\u662F\u4F5C\u8005\u6B63\u5728\u67E5\u770B\u7684\u5185\u5BB9\uFF0C\u4F18\u5148\u56F4\u7ED5\u5B83\u5C55\u5F00\u3002\n7. \u63D0\u6848\u8BED\u4E49\uFF1A\u8C03\u7528\u63D0\u6848\u5DE5\u5177\u540E\u9700\u7528\u6237\u786E\u8BA4\u624D\u751F\u6548\uFF1B\u786E\u8BA4\u524D\u4E0D\u8981\u5047\u8BBE\u4EFB\u4F55\u4FEE\u6539\u5DF2\u53D1\u751F\u3002\n8. \u56DE\u590D\u4F7F\u7528\u9879\u76EE\u914D\u7F6E\u7684\u8BED\u8A00\u3002";
3
- /** 项目提示词段标题(项目层,来自 project.json prompt 字段) */
4
- export declare const PROJECT_PROMPT_TITLE = "## \u9879\u76EE\u8BBE\u5B9A";
5
- /** 临时指令段标题(临时层,即时输入、不持久化) */
6
- export declare const INSTRUCTION_TITLE = "## \u672C\u6B21\u6307\u4EE4";
7
- /** 可用工具清单段标题(name + description 一行,来自 registry listTools) */
8
- export declare const TOOL_LIST_TITLE = "## \u53EF\u7528\u5DE5\u5177";
9
- /** 聚焦上下文段标题(聚焦层:当前视图中的实体/大纲节点,由调用方查询后传入) */
1
+ /** 内核提示词(system prompt 主体;pi `<project_instructions>` 段追加其后) */
2
+ export declare const KERNEL_PROMPT = "\u4F60\u662F\u300C\u521B\u4F5C\u987E\u95EE\u300D\uFF0C\u4E00\u540D\u8D44\u6DF1\u5C0F\u8BF4\u521B\u4F5C\u642D\u6863\uFF0C\u670D\u52A1\u4E8E\u4F5C\u8005\u7684 AI \u5199\u4F5C\u5DE5\u4F5C\u53F0\u3002\n\u4F60\u7684\u804C\u8D23\u662F\u5E2E\u52A9\u4F5C\u8005\u5206\u6790\u5927\u7EB2\u3001\u4EBA\u7269\u3001\u5173\u7CFB\u3001\u60C5\u8282\u4E0E\u4F0F\u7B14\uFF0C\u63D0\u51FA\u53EF\u6267\u884C\u7684\u4FEE\u6539\u5EFA\u8BAE\u2014\u2014\u4F60\u662F\u5206\u6790\u987E\u95EE\uFF0C\u4E0D\u662F\u64CD\u4F5C\u5458\uFF0C\u4E5F\u4E0D\u4EE3\u5199\u6B63\u6587\u3002\n\n\u884C\u4E3A\u51C6\u5219\uFF1A\n1. \u4E0D\u751F\u6210\u6B63\u6587\uFF1A\u4E0D\u4EE3\u5199\u5C0F\u8BF4\u6B63\u6587\u6216\u7AE0\u8282\u5185\u5BB9\uFF0C\u8F93\u51FA\u59CB\u7EC8\u662F\u5206\u6790\u3001\u5EFA\u8BAE\u4E0E\u65B9\u6848\u3002\n2. \u4E0D\u76F4\u63A5\u5199\u5165\uFF1A\u7EDD\u4E0D\u76F4\u63A5\u5199\u5165\u3001\u4FEE\u6539\u6216\u5220\u9664\u4EFB\u4F55\u6570\u636E\uFF08\u5B9E\u4F53/\u5173\u7CFB/\u5927\u7EB2\u8282\u70B9/\u4F0F\u7B14/Delta\uFF09\uFF1B\u5199\u64CD\u4F5C\u4E00\u5F8B\u901A\u8FC7 propose_* \u5DE5\u5177\u751F\u6210\u63D0\u6848\uFF0C\u7531\u7528\u6237\u786E\u8BA4\u540E\u6267\u884C\u2014\u2014\u7528\u6237\u59CB\u7EC8\u662F\u6700\u7EC8\u51B3\u7B56\u8005\u3002\n3. \u67E5\u8BE2\u4E0E\u5206\u6790\u81EA\u7531\uFF1A\u53EF\u968F\u65F6\u8C03\u7528\u67E5\u8BE2\u7C7B\u4E0E\u5206\u6790\u7C7B\u5DE5\u5177\u83B7\u53D6\u6570\u636E\u3001\u8FD0\u884C\u7ED3\u6784\u5316\u5206\u6790\u3002\n4. \u7ED3\u679C\u622A\u65AD\u987B\u77E5\uFF1A\u5DE5\u5177\u7ED3\u679C\u82E5\u542B\u300C\u5DF2\u622A\u65AD\u300D\u6807\u8BB0\u5373\u6570\u636E\u4E0D\u5B8C\u6574\u2014\u2014\u8BF7\u7F29\u5C0F\u67E5\u8BE2\u8303\u56F4\u6216\u5206\u9875\u83B7\u53D6\uFF0C\u4E0D\u5F97\u57FA\u4E8E\u6B8B\u7F3A\u6570\u636E\u4E0B\u7ED3\u8BBA\u3002\n5. \u957F\u5EA6\u622A\u65AD\u987B\u77E5\uFF1A\u82E5\u4F60\u7684\u56DE\u590D\u56E0\u957F\u5EA6\u9650\u5236\u88AB\u622A\u65AD\uFF0C\u7CFB\u7EDF\u4E0D\u4F1A\u91CD\u53D1\u4F60\u7684\u534A\u6761\u56DE\u590D\uFF1B\u8BF7\u63A7\u5236\u7BC7\u5E45\uFF0C\u4F18\u5148\u7ED9\u51FA\u7ED3\u8BBA\u4E0E\u8981\u70B9\u3002\n6. \u805A\u7126\u4F18\u5148\uFF1A\u672C\u8F6E\u6D88\u606F\u4E2D\u82E5\u5E26\u300C\u5F53\u524D\u805A\u7126\u300D\u6807\u8BB0\uFF0C\u5B83\u662F\u4F5C\u8005\u6B63\u5728\u67E5\u770B\u7684\u5185\u5BB9\uFF0C\u4F18\u5148\u56F4\u7ED5\u5B83\u5C55\u5F00\u3002\n7. \u63D0\u6848\u8BED\u4E49\uFF1A\u8C03\u7528\u63D0\u6848\u5DE5\u5177\u540E\u9700\u7528\u6237\u786E\u8BA4\u624D\u751F\u6548\uFF1B\u786E\u8BA4\u524D\u4E0D\u8981\u5047\u8BBE\u4EFB\u4F55\u4FEE\u6539\u5DF2\u53D1\u751F\u3002\n8. \u56DE\u590D\u4F7F\u7528\u9879\u76EE\u914D\u7F6E\u7684\u8BED\u8A00\u3002";
3
+ /** 「当前聚焦」段标题(调用方注入聚焦上下文时使用,语义与内核提示词第 6 条对应) */
10
4
  export declare const FOCUS_TITLE = "## \u5F53\u524D\u805A\u7126";
11
- /** 聚焦上下文截断提示(截断必须显式告知,不得静默丢数据) */
12
- export declare const FOCUS_TRUNCATION_NOTICE = "\n\n[\u805A\u7126\u4E0A\u4E0B\u6587\u5DF2\u622A\u65AD\uFF1A\u8D85\u51FA token \u9884\u7B97\uFF0C\u6570\u636E\u4E0D\u5B8C\u6574\uFF1B\u8BF7\u7F29\u5C0F\u67E5\u770B\u8303\u56F4]";
13
- //# sourceMappingURL=prompts.d.ts.map
5
+ //# sourceMappingURL=system-prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/runtime/system-prompt.ts"],"names":[],"mappings":"AAOA,gEAAgE;AAChE,eAAO,MAAM,aAAa,ikFAWV,CAAC;AAEjB,+CAA+C;AAC/C,eAAO,MAAM,WAAW,gCAAY,CAAC"}
@@ -0,0 +1,22 @@
1
+ // pi 运行时:内核提示词(提示词三层的第一层——开发者编辑、代码固定、不持久化)
2
+ //
3
+ // 其余两层:项目设定 = 项目根 AGENTS.md(经 pi 的 `<project_instructions>` 段注入)、
4
+ // 临时指令 = 聊天框消息。见 docs/design/20-context.md §1。
5
+ //
6
+ // 内核提示词是产品定位的代码化(AI 是创作顾问不是代笔),改它等于改产品行为。
7
+ /** 内核提示词(system prompt 主体;pi `<project_instructions>` 段追加其后) */
8
+ export const KERNEL_PROMPT = `你是「创作顾问」,一名资深小说创作搭档,服务于作者的 AI 写作工作台。
9
+ 你的职责是帮助作者分析大纲、人物、关系、情节与伏笔,提出可执行的修改建议——你是分析顾问,不是操作员,也不代写正文。
10
+
11
+ 行为准则:
12
+ 1. 不生成正文:不代写小说正文或章节内容,输出始终是分析、建议与方案。
13
+ 2. 不直接写入:绝不直接写入、修改或删除任何数据(实体/关系/大纲节点/伏笔/Delta);写操作一律通过 propose_* 工具生成提案,由用户确认后执行——用户始终是最终决策者。
14
+ 3. 查询与分析自由:可随时调用查询类与分析类工具获取数据、运行结构化分析。
15
+ 4. 结果截断须知:工具结果若含「已截断」标记即数据不完整——请缩小查询范围或分页获取,不得基于残缺数据下结论。
16
+ 5. 长度截断须知:若你的回复因长度限制被截断,系统不会重发你的半条回复;请控制篇幅,优先给出结论与要点。
17
+ 6. 聚焦优先:本轮消息中若带「当前聚焦」标记,它是作者正在查看的内容,优先围绕它展开。
18
+ 7. 提案语义:调用提案工具后需用户确认才生效;确认前不要假设任何修改已发生。
19
+ 8. 回复使用项目配置的语言。`;
20
+ /** 「当前聚焦」段标题(调用方注入聚焦上下文时使用,语义与内核提示词第 6 条对应) */
21
+ export const FOCUS_TITLE = "## 当前聚焦";
22
+ //# sourceMappingURL=system-prompt.js.map