@orion-agents/orion-code 0.3.4 → 0.3.7

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 (86) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +61 -5
  3. package/README.zh-CN.md +7 -3
  4. package/dist/commands/context-tool-command-handlers.js +1 -5
  5. package/dist/commands/registry.js +1 -4
  6. package/dist/harness/intent.js +69 -16
  7. package/dist/harness/turn-summary.js +3 -1
  8. package/dist/memory/embeddings.js +2 -3
  9. package/dist/memory/prompts.js +1 -1
  10. package/dist/memory/relevant-finder.js +125 -15
  11. package/dist/memory/vector-store.js +13 -8
  12. package/dist/migration/migrate.js +8 -12
  13. package/dist/runtime/composer/history.js +1 -3
  14. package/dist/runtime/composer/layout.js +7 -4
  15. package/dist/runtime/legacy-thread-materializer.d.ts +3 -1
  16. package/dist/runtime/legacy-thread-materializer.js +15 -3
  17. package/dist/runtime/product-bootstrap.d.ts +12 -0
  18. package/dist/runtime/product-bootstrap.js +37 -22
  19. package/dist/runtime/rich-text/ansi-parser.js +12 -2
  20. package/dist/runtime/rich-text/layout.js +15 -19
  21. package/dist/runtime/subagents/budget.js +4 -1
  22. package/dist/runtime/subagents/policy.js +11 -4
  23. package/dist/runtime/subagents/provider-gate.js +6 -4
  24. package/dist/runtime/subagents/research-quality.js +3 -1
  25. package/dist/runtime/subagents/research-renderer.js +9 -2
  26. package/dist/runtime/subagents/result-parser.js +18 -7
  27. package/dist/runtime/thread-event-store.d.ts +5 -3
  28. package/dist/runtime/thread-event-store.js +29 -8
  29. package/dist/runtime/thread-projection.d.ts +17 -0
  30. package/dist/runtime/thread-projection.js +42 -1
  31. package/dist/runtime/tool-output-presentation.js +7 -7
  32. package/dist/runtime/turn-controller.js +1 -2
  33. package/dist/services/auth/auth.js +1 -4
  34. package/dist/services/auth/aws.js +24 -14
  35. package/dist/services/auto-fix/autoFixRunner.js +1 -1
  36. package/dist/services/auto-fix/index.d.ts +1 -1
  37. package/dist/services/diagnostic-tracking/diagnosticTracking.js +4 -2
  38. package/dist/services/diagnostic-tracking/index.d.ts +1 -1
  39. package/dist/services/effort.js +1 -9
  40. package/dist/services/file-glob.js +3 -4
  41. package/dist/services/mcp/transports.js +4 -4
  42. package/dist/services/model-catalog.js +179 -30
  43. package/dist/services/model-context.js +1 -1
  44. package/dist/services/provider-resilience/stream-recovery.js +1 -3
  45. package/dist/services/session-storage.d.ts +28 -3
  46. package/dist/services/session-storage.js +70 -6
  47. package/dist/services/smart-routing.js +19 -11
  48. package/dist/services/verification-profile.js +17 -22
  49. package/dist/services/web-search-provider.js +21 -9
  50. package/dist/services/workspace-diff.js +10 -4
  51. package/dist/skills/builtin/code-review/SKILL.md +1 -1
  52. package/dist/skills/builtin/security-check/SKILL.md +1 -1
  53. package/dist/skills/builtin/test-gen/SKILL.md +1 -1
  54. package/dist/terminal-ui/editor.js +6 -2
  55. package/dist/terminal-ui/output-queue.js +9 -3
  56. package/dist/tools/lsp.js +12 -6
  57. package/dist/tui-core/frame.js +9 -7
  58. package/dist/tui-ui/transcript-cache.js +6 -6
  59. package/dist/tui-ui/transcript-inspector-surface.js +4 -1
  60. package/dist/tui-ui/transcript-inspector.js +3 -8
  61. package/dist/ui/box.js +14 -7
  62. package/dist/ui/markdown.js +4 -2
  63. package/dist/ui/shared/input-frame.js +12 -8
  64. package/dist/ui/stream-markdown.js +1 -1
  65. package/dist/ui/tool-preview.js +11 -3
  66. package/dist/ui/user-input.js +16 -10
  67. package/dist/web/protocol.d.ts +2 -0
  68. package/dist/web/protocol.js +1 -0
  69. package/dist/web/server.js +69 -0
  70. package/dist/web/session-runtime-registry.d.ts +14 -0
  71. package/dist/web/session-runtime-registry.js +23 -0
  72. package/dist/web/workbench-controller.d.ts +55 -0
  73. package/dist/web/workbench-controller.js +224 -14
  74. package/dist/web-client/assets/{DiffViewer-D6mG729y.js → DiffViewer-DqbZ02RN.js} +1 -1
  75. package/dist/web-client/assets/{FilesPanel-CCDoAZ1J.js → FilesPanel-Bu1Ab2kJ.js} +1 -1
  76. package/dist/web-client/assets/{GitPanel-DtNgiH7A.js → GitPanel-BixIWb4k.js} +1 -1
  77. package/dist/web-client/assets/ReviewPanel-DsqJpku_.js +1 -0
  78. package/dist/web-client/assets/{TerminalPanel-CNxMaBLp.js → TerminalPanel-DdjQP2mp.js} +1 -1
  79. package/dist/web-client/assets/index-30GT9Vnx.js +17 -0
  80. package/dist/web-client/assets/index-DQfpqPTD.css +1 -0
  81. package/dist/web-client/index.html +2 -2
  82. package/npm-shrinkwrap.json +356 -120
  83. package/package.json +3 -1
  84. package/dist/web-client/assets/ReviewPanel-CKEBSt6P.js +0 -1
  85. package/dist/web-client/assets/index-BBvNg3By.css +0 -1
  86. package/dist/web-client/assets/index-C6TDJwMC.js +0 -17
package/CHANGELOG.md CHANGED
@@ -22,6 +22,130 @@ which is **not** a pass.
22
22
 
23
23
  ## [Unreleased]
24
24
 
25
+ ## [0.3.7] — CANDIDATE
26
+
27
+ > **Status: candidate.** This work is isolated on the codex/v0.3.7 worktree (left project
28
+ > rail interaction quality). It is not merged, tagged or published to npm.
29
+
30
+ ### Added
31
+
32
+ - Session **tags**: up to 8 user-assigned tags per Session (each ≤ 32 chars), persisted on
33
+ the Session record (`SessionMeta.tags`), projected into `WebSessionSummaryV1.tags`,
34
+ managed from a new "管理标签…" dialog (chip add via Enter/comma, per-chip remove) and
35
+ rendered as small badges on rail rows. Host endpoint `POST /sessions/:id/tags`.
36
+ - Session **archive / restore**: soft delete via `SessionMeta.archivedAt` that keeps every
37
+ file, hides the Session from default listings and counts, and surfaces it under a new
38
+ "已归档" rail section with a 还原 action (`POST /sessions/:id/archive|restore`;
39
+ `GET /workspaces/:id/sessions/archived`).
40
+ - Session **delete** from the web workbench: destructive confirmation dialog (explicit
41
+ "永久删除" guard, 不可恢复 copy) wired to the existing storage `deleteSession` through a
42
+ new `DELETE /sessions/:id` endpoint; refuse-while-busy checks in the controller.
43
+ - The rail rename affordance becomes a general **overflow menu** (`SessionRowMenu`,
44
+ keyboard navigable ↑↓/Home/End/Esc with focus restore) hosting 重命名… / 管理标签… /
45
+ 归档 / 删除… so future per-Session actions have a stable home.
46
+
47
+ ### Changed
48
+
49
+ - Active-workspace session listings (`listProjectSessions`, `listSessions`,
50
+ `countSessionsByProject`) exclude archived Sessions by default.
51
+ - Rail footer status copy is shortened to 已连接 / 离线 / … with full sentences moved to a
52
+ tooltip (`connectionTitle`); the collapsed rail dot keeps an accessible short label.
53
+ - Project tag chips, archived-section rows and the two new dialogs ship with their own
54
+ styles; stale `.session-rename`/`.session-row` rules were removed.
55
+
56
+ ### Fixed
57
+
58
+ - Rename failures are no longer silent: `RenameDialog` surfaces the server error inline
59
+ (`field-error`) instead of closing without feedback.
60
+ - Session-row menu hint ids are instance-unique (`useId`) so `aria-describedby` can never
61
+ point at another row's hint.
62
+ - The archived rail listing is bound to its owning Workspace: switching projects drops stale
63
+ foreign archive rows immediately and never flashes another project's archive while loading;
64
+ `session_deleted` also clears the stale runtime projection.
65
+
66
+ ### Tests
67
+
68
+ - New contract suites: `web-reducer-session-lifecycle` (14, incl. owner-binding + runtime
69
+ cleanup regression), `web-ui-SessionDialogs` (7), `web-ui-SessionRowMenu` (3),
70
+ `web-ui-WorkspaceDialog` (6), `session-storage-tags-archive` (5, incl. delete),
71
+ plus controller-level guards (tags validation / archive-restore / delete-while-busy /
72
+ foreign-workspace rejection / 404) in `web-workbench-controller.test.ts`; `web-ui-*` now
73
+ 112 cases green. E2E: `web-v037-session-lifecycle.spec.ts` (archive/restore, delete-confirm,
74
+ tags round-trip) runs in CI.
75
+
76
+
77
+ ## [0.3.6] — CANDIDATE
78
+
79
+ > **Status: candidate.** This work is isolated on the codex/v0.3.6 worktree (pure web
80
+ > workbench interaction quality). It is not merged, tagged or published to npm.
81
+
82
+ ### Added
83
+
84
+ - Component rendering contract layer for the web workbench: six `.test.tsx` suites
85
+ (89 cases) assert on `react-dom/server.renderToStaticMarkup` output without jsdom —
86
+ Markdown / StateDot / ShortcutHelp / DiffViewer / ApprovalCard / PanelResize.
87
+ - `StateDot` status indicator that never relies on colour alone (WCAG 1.4.1): every dot
88
+ carries a shape channel via `data-tone` (square / circle / pulsing ring / hollow ring /
89
+ triangle) plus a visually-hidden text label for assistive tech; applied to reasoning,
90
+ research sources, review evidence and the work-panel rail.
91
+ - Single-source keyboard binding table (`shortcuts.ts`) plus a `⌘/` shortcut help dialog
92
+ rendered from it, so the panel cannot drift from the live bindings; `matchesShortcut`
93
+ now resolves `Esc` → `Escape` so the help vocabulary matches real key events.
94
+ - Header theme-cycle button (system → light → dark, persisted through the settings
95
+ document); the `SettingsDialog` entry stays intact.
96
+ - Stacked notification toasts (P1-C): multiple notices queue without overwriting, each
97
+ non-recoverable non-error notice auto-dismisses after 5s, error / recovery notices stay
98
+ until acted on, and `aria-live` is graded (alert for errors, status otherwise).
99
+
100
+ ### Changed
101
+
102
+ - `PanelResizeHandle` is keyboard reachable with full separator semantics: `role=separator`
103
+ with live `aria-valuenow`/`aria-valuetext`, ←/→ steps of 2% (Shift 10%), Home/End jump,
104
+ Enter/Space reset to default; stepping logic is extracted as pure functions.
105
+ - Notification region becomes a stacked container (`.workbench-notice-stack`); cards slide
106
+ in on `opacity`/`transform` only and the whole stack is inert while drawers are open.
107
+ - Icon set gains `sun` / `moon` / `monitor` / `keyboard` glyphs.
108
+
109
+ ### Fixed
110
+
111
+ - Markdown heading off-by-one: `##` renders as `<h2>` (was `<h3>`), a lone `#` no longer
112
+ collapses to `<h6>`; >6 hashes stay a plain paragraph per GFM.
113
+
114
+
115
+ ## [0.3.5] — CANDIDATE
116
+
117
+ > **Status: candidate.** This work is isolated on the codex/v0.3.5 worktree. It is not
118
+ > merged, tagged or published to npm, and exact-artifact qualification remains a separate gate.
119
+
120
+ ### Added
121
+
122
+ - Version thread projection digests so v0.3.2/v0.3.3 cutover receipts keep verifying while new
123
+ projections carry the full diagnostic/compact shape; forged digests fail closed.
124
+ - Session snapshot sync state is decoupled from the Host transport: a Session snapshot failure
125
+ can no longer flip the live SSE to `replay-required`; Composer and command gates require both
126
+ transport live and the foreground Session snapshot ready.
127
+ - Host and browser diagnostics counters for Session switches, snapshot cache hits/loads/latency,
128
+ and Session actor allocation/eviction/closure, so "pure selection allocates no Runtime" is
129
+ provable from counters instead of prose.
130
+ - Runtime ownership across Workspace Context switches: the Host-level Session registry is created
131
+ once and preserved; control planes and Session actors borrow per-Workspace shared kernels, and
132
+ only Host shutdown closes every actor and kernel.
133
+
134
+ ### Changed
135
+
136
+ - Instant Session switching renders a cached projection immediately, refreshes with a bounded
137
+ tail snapshot in the background, and prefetches the most recent Sessions after a baseline.
138
+ - Version and release hygiene: package identity moves to 0.3.5, `release:check` recognizes
139
+ `codex/vX.Y.Z` and `GITHUB_HEAD_REF` release branches, and the Web rail no longer falls back to
140
+ a hardcoded version string.
141
+
142
+ ### Fixed
143
+
144
+ - Workspace Context activation no longer shuts down resident Session actors of other Workspaces
145
+ or requires every actor to be idle before switching the active control plane.
146
+ - A failed Context activation restores the previous control plane without rebuilding the Session
147
+ registry, so resident actors and their Runtime revisions survive the rollback.
148
+
25
149
  ## [0.3.4] — CANDIDATE
26
150
 
27
151
  > **Status: candidate.** This stabilization work is isolated on the v0.3.4 worktree. It is not
package/README.md CHANGED
@@ -2,11 +2,17 @@
2
2
 
3
3
  Local-first, goal-driven coding agent for the terminal and browser.
4
4
 
5
- > v0.3.4 candidate — a compatible security, terminal-ordering and release-evidence stabilization of
6
- > the v0.3.3 multi-Session Web Workbench. Candidate source is not an npm publication, Git tag, or
7
- > merge-ready release.
5
+ > v0.3.7 candidate — a left project-rail interaction release on top of the v0.3.6 work:
6
+ > shortened connection copy, a keyboard-reachable per-Session overflow menu
7
+ > (rename / tags / archive / delete with confirmation), persisted Session tags with
8
+ > rail badges, an archived-Session section with restore, and a friendlier
9
+ > "打开或新增项目" workspace dialog. Candidate source is not an npm publication,
10
+ > Git tag, or merge-ready release.
8
11
 
9
12
  [中文说明](README.zh-CN.md) ·
13
+ [v0.3.7 plan](docs/plan/v0.3.7-left-rail-improvement-plan.md) ·
14
+ [v0.3.6 plan](docs/plan/v0.3.6-plan.md) ·
15
+ [v0.3.5 plan](docs/plan/v0.3.5-plan.md) ·
10
16
  [v0.3.4 stabilization plan](docs/plan/v0.3.4-stabilization-plan.md) ·
11
17
  [v0.3.4 contract](docs/architecture/v0.3.4-stabilization-contract.md) ·
12
18
  [v0.3.4 E2E plan](docs/test/v0.3.4-stabilization-e2e-plan.md) ·
@@ -24,6 +30,56 @@ Local-first, goal-driven coding agent for the terminal and browser.
24
30
  [Settings migration](docs/migration/v0.2.2-to-v0.3.0-settings.md) ·
25
31
  [real-state gallery](docs/assets/screenshots/v0.3.0-web/README.md)
26
32
 
33
+ ## What v0.3.7 changes
34
+
35
+ - **Left rail status copy** is shortened (已连接 / 离线 / …) with full sentences on hover.
36
+ - **Per-Session overflow menu** replaces the rename icon — 重命名… / 管理标签… / 归档 /
37
+ 删除…, fully keyboard navigable, deleting requires an explicit confirmation dialog.
38
+ - **Session tags**: up to 8 persisted tags per Session, edited as chips and shown as
39
+ badges on rows; archive hides a Session (all files kept) into a rail 已归档 section
40
+ with one-click restore.
41
+ - **Workspace dialog** rework: "打开或新增项目" with suggested workspaces, a collapsible
42
+ "其他工作区" list and busy/validation feedback.
43
+
44
+ ## What v0.3.6 changes
45
+
46
+ - **A component-rendering contract layer.** Six `.test.tsx` suites (89 cases) assert the web
47
+ workbench UI through `renderToStaticMarkup` — no jsdom needed — pinning Markdown tables and
48
+ headings, status dots, the shortcut help dialog, the diff viewer, the permission card and the
49
+ resize separator keyboard contract.
50
+ - **Keyboard-reachable resize separators.** Project navigation and work-panel splitters expose
51
+ full separator semantics (`aria-valuenow`/`aria-valuetext`) and answer ←/→ (±2%, Shift ±10%),
52
+ Home/End and Enter/Space from the keyboard.
53
+ - **Status without colour alone.** A shared `StateDot` adds a shape channel (`data-tone`) and a
54
+ screen-reader label to every status dot — reasoning, research sources, review evidence and the
55
+ work-panel rail.
56
+ - **Single-source shortcut help.** `⌘/` opens a help dialog rendered from the same binding table
57
+ the live handlers match against.
58
+ - **Theme cycle in the header.** One click walks system → light → dark and persists through the
59
+ settings document; light tokens stay consolidated in the blocksmith token sheet.
60
+ - **Stacked notifications.** Toasts queue instead of overwriting, auto-dismiss after 5s unless
61
+ error/recovery, and grade `aria-live` by severity.
62
+
63
+ ## What v0.3.5 changes
64
+
65
+ - **Session snapshot state is its own machine.** A failed HTTP snapshot marks only the foreground
66
+ Session `failed` and never flips the live SSE into `replay-required`; Composer and command gates
67
+ require both a live transport and a ready foreground Session snapshot.
68
+ - **Instant Session switching.** A cached projection renders synchronously, a bounded tail snapshot
69
+ refreshes it in the background, the most recent Sessions are prefetched after a baseline, and stale
70
+ responses can never overwrite the browser-local foreground.
71
+ - **Runtime ownership across Workspaces.** The Host-level Session actor registry is created once per
72
+ Host. A Workspace Context switch swaps only the active control plane; Session actors of other
73
+ Workspaces keep running on the Workspace kernel they were created with, and only Host shutdown
74
+ closes every actor and kernel. Failed switches roll back without rebuilding the registry.
75
+ - **Provable runtime ownership.** Host and browser diagnostics count Session snapshot requests,
76
+ latency and failures, Session switches and cache hits, and actor allocation/eviction/closure — so a
77
+ pure selection provably allocates zero Session Runtimes.
78
+ - **Release hygiene.** Version identity moves to 0.3.5; `release:check` understands `codex/vX.Y.Z`
79
+ and `GITHUB_HEAD_REF`; the Web rail no longer hardcodes a fallback version.
80
+
81
+ This branch also carries the merged [v0.3.4 stabilization](#what-v034-stabilizes) below.
82
+
27
83
  ## What v0.3.4 stabilizes
28
84
 
29
85
  - **Context-bound writes.** Settings mutations now carry the active Workspace and Context revision;
@@ -113,10 +169,10 @@ user extension boundaries.
113
169
  Node.js 22.12+, 24, and 26 are supported. Use Node 24 LTS for production or Node 26 Current for
114
170
  current development environments. Node 20 is upstream EOL and is no longer a v0.3 runtime.
115
171
 
116
- After the immutable `0.3.4` npm receipt exists:
172
+ After the immutable `0.3.5` npm receipt exists:
117
173
 
118
174
  ```bash
119
- npm install -g @orion-agents/orion-code@0.3.4
175
+ npm install -g @orion-agents/orion-code@0.3.7
120
176
  orion --version
121
177
  orion doctor
122
178
  ```
package/README.zh-CN.md CHANGED
@@ -2,10 +2,14 @@
2
2
 
3
3
  面向终端与浏览器、本地优先的目标驱动 Coding Agent。
4
4
 
5
- > v0.3.4 候选版本:对 v0.3.3 Session Web Workbench 进行兼容的安全、终端顺序与发布证据
6
- > 稳定化。Candidate 源码不代表已创建 npm 发布、Git tag 或达到可合并状态。
5
+ > v0.3.7 候选版本:左侧项目栏交互发布(基于 v0.3.6 之上)—— 状态文案精简、会话行键盘可达
6
+ > 操作菜单(重命名 / 标签 / 归档 / 删除含确认)、可持久化会话标签与行内徽章、已归档会话区一键还原、
7
+ > 以及更友好的「打开或新增项目」工作区对话框。Candidate 源码不代表已创建 npm 发布、Git tag 或达到
8
+ > 可合并状态。
7
9
 
8
10
  [English](README.md) ·
11
+ [v0.3.7 方案](docs/plan/v0.3.7-left-rail-improvement-plan.md) ·
12
+ [v0.3.6 方案](docs/plan/v0.3.6-plan.md) ·
9
13
  [v0.3.4 稳定化方案](docs/plan/v0.3.4-stabilization-plan.md) ·
10
14
  [v0.3.4 合同](docs/architecture/v0.3.4-stabilization-contract.md) ·
11
15
  [v0.3.4 E2E 方案](docs/test/v0.3.4-stabilization-e2e-plan.md) ·
@@ -98,7 +102,7 @@ Current。Node 20 已结束上游维护,不再属于 v0.3 Runtime 合同。
98
102
  当 npm 已存在不可变的 `0.3.4` 发布凭据后:
99
103
 
100
104
  ```bash
101
- npm install -g @orion-agents/orion-code@0.3.4
105
+ npm install -g @orion-agents/orion-code@0.3.7
102
106
  orion --version
103
107
  orion doctor
104
108
  ```
@@ -206,11 +206,7 @@ async function showHarness(ctx, args = '') {
206
206
  },
207
207
  };
208
208
  const result = () => ({ success: true, output: lines.join('\n') });
209
- const tokens = args
210
- .trim()
211
- .toLowerCase()
212
- .split(/\s+/u)
213
- .filter(Boolean);
209
+ const tokens = args.trim().toLowerCase().split(/\s+/u).filter(Boolean);
214
210
  const explain = tokens[0] === 'explain';
215
211
  const json = tokens.includes('--json');
216
212
  if (tokens.some(token => token !== 'explain' && token !== '--json')) {
@@ -448,10 +448,7 @@ function registerBuiltinCommands(definitions) {
448
448
  if (ids.has(command.id))
449
449
  throw new Error(`Duplicate command id: ${command.id}`);
450
450
  ids.add(command.id);
451
- for (const value of [
452
- command.name,
453
- ...(command.aliases ?? []),
454
- ]) {
451
+ for (const value of [command.name, ...(command.aliases ?? [])]) {
455
452
  const normalized = value.toLowerCase();
456
453
  const owner = names.get(normalized);
457
454
  if (owner)
@@ -38,14 +38,24 @@ function extractTools(input) {
38
38
  return known.filter(tool => lower.includes(tool.toLowerCase()));
39
39
  }
40
40
  function extractConstraints(input) {
41
- const lines = input.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
41
+ const lines = input
42
+ .split(/\r?\n/)
43
+ .map(line => line.trim())
44
+ .filter(Boolean);
42
45
  const constraintPattern = /(必须|要求|确保|保持|不要|不能|不准|避免|只|仅|must|should|required|ensure|keep|avoid|without|do not|don't|never|only)/i;
43
- return [...new Set(lines.filter(line => constraintPattern.test(line)).map(line => truncate(line, 180)))].slice(0, 10);
46
+ return [
47
+ ...new Set(lines.filter(line => constraintPattern.test(line)).map(line => truncate(line, 180))),
48
+ ].slice(0, 10);
44
49
  }
45
50
  function extractNonGoals(input) {
46
- const lines = input.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
51
+ const lines = input
52
+ .split(/\r?\n/)
53
+ .map(line => line.trim())
54
+ .filter(Boolean);
47
55
  const nonGoalPattern = /(不要|不能|不准|避免|不引入|不新增|禁止|do not|don't|never|avoid|without)/i;
48
- return [...new Set(lines.filter(line => nonGoalPattern.test(line)).map(line => truncate(line, 180)))].slice(0, 8);
56
+ return [
57
+ ...new Set(lines.filter(line => nonGoalPattern.test(line)).map(line => truncate(line, 180))),
58
+ ].slice(0, 8);
49
59
  }
50
60
  function extractQuestions(input) {
51
61
  return input
@@ -75,45 +85,86 @@ function classifyKind(input, state) {
75
85
  return { kind: 'casual_or_feedback', confidence: 0.5, reason: 'empty input' };
76
86
  }
77
87
  if (isGenericContinue(text)) {
78
- return { kind: 'continue_current_task', confidence: 0.78, reason: 'generic continuation request' };
88
+ return {
89
+ kind: 'continue_current_task',
90
+ confidence: 0.78,
91
+ reason: 'generic continuation request',
92
+ };
79
93
  }
80
94
  if (isShortFeedback(text)) {
81
- return { kind: 'casual_or_feedback', confidence: 0.72, reason: 'short feedback should not replace the root objective' };
95
+ return {
96
+ kind: 'casual_or_feedback',
97
+ confidence: 0.72,
98
+ reason: 'short feedback should not replace the root objective',
99
+ };
82
100
  }
83
- if (matches(lower, /\b(verify|test|run tests?|check|lint|typecheck|build)\b/) || matches(text, /(验证|测试|检查|跑一下|运行测试|构建|编译)/)) {
101
+ if (matches(lower, /\b(verify|test|run tests?|check|lint|typecheck|build)\b/) ||
102
+ matches(text, /(验证|测试|检查|跑一下|运行测试|构建|编译)/)) {
84
103
  return { kind: 'verify_or_test', confidence: 0.82, reason: 'verification-oriented wording' };
85
104
  }
86
105
  if (hasRoot && isVersionScopedRefinement(text)) {
87
- return { kind: 'refine_current_task', confidence: 0.78, reason: 'version-scoped refinement within the active task' };
106
+ return {
107
+ kind: 'refine_current_task',
108
+ confidence: 0.78,
109
+ reason: 'version-scoped refinement within the active task',
110
+ };
88
111
  }
89
112
  if (matches(lower, /\b(harness|mcp|session|resume|compact|config|configuration|ui|npm|publish|push|pull request|pr)\b/) ||
90
113
  matches(text, /(配置|会话|恢复|压缩|版本|分支|发布|新ui|界面|工具|权限|确认|配置文件)/)) {
91
114
  if (matches(text, /(基于当前|切\s*v|切到|这个版本|新版本|开始按|完整开发|实现这个方案)/) ||
92
115
  matches(lower, /\b(please implement|implement this plan|start implementing|new version)\b/)) {
93
- return { kind: 'new_task', confidence: 0.88, reason: 'explicit versioned implementation task' };
116
+ return {
117
+ kind: 'new_task',
118
+ confidence: 0.88,
119
+ reason: 'explicit versioned implementation task',
120
+ };
94
121
  }
95
- return { kind: 'meta_configuration', confidence: 0.75, reason: 'configuration or CLI meta request' };
122
+ return {
123
+ kind: 'meta_configuration',
124
+ confidence: 0.75,
125
+ reason: 'configuration or CLI meta request',
126
+ };
96
127
  }
97
128
  if (matches(text, /(不对|不是|错了|改成|改为|换成|重新|重做|不要.+而是|应该.+而不是)/) ||
98
129
  matches(lower, /\b(instead|replace|switch to|redo|wrong)\b/)) {
99
- return { kind: 'interrupt_and_replace_current_step', confidence: 0.8, reason: 'correction or replacement wording' };
130
+ return {
131
+ kind: 'interrupt_and_replace_current_step',
132
+ confidence: 0.8,
133
+ reason: 'correction or replacement wording',
134
+ };
100
135
  }
101
136
  if (matches(text, /(基于|切到|这个版本|新版本|开始|实现|开发|生成|创建|写文档|完成本次|按这个计划|完整支持|帮我完成)/) ||
102
137
  matches(lower, /\b(generate|create|implement|build|start|complete this|write a|please implement)\b/)) {
103
- return { kind: 'new_task', confidence: hasRoot ? 0.76 : 0.86, reason: 'explicit task creation wording' };
138
+ return {
139
+ kind: 'new_task',
140
+ confidence: hasRoot ? 0.76 : 0.86,
141
+ reason: 'explicit task creation wording',
142
+ };
104
143
  }
105
144
  // Clarification: user is asking for explanation or asking about meaning
106
145
  // Must be checked BEFORE refinement since clarification phrases may contain "这个"
107
146
  if (matches(text, /(什么意思|解释一下|解释说明|clarify|explain to me|what does.*mean|what is.*for|can you explain|meaning of)/) ||
108
147
  matches(lower, /^(what|why|how|explain|clarify|could you explain|can you explain)\b/)) {
109
- return { kind: 'clarification', confidence: 0.7, reason: 'clarification or explanation request' };
148
+ return {
149
+ kind: 'clarification',
150
+ confidence: 0.7,
151
+ reason: 'clarification or explanation request',
152
+ };
110
153
  }
111
154
  if (matches(text, /(补充|另外|同时|还有|希望|可以|顺便|完善|优化|灰色|填充|这个|这里|如图|也|继续)/) ||
112
155
  matches(lower, /\b(also|additionally|please also|refine|improve|continue)\b/)) {
113
- return { kind: 'refine_current_task', confidence: hasRoot ? 0.76 : 0.58, reason: 'refinement or addition wording' };
156
+ return {
157
+ kind: 'refine_current_task',
158
+ confidence: hasRoot ? 0.76 : 0.58,
159
+ reason: 'refinement or addition wording',
160
+ };
114
161
  }
115
162
  return hasRoot
116
- ? { kind: 'refine_current_task', confidence: 0.55, reason: 'existing task makes ambiguous input a refinement' }
163
+ ? {
164
+ kind: 'refine_current_task',
165
+ confidence: 0.55,
166
+ reason: 'existing task makes ambiguous input a refinement',
167
+ }
117
168
  : { kind: 'new_task', confidence: 0.62, reason: 'first meaningful user task' };
118
169
  }
119
170
  function classifyIntent(input, state) {
@@ -121,7 +172,9 @@ function classifyIntent(input, state) {
121
172
  const baseEpoch = state?.taskEpoch ?? (state?.contract ? 1 : 0);
122
173
  const classified = classifyKind(input, state);
123
174
  const rootObjectiveChanged = classified.kind === 'new_task' || !state?.contract;
124
- const taskEpoch = rootObjectiveChanged ? Math.max(1, baseEpoch + (state?.contract ? 1 : 0)) : Math.max(1, baseEpoch || 1);
175
+ const taskEpoch = rootObjectiveChanged
176
+ ? Math.max(1, baseEpoch + (state?.contract ? 1 : 0))
177
+ : Math.max(1, baseEpoch || 1);
125
178
  const summary = truncate(input || 'Continue the current task');
126
179
  return {
127
180
  id: (0, crypto_1.randomUUID)(),
@@ -12,7 +12,9 @@ function unique(items) {
12
12
  function parseJsonObject(text) {
13
13
  try {
14
14
  const parsed = JSON.parse(text);
15
- return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
15
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
16
+ ? parsed
17
+ : null;
16
18
  }
17
19
  catch {
18
20
  return null;
@@ -100,7 +100,7 @@ class EmbeddingService {
100
100
  input: text,
101
101
  }, {
102
102
  headers: {
103
- 'Authorization': `Bearer ${apiKey}`,
103
+ Authorization: `Bearer ${apiKey}`,
104
104
  'Content-Type': 'application/json',
105
105
  },
106
106
  timeout: 30000,
@@ -121,8 +121,7 @@ let defaultService = null;
121
121
  function getEmbeddingService(config) {
122
122
  if (!defaultService) {
123
123
  // Auto-detect provider from environment
124
- const provider = process.env[environment_1.ENV.EMBEDDING_PROVIDER] ||
125
- (process.env.OLLAMA_BASE_URL ? 'ollama' : 'openai');
124
+ const provider = process.env[environment_1.ENV.EMBEDDING_PROVIDER] || (process.env.OLLAMA_BASE_URL ? 'ollama' : 'openai');
126
125
  defaultService = new EmbeddingService({
127
126
  provider: provider,
128
127
  model: process.env[environment_1.ENV.EMBEDDING_MODEL],
@@ -24,7 +24,7 @@ function buildMemoryPrompt() {
24
24
  'This directory already exists — write to it directly with the Write tool.',
25
25
  '',
26
26
  'You should build up this memory system over time so that future conversations',
27
- 'can have a complete picture of who the user is, how they\'d like to collaborate,',
27
+ "can have a complete picture of who the user is, how they'd like to collaborate,",
28
28
  'what behaviors to avoid or repeat, and the context behind the work.',
29
29
  '',
30
30
  'If the user explicitly asks you to remember something, save it immediately.',
@@ -38,20 +38,128 @@ function extractKeywords(text, preserveCase = false) {
38
38
  .filter(w => w.length >= 2);
39
39
  // 去除常见停用词
40
40
  const stopWords = new Set([
41
- 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
42
- 'of', 'with', 'by', 'from', 'as', 'is', 'was', 'are', 'were', 'been',
43
- 'be', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would',
44
- 'could', 'should', 'may', 'might', 'must', 'can', 'this', 'that',
45
- 'these', 'those', 'it', 'its', 'they', 'them', 'their', 'we', 'our',
46
- 'you', 'your', 'he', 'him', 'his', 'she', 'her', 'i', 'me', 'my',
47
- 'not', 'no', 'yes', 'all', 'any', 'some', 'each', 'every', 'both',
48
- 'few', 'many', 'more', 'most', 'other', 'another', 'such', 'only',
49
- 'own', 'same', 'so', 'than', 'too', 'very', 'just', 'also', 'now',
50
- 'then', 'here', 'there', 'when', 'where', 'why', 'how', 'what', 'which',
51
- 'who', 'whom', 'whose', 'if', 'else', 'then', 'because', 'since',
52
- 'although', 'though', 'while', 'during', 'before', 'after', 'until',
53
- 'above', 'below', 'between', 'under', 'over', 'out', 'into', 'onto',
54
- 'about', 'against', 'through', 'across', 'along', 'around', 'again',
41
+ 'the',
42
+ 'a',
43
+ 'an',
44
+ 'and',
45
+ 'or',
46
+ 'but',
47
+ 'in',
48
+ 'on',
49
+ 'at',
50
+ 'to',
51
+ 'for',
52
+ 'of',
53
+ 'with',
54
+ 'by',
55
+ 'from',
56
+ 'as',
57
+ 'is',
58
+ 'was',
59
+ 'are',
60
+ 'were',
61
+ 'been',
62
+ 'be',
63
+ 'have',
64
+ 'has',
65
+ 'had',
66
+ 'do',
67
+ 'does',
68
+ 'did',
69
+ 'will',
70
+ 'would',
71
+ 'could',
72
+ 'should',
73
+ 'may',
74
+ 'might',
75
+ 'must',
76
+ 'can',
77
+ 'this',
78
+ 'that',
79
+ 'these',
80
+ 'those',
81
+ 'it',
82
+ 'its',
83
+ 'they',
84
+ 'them',
85
+ 'their',
86
+ 'we',
87
+ 'our',
88
+ 'you',
89
+ 'your',
90
+ 'he',
91
+ 'him',
92
+ 'his',
93
+ 'she',
94
+ 'her',
95
+ 'i',
96
+ 'me',
97
+ 'my',
98
+ 'not',
99
+ 'no',
100
+ 'yes',
101
+ 'all',
102
+ 'any',
103
+ 'some',
104
+ 'each',
105
+ 'every',
106
+ 'both',
107
+ 'few',
108
+ 'many',
109
+ 'more',
110
+ 'most',
111
+ 'other',
112
+ 'another',
113
+ 'such',
114
+ 'only',
115
+ 'own',
116
+ 'same',
117
+ 'so',
118
+ 'than',
119
+ 'too',
120
+ 'very',
121
+ 'just',
122
+ 'also',
123
+ 'now',
124
+ 'then',
125
+ 'here',
126
+ 'there',
127
+ 'when',
128
+ 'where',
129
+ 'why',
130
+ 'how',
131
+ 'what',
132
+ 'which',
133
+ 'who',
134
+ 'whom',
135
+ 'whose',
136
+ 'if',
137
+ 'else',
138
+ 'then',
139
+ 'because',
140
+ 'since',
141
+ 'although',
142
+ 'though',
143
+ 'while',
144
+ 'during',
145
+ 'before',
146
+ 'after',
147
+ 'until',
148
+ 'above',
149
+ 'below',
150
+ 'between',
151
+ 'under',
152
+ 'over',
153
+ 'out',
154
+ 'into',
155
+ 'onto',
156
+ 'about',
157
+ 'against',
158
+ 'through',
159
+ 'across',
160
+ 'along',
161
+ 'around',
162
+ 'again',
55
163
  ]);
56
164
  // 停用词过滤始终大小写不敏感,否则 preserveCase 模式下 "The"/"And" 会漏过。
57
165
  return words.filter(w => !stopWords.has(preserveCase ? w.toLowerCase() : w));
@@ -84,7 +192,9 @@ function calculateKeywordMatch(queryKeywords, memoryEntry, caseSensitive = false
84
192
  for (const mk of memoryKeywords) {
85
193
  const compareMk = caseSensitive ? mk : mk.toLowerCase();
86
194
  // 精确匹配或部分匹配(包含关系)
87
- if (compareMk === compareQk || compareMk.includes(compareQk) || compareQk.includes(compareMk)) {
195
+ if (compareMk === compareQk ||
196
+ compareMk.includes(compareQk) ||
197
+ compareQk.includes(compareMk)) {
88
198
  matchedKeywords.push(qk);
89
199
  matchCount++;
90
200
  break;