@cjhyy/code-shell-core 0.6.0-rc.9 → 0.7.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 (193) hide show
  1. package/THIRD_PARTY_NOTICES.md +206 -0
  2. package/dist/automation/scheduler.d.ts +13 -7
  3. package/dist/automation/scheduler.js +116 -37
  4. package/dist/capability-control/service.d.ts +2 -0
  5. package/dist/capability-control/service.js +4 -2
  6. package/dist/cc-orchestrator/agent-adapter.d.ts +4 -0
  7. package/dist/cc-orchestrator/agent-adapter.js +11 -1
  8. package/dist/cc-orchestrator/codex-session-history.d.ts +14 -1
  9. package/dist/cc-orchestrator/codex-session-history.js +65 -5
  10. package/dist/cc-orchestrator/cwd-normalize.d.ts +2 -0
  11. package/dist/cc-orchestrator/cwd-normalize.js +19 -0
  12. package/dist/cc-orchestrator/external-agent-bindings.d.ts +27 -0
  13. package/dist/cc-orchestrator/external-agent-bindings.js +150 -0
  14. package/dist/cc-orchestrator/external-agent-changes.js +22 -5
  15. package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -0
  16. package/dist/cc-orchestrator/external-agent-driver.js +265 -50
  17. package/dist/cc-orchestrator/external-agent-session-store.d.ts +23 -0
  18. package/dist/cc-orchestrator/external-agent-session-store.js +146 -0
  19. package/dist/cc-orchestrator/session-history.d.ts +35 -0
  20. package/dist/cc-orchestrator/session-history.js +98 -15
  21. package/dist/cli/agent-server-stdio.js +9 -2
  22. package/dist/context/compaction.d.ts +8 -1
  23. package/dist/context/compaction.js +49 -4
  24. package/dist/context/manager.d.ts +16 -2
  25. package/dist/context/manager.js +103 -19
  26. package/dist/credentials/access.d.ts +57 -0
  27. package/dist/credentials/access.js +185 -0
  28. package/dist/credentials/index.d.ts +3 -1
  29. package/dist/credentials/index.js +2 -0
  30. package/dist/credentials/inject-credential-tool.d.ts +3 -1
  31. package/dist/credentials/inject-credential-tool.js +30 -11
  32. package/dist/credentials/oauth.d.ts +20 -0
  33. package/dist/credentials/oauth.js +114 -0
  34. package/dist/credentials/store.d.ts +1 -0
  35. package/dist/credentials/store.js +3 -1
  36. package/dist/credentials/types.d.ts +49 -3
  37. package/dist/credentials/use-credential-tool.d.ts +9 -1
  38. package/dist/credentials/use-credential-tool.js +58 -45
  39. package/dist/engine/engine.d.ts +23 -3
  40. package/dist/engine/engine.js +380 -255
  41. package/dist/engine/goal.d.ts +17 -0
  42. package/dist/engine/goal.js +16 -6
  43. package/dist/engine/image-policy.d.ts +6 -0
  44. package/dist/engine/image-policy.js +17 -6
  45. package/dist/engine/input-attachments.d.ts +13 -0
  46. package/dist/engine/input-attachments.js +398 -0
  47. package/dist/engine/model-facade.d.ts +5 -2
  48. package/dist/engine/model-facade.js +4 -4
  49. package/dist/engine/parse-task.d.ts +10 -0
  50. package/dist/engine/parse-task.js +5 -0
  51. package/dist/engine/run-image-input.d.ts +22 -0
  52. package/dist/engine/run-image-input.js +195 -0
  53. package/dist/engine/steer-queue.d.ts +3 -1
  54. package/dist/engine/steer-queue.js +10 -2
  55. package/dist/engine/streaming-tool-queue.d.ts +11 -7
  56. package/dist/engine/streaming-tool-queue.js +11 -7
  57. package/dist/engine/turn-loop.d.ts +37 -2
  58. package/dist/engine/turn-loop.js +228 -43
  59. package/dist/engine/types.d.ts +8 -0
  60. package/dist/git/worktree/crud.d.ts +69 -0
  61. package/dist/git/worktree/crud.js +206 -0
  62. package/dist/git/worktree/diff.d.ts +14 -0
  63. package/dist/git/worktree/diff.js +82 -0
  64. package/dist/git/worktree/git-exec.d.ts +7 -0
  65. package/dist/git/worktree/git-exec.js +51 -0
  66. package/dist/git/worktree/index.d.ts +5 -0
  67. package/dist/git/worktree/index.js +5 -0
  68. package/dist/git/worktree/query.d.ts +42 -0
  69. package/dist/git/worktree/query.js +121 -0
  70. package/dist/git/worktree/slug.d.ts +11 -0
  71. package/dist/git/worktree/slug.js +58 -0
  72. package/dist/git/worktree.d.ts +1 -84
  73. package/dist/git/worktree.js +5 -230
  74. package/dist/hooks/goal-stop-hook.d.ts +33 -1
  75. package/dist/hooks/goal-stop-hook.js +202 -34
  76. package/dist/index.d.ts +31 -28
  77. package/dist/index.js +27 -25
  78. package/dist/logging/logger.js +6 -6
  79. package/dist/logging/sanitize-messages.d.ts +10 -2
  80. package/dist/logging/sanitize-messages.js +21 -6
  81. package/dist/plugins/installer/checkUpdate.d.ts +4 -1
  82. package/dist/plugins/installer/checkUpdate.js +4 -2
  83. package/dist/plugins/installer/install.js +2 -0
  84. package/dist/plugins/installer/installFromSource.js +9 -1
  85. package/dist/plugins/installer/parseSource.d.ts +4 -1
  86. package/dist/plugins/installer/parseSource.js +28 -10
  87. package/dist/plugins/installer/sourcePath.d.ts +9 -0
  88. package/dist/plugins/installer/sourcePath.js +50 -0
  89. package/dist/plugins/installer/update.d.ts +4 -1
  90. package/dist/plugins/installer/update.js +5 -3
  91. package/dist/plugins/parseMarketplaceInput.d.ts +4 -1
  92. package/dist/plugins/parseMarketplaceInput.js +4 -3
  93. package/dist/plugins/pluginInstaller.js +24 -22
  94. package/dist/preset/index.d.ts +1 -0
  95. package/dist/preset/index.js +30 -13
  96. package/dist/prompt/sections/base.md +1 -1
  97. package/dist/protocol/chat-session-manager.d.ts +2 -0
  98. package/dist/protocol/chat-session-manager.js +38 -2
  99. package/dist/protocol/chat-session.d.ts +3 -0
  100. package/dist/protocol/chat-session.js +1 -0
  101. package/dist/protocol/client.d.ts +9 -4
  102. package/dist/protocol/client.js +18 -1
  103. package/dist/protocol/server.d.ts +30 -0
  104. package/dist/protocol/server.js +247 -47
  105. package/dist/protocol/types.d.ts +49 -0
  106. package/dist/protocol/types.js +6 -0
  107. package/dist/run/FileRunStore.js +10 -1
  108. package/dist/run/Heartbeat.js +12 -0
  109. package/dist/run/RunApprovalBackend.d.ts +3 -0
  110. package/dist/run/RunApprovalBackend.js +41 -6
  111. package/dist/run/RunLock.js +2 -0
  112. package/dist/run/RunManager.d.ts +2 -0
  113. package/dist/run/RunManager.js +64 -24
  114. package/dist/run/ids.d.ts +2 -0
  115. package/dist/run/ids.js +23 -0
  116. package/dist/runtime/background-shell.d.ts +1 -0
  117. package/dist/runtime/background-shell.js +23 -13
  118. package/dist/runtime/spawn-common.js +10 -0
  119. package/dist/services/auto-dream.d.ts +15 -4
  120. package/dist/services/auto-dream.js +20 -20
  121. package/dist/services/dream-consolidation.d.ts +2 -3
  122. package/dist/services/dream-consolidation.js +65 -15
  123. package/dist/services/extract-memories.d.ts +14 -5
  124. package/dist/services/extract-memories.js +20 -3
  125. package/dist/services/global-dream-promotion.d.ts +23 -0
  126. package/dist/services/global-dream-promotion.js +112 -0
  127. package/dist/services/memory-orchestrator.js +347 -34
  128. package/dist/session/memory.d.ts +61 -18
  129. package/dist/session/memory.js +342 -79
  130. package/dist/session/session-manager.d.ts +35 -1
  131. package/dist/session/session-manager.js +224 -4
  132. package/dist/session/transcript.d.ts +1 -1
  133. package/dist/session/transcript.js +17 -5
  134. package/dist/settings/manager.d.ts +1 -0
  135. package/dist/settings/manager.js +87 -37
  136. package/dist/settings/schema-export.d.ts +2 -3
  137. package/dist/settings/schema-export.js +2 -3
  138. package/dist/settings/schema.d.ts +21 -0
  139. package/dist/settings/schema.js +12 -0
  140. package/dist/skills/scanner.d.ts +3 -2
  141. package/dist/skills/scanner.js +12 -9
  142. package/dist/tool-system/builtin/agent-notifications.d.ts +11 -4
  143. package/dist/tool-system/builtin/agent-notifications.js +19 -7
  144. package/dist/tool-system/builtin/background-jobs.d.ts +35 -6
  145. package/dist/tool-system/builtin/background-jobs.js +116 -7
  146. package/dist/tool-system/builtin/background-work.d.ts +17 -18
  147. package/dist/tool-system/builtin/background-work.js +51 -5
  148. package/dist/tool-system/builtin/config.d.ts +2 -1
  149. package/dist/tool-system/builtin/config.js +16 -11
  150. package/dist/tool-system/builtin/cron-list.definition.d.ts +3 -0
  151. package/dist/tool-system/builtin/cron-list.definition.js +6 -0
  152. package/dist/tool-system/builtin/cron.d.ts +1 -2
  153. package/dist/tool-system/builtin/cron.js +9 -7
  154. package/dist/tool-system/builtin/drive-claude-code.d.ts +23 -2
  155. package/dist/tool-system/builtin/drive-claude-code.js +589 -48
  156. package/dist/tool-system/builtin/edit.js +5 -2
  157. package/dist/tool-system/builtin/generate-video.d.ts +1 -0
  158. package/dist/tool-system/builtin/generate-video.js +13 -4
  159. package/dist/tool-system/builtin/index.d.ts +8 -3
  160. package/dist/tool-system/builtin/index.js +50 -22
  161. package/dist/tool-system/builtin/lsp.d.ts +2 -1
  162. package/dist/tool-system/builtin/lsp.js +6 -3
  163. package/dist/tool-system/builtin/memory.js +40 -8
  164. package/dist/tool-system/builtin/notebook-edit.js +5 -2
  165. package/dist/tool-system/builtin/powershell.d.ts +5 -2
  166. package/dist/tool-system/builtin/powershell.js +11 -7
  167. package/dist/tool-system/builtin/read.js +118 -6
  168. package/dist/tool-system/builtin/sleep.d.ts +1 -2
  169. package/dist/tool-system/builtin/sleep.definition.d.ts +8 -0
  170. package/dist/tool-system/builtin/sleep.definition.js +28 -0
  171. package/dist/tool-system/builtin/sleep.js +1 -22
  172. package/dist/tool-system/builtin/view-image.d.ts +2 -2
  173. package/dist/tool-system/builtin/view-image.js +109 -19
  174. package/dist/tool-system/builtin/worktree.d.ts +4 -4
  175. package/dist/tool-system/builtin/worktree.js +297 -74
  176. package/dist/tool-system/builtin/write.js +5 -3
  177. package/dist/tool-system/context.d.ts +41 -1
  178. package/dist/tool-system/executor.d.ts +1 -5
  179. package/dist/tool-system/executor.js +94 -115
  180. package/dist/tool-system/mcp-manager.d.ts +31 -6
  181. package/dist/tool-system/mcp-manager.js +193 -78
  182. package/dist/tool-system/path-policy.d.ts +2 -0
  183. package/dist/tool-system/path-policy.js +41 -12
  184. package/dist/tool-system/permission.d.ts +28 -7
  185. package/dist/tool-system/permission.js +130 -49
  186. package/dist/tool-system/registry.js +11 -4
  187. package/dist/tool-system/tool-result-redaction.d.ts +7 -0
  188. package/dist/tool-system/tool-result-redaction.js +48 -0
  189. package/dist/tool-system/workspace-bridge.d.ts +11 -0
  190. package/dist/tool-system/workspace-bridge.js +1 -0
  191. package/dist/types.d.ts +78 -11
  192. package/dist/utils/toolDisplay.js +1 -1
  193. package/package.json +4 -3
@@ -13,17 +13,17 @@
13
13
  * The loop is intentionally small and offline:
14
14
  * - No streaming, no UI events — it runs in the background.
15
15
  * - No permission prompts — there is no interactive backend on this path, so
16
- * we hard-reject any attempt to Save/Delete in the "user" scope before
17
- * dispatching. The "dream" scope is the LLM's workspace and goes through
18
- * freely.
16
+ * Save/Delete goes through an origin guard before dispatching. Dream may
17
+ * maintain origin:dream/auto entries but never touches origin:manual.
19
18
  * - Capped at MAX_TURNS LLM round-trips and MAX_WRITES total mutations to
20
19
  * bound damage on misbehavior.
21
20
  */
22
21
  import { MemoryManager } from "../session/memory.js";
23
- import { buildDreamSystemPrompt, buildDreamUserPrompt, } from "./auto-dream.js";
22
+ import { buildDreamSystemPrompt, buildDreamUserPrompt } from "./auto-dream.js";
24
23
  import { logger } from "../logging/logger.js";
25
24
  const MAX_TURNS = 8;
26
25
  const MAX_WRITES = 10;
26
+ const FRESH_ENTRY_GRACE_MS = 10 * 60 * 1000;
27
27
  const MEMORY_TOOL_NAMES = ["MemoryList", "MemoryRead", "MemorySave", "MemoryDelete"];
28
28
  /**
29
29
  * Drive the dream-scope consolidation tool-call loop.
@@ -37,9 +37,7 @@ const MEMORY_TOOL_NAMES = ["MemoryList", "MemoryRead", "MemorySave", "MemoryDele
37
37
  */
38
38
  export async function runDreamConsolidation(input) {
39
39
  const { llmClient, toolRegistry, projectDir, sessionId } = input;
40
- const memoryTools = MEMORY_TOOL_NAMES
41
- .map((n) => toolRegistry.getTool(n))
42
- .filter((t) => t != null);
40
+ const memoryTools = MEMORY_TOOL_NAMES.map((n) => toolRegistry.getTool(n)).filter((t) => t != null);
43
41
  if (memoryTools.length < MEMORY_TOOL_NAMES.length) {
44
42
  logger.warn("memory.dream_missing_tools", {
45
43
  sessionId,
@@ -52,8 +50,9 @@ export async function runDreamConsolidation(input) {
52
50
  const mm = new MemoryManager({ projectDir });
53
51
  const userMems = mm.loadScope("user");
54
52
  const dreamMems = mm.loadScope("dream");
53
+ const globalDreamMems = projectDir ? new MemoryManager({ scope: "dream" }).loadAll() : [];
55
54
  const systemPrompt = buildDreamSystemPrompt();
56
- const userPrompt = buildDreamUserPrompt(userMems, dreamMems);
55
+ const userPrompt = buildDreamUserPrompt(userMems, dreamMems, globalDreamMems);
57
56
  // Strip RegisteredTool down to the shape createMessage expects.
58
57
  const toolDefs = memoryTools.map((t) => ({
59
58
  name: t.name,
@@ -120,8 +119,8 @@ export async function runDreamConsolidation(input) {
120
119
  * Execute one memory tool call inside the dream loop. Enforces the two
121
120
  * dream-loop invariants the prompt also states:
122
121
  * - Only the 4 memory tools are dispatchable.
123
- * - Save/Delete in "user" scope is refused (returned as a tool error)
124
- * because dream runs without an interactive permission backend.
122
+ * - Save/Delete is allowed only for origin:auto/origin:dream owned entries.
123
+ * Missing origin is manual, and manual is always protected.
125
124
  */
126
125
  async function dispatchDreamTool(tc, toolRegistry, ctx, consumeWriteBudget) {
127
126
  const allowed = new Set(MEMORY_TOOL_NAMES);
@@ -130,17 +129,17 @@ async function dispatchDreamTool(tc, toolRegistry, ctx, consumeWriteBudget) {
130
129
  }
131
130
  const isWrite = tc.toolName === "MemorySave" || tc.toolName === "MemoryDelete";
132
131
  if (isWrite) {
133
- const scope = tc.args?.scope;
134
- if (scope !== "dream") {
135
- return (`Error: dream loop may only write to scope "dream", got "${scope}". ` +
136
- `User-scope changes require interactive permission, which is not available here.`);
132
+ const guard = checkDreamWriteGuard(tc, ctx);
133
+ if (!guard.ok) {
134
+ return guard.error;
137
135
  }
138
136
  if (!consumeWriteBudget()) {
139
137
  return "Error: dream write budget exhausted — stop calling write tools and summarize instead.";
140
138
  }
141
139
  }
142
140
  try {
143
- const result = await toolRegistry.executeTool(tc.toolName, tc.args, { ctx });
141
+ const dreamCtx = { ...ctx, __dreamLoop: true };
142
+ const result = await toolRegistry.executeTool(tc.toolName, tc.args, { ctx: dreamCtx });
144
143
  if (result.isError)
145
144
  return result.error ?? `Error executing ${tc.toolName}`;
146
145
  return result.result ?? "";
@@ -149,3 +148,54 @@ async function dispatchDreamTool(tc, toolRegistry, ctx, consumeWriteBudget) {
149
148
  return `Error executing ${tc.toolName}: ${err.message}`;
150
149
  }
151
150
  }
151
+ function checkDreamWriteGuard(tc, ctx) {
152
+ const scope = tc.args?.scope;
153
+ if (scope !== "dream" && scope !== "user") {
154
+ return {
155
+ ok: false,
156
+ error: `Error: dream loop may only write to scope "dream" or "user", got "${scope}".`,
157
+ };
158
+ }
159
+ const location = tc.args?.location === "global" ? "global" : "project";
160
+ const mm = new MemoryManager({
161
+ projectDir: location === "project" ? ctx.cwd : undefined,
162
+ scope,
163
+ });
164
+ const id = typeof tc.args?.id === "string" ? tc.args.id : undefined;
165
+ const name = typeof tc.args?.name === "string" ? tc.args.name : undefined;
166
+ const targetById = id ? mm.findById(id) : undefined;
167
+ const targetByName = name ? mm.find(name) : undefined;
168
+ const targets = [targetById, targetByName].filter((target, index, all) => Boolean(target) && all.findIndex((item) => item?.id === target?.id) === index);
169
+ const protectedTarget = targets.find((target) => target.origin === "manual" || !target.origin);
170
+ if (protectedTarget) {
171
+ return {
172
+ ok: false,
173
+ error: `Error: dream loop cannot modify origin:manual memory "${protectedTarget.name}" ` +
174
+ `(${location}/${scope}/${protectedTarget.id ?? protectedTarget.fileName}).`,
175
+ };
176
+ }
177
+ const target = targetById ?? targetByName;
178
+ if (!target)
179
+ return { ok: true };
180
+ if (target.origin !== "auto" && target.origin !== "dream") {
181
+ return {
182
+ ok: false,
183
+ error: `Error: dream loop can only modify origin:auto or origin:dream memories; ` +
184
+ `"${target.name}" has origin:${target.origin}.`,
185
+ };
186
+ }
187
+ if (tc.toolName === "MemoryDelete" && isFreshEntry(target.createdAt)) {
188
+ return {
189
+ ok: false,
190
+ error: `Error: dream loop cannot delete freshly-created memory "${target.name}" yet; ` +
191
+ "leave it for the next consolidation pass.",
192
+ };
193
+ }
194
+ return { ok: true };
195
+ }
196
+ function isFreshEntry(createdAt) {
197
+ if (!createdAt)
198
+ return false;
199
+ const createdMs = new Date(createdAt).getTime();
200
+ return Number.isFinite(createdMs) && Date.now() - createdMs < FRESH_ENTRY_GRACE_MS;
201
+ }
@@ -18,17 +18,26 @@ export interface ExtractedMemory {
18
18
  description: string;
19
19
  content: string;
20
20
  }
21
+ export interface ExistingMemorySummary {
22
+ id?: string;
23
+ name: string;
24
+ type: string;
25
+ description: string;
26
+ location?: "project" | "global";
27
+ memoryScope?: "user" | "dream";
28
+ origin?: "manual" | "auto" | "dream";
29
+ pinned?: boolean;
30
+ useCount?: number;
31
+ updateCount?: number;
32
+ updatedAt?: string;
33
+ }
21
34
  /**
22
35
  * Build the prompt for extracting memories from a conversation.
23
36
  */
24
37
  export declare function buildExtractionPrompt(transcript: Array<{
25
38
  role: string;
26
39
  content: string;
27
- }>, existingMemories: Array<{
28
- name: string;
29
- type: string;
30
- description: string;
31
- }>): string;
40
+ }>, existingMemories: ExistingMemorySummary[]): string;
32
41
  /** Max memories to accept from a single extraction pass. Code-side cap that
33
42
  * enforces the same limit the prompt asks for — the model occasionally
34
43
  * ignores prompt rules, but this guarantees the cap. */
@@ -12,7 +12,21 @@ export function buildExtractionPrompt(transcript, existingMemories) {
12
12
  .map((m) => `[${m.role}]: ${m.content.slice(0, 3000)}`)
13
13
  .join("\n\n");
14
14
  const existingList = existingMemories.length > 0
15
- ? existingMemories.map((m) => ` - [${m.type}] ${m.name}: ${m.description}`).join("\n")
15
+ ? existingMemories
16
+ .map((m) => {
17
+ const meta = [
18
+ m.id ? `id:${m.id}` : null,
19
+ m.location ? `location:${m.location}` : null,
20
+ m.memoryScope ? `scope:${m.memoryScope}` : null,
21
+ m.origin ? `origin:${m.origin}` : null,
22
+ typeof m.useCount === "number" ? `use:${m.useCount}` : null,
23
+ typeof m.updateCount === "number" ? `updates:${m.updateCount}` : null,
24
+ ]
25
+ .filter(Boolean)
26
+ .join(", ");
27
+ return ` - [${m.type}] ${m.name}${meta ? ` (${meta})` : ""}: ${m.description}`;
28
+ })
29
+ .join("\n")
16
30
  : " (none)";
17
31
  return `Analyze the following conversation and extract any information worth remembering for future sessions.
18
32
 
@@ -23,7 +37,7 @@ ${existingList}
23
37
  ${conversationText.slice(0, 30000)}
24
38
 
25
39
  ## Instructions
26
- Identify NEW information that should be saved as persistent memories. Categories (type):
40
+ Identify candidate information that may be saved as persistent memories. The extractor only proposes candidates; code will route automatic candidates into dream memory first, never directly into user memory. Categories (type):
27
41
  - **user**: Information about the user's role, preferences, or expertise
28
42
  - **feedback**: Guidance about how to approach work (corrections or confirmations)
29
43
  - **project**: Non-obvious facts about ongoing work, goals, or decisions
@@ -39,7 +53,10 @@ Rules:
39
53
  - Extract AT MOST 2 memories per session. Prefer 0 to a marginal one — most sessions are noise.
40
54
  - AT MOST 1 of the extracted memories may be "global". The global layer is injected every session, so keep it tiny and high-value. If two candidates both seem global, keep the single most universal one global and make the other "project".
41
55
  - Only extract information that would be useful in FUTURE conversations
42
- - Do not duplicate existing memories (re-read the "Existing Memories" list above carefully)
56
+ - Do not duplicate existing memories (re-read the "Existing Memories" list above carefully).
57
+ - If an existing auto/dream memory has the same durable topic, reuse that topic in the candidate instead of creating a date-stamped variant; the write decision layer will update the existing id.
58
+ - Manual user memories are read-only curated memories. Do not re-extract the same topic just because the wording or date differs.
59
+ - Avoid dates, version stamps, batch names, and progress-snapshot suffixes in names. Names should be stable topic identifiers, not "today" variants.
43
60
  - Do not extract code patterns, file structures, or git history (derivable from code)
44
61
  - Do not extract ephemeral task details, progress snapshots, or in-flight work state — those belong in the conversation, not memory
45
62
  - Do not extract one-off research products (news reports, AI industry summaries, slide deck content, daily progress dumps) — they're done and not "durable, reusable information"
@@ -0,0 +1,23 @@
1
+ import type { ExtractedMemory } from "./extract-memories.js";
2
+ export interface GlobalDreamPromotionInput {
3
+ candidate: ExtractedMemory;
4
+ projectDir?: string;
5
+ baseDir?: string;
6
+ userDirectGlobal?: boolean;
7
+ now?: Date;
8
+ }
9
+ export interface GlobalDreamPromotionResult {
10
+ /** No automatic write to global dream is performed; approval is required. */
11
+ promoted: false;
12
+ /** True when this call created a new pending approval item. */
13
+ pendingSuggested: boolean;
14
+ originProjects: string[];
15
+ evidenceCount: number;
16
+ projectEvidenceSaved: boolean;
17
+ promotionReason: string;
18
+ }
19
+ export declare function detectUserDirectGlobalPreference(transcript: Array<{
20
+ role: string;
21
+ content: string;
22
+ }>): boolean;
23
+ export declare function applyGlobalDreamPromotionGate(input: GlobalDreamPromotionInput): GlobalDreamPromotionResult;
@@ -0,0 +1,112 @@
1
+ import { MemoryManager, resolveMemoryBaseDir } from "../session/memory.js";
2
+ export function detectUserDirectGlobalPreference(transcript) {
3
+ const userText = transcript
4
+ .filter((m) => m.role === "user")
5
+ .map((m) => m.content)
6
+ .join("\n")
7
+ .toLowerCase();
8
+ return (/(?:全局记住|全局保存|所有项目|全部项目|以后所有项目|今后所有项目|以后都|以后默认|我偏好|我的偏好)/u.test(userText) ||
9
+ /(?:remember globally|global memory|all projects|every project|my preference|i prefer|always remember)/i.test(userText));
10
+ }
11
+ export function applyGlobalDreamPromotionGate(input) {
12
+ const baseDir = resolveMemoryBaseDir(input.baseDir);
13
+ const now = input.now ?? new Date();
14
+ const nowIso = now.toISOString();
15
+ const promotionReason = input.userDirectGlobal
16
+ ? "user-direct global preference suggested global dream approval"
17
+ : "candidate suggested global dream approval";
18
+ const projectEvidence = input.projectDir
19
+ ? saveProjectDreamCandidate({
20
+ baseDir,
21
+ projectDir: input.projectDir,
22
+ candidate: input.candidate,
23
+ nowIso,
24
+ promotionReason,
25
+ })
26
+ : undefined;
27
+ const originProjects = projectEvidence?.originProjects ?? (input.projectDir ? [input.projectDir] : []);
28
+ const rejected = projectEvidence?.promotionStatus === "rejected";
29
+ const pendingSuggested = !rejected &&
30
+ savePendingGlobalSuggestion({
31
+ baseDir,
32
+ projectDir: input.projectDir,
33
+ candidate: input.candidate,
34
+ source: projectEvidence,
35
+ originProjects,
36
+ nowIso,
37
+ promotionReason,
38
+ });
39
+ return {
40
+ promoted: false,
41
+ pendingSuggested,
42
+ originProjects,
43
+ evidenceCount: originProjects.length,
44
+ projectEvidenceSaved: Boolean(projectEvidence),
45
+ promotionReason,
46
+ };
47
+ }
48
+ function saveProjectDreamCandidate(input) {
49
+ const mm = new MemoryManager({
50
+ baseDir: input.baseDir,
51
+ projectDir: input.projectDir,
52
+ scope: "dream",
53
+ });
54
+ const existing = mm
55
+ .loadAll()
56
+ .find((entry) => entry.name === input.candidate.name &&
57
+ (entry.origin === "auto" || entry.origin === "dream"));
58
+ const originProjects = unique([...(existing?.originProjects ?? []), input.projectDir]);
59
+ const promotionStatus = existing?.promotionStatus === "rejected" ? "rejected" : "pending";
60
+ const fileName = mm.save({
61
+ id: existing?.id,
62
+ name: input.candidate.name,
63
+ description: input.candidate.description,
64
+ type: input.candidate.type,
65
+ content: input.candidate.content,
66
+ origin: existing?.origin ?? "auto",
67
+ pinned: existing?.pinned,
68
+ createdAt: existing?.createdAt,
69
+ useCount: existing?.useCount,
70
+ updateCount: existing?.updateCount,
71
+ lastUsedAt: existing?.lastUsedAt,
72
+ originProject: input.projectDir,
73
+ originProjects,
74
+ evidenceCount: originProjects.length,
75
+ firstSeenAt: existing?.firstSeenAt ?? existing?.createdAt ?? input.nowIso,
76
+ lastSeenAt: input.nowIso,
77
+ promotionReason: input.promotionReason,
78
+ promotionStatus,
79
+ }, { forceOrigin: existing?.origin ?? "auto" });
80
+ return mm.find(fileName);
81
+ }
82
+ function savePendingGlobalSuggestion(input) {
83
+ const globalDream = new MemoryManager({ baseDir: input.baseDir, scope: "dream" });
84
+ if (globalDream.loadAll().some((entry) => entry.name === input.candidate.name))
85
+ return false;
86
+ const pending = new MemoryManager({ baseDir: input.baseDir, scope: "pending" });
87
+ const duplicate = pending
88
+ .loadAll()
89
+ .some((entry) => entry.name === input.candidate.name &&
90
+ (entry.originProject ?? "") === (input.projectDir ?? ""));
91
+ if (duplicate)
92
+ return false;
93
+ pending.save({
94
+ name: input.candidate.name,
95
+ description: input.candidate.description,
96
+ type: input.candidate.type,
97
+ content: input.candidate.content,
98
+ origin: "dream",
99
+ originProject: input.projectDir,
100
+ originProjects: input.originProjects,
101
+ evidenceCount: input.originProjects.length,
102
+ firstSeenAt: input.source?.firstSeenAt ?? input.source?.createdAt ?? input.nowIso,
103
+ lastSeenAt: input.nowIso,
104
+ promotionReason: input.promotionReason,
105
+ promotionStatus: "pending",
106
+ promotionSourceId: input.source?.id,
107
+ }, { forceOrigin: "dream" });
108
+ return true;
109
+ }
110
+ function unique(values) {
111
+ return [...new Set(values.filter((value) => Boolean(value)))];
112
+ }