@prevalentware/opencode-goal-plugin 0.1.48 → 0.1.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prevalentware/opencode-goal-plugin",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
4
4
  "description": "OpenCode goal plugin that adds Codex-style long-running goal mode, /goal commands, persistence, and TUI status for AI coding agents.",
5
5
  "keywords": [
6
6
  "opencode",
@@ -39,6 +39,7 @@
39
39
  "files": [
40
40
  "dist",
41
41
  "src/tui.ts",
42
+ "src/i18n.ts",
42
43
  "LICENSE",
43
44
  "README.md"
44
45
  ],
@@ -56,23 +57,31 @@
56
57
  },
57
58
  "dependencies": {
58
59
  "@opencode-ai/plugin": "^1.17.1",
59
- "@opentui/solid": ">=0.4.0 <0.6.0",
60
60
  "effect": "^3.21.2",
61
- "solid-js": "1.9.12",
62
61
  "zod": "^4.1.8"
63
62
  },
63
+ "peerDependencies": {
64
+ "@opentui/solid": ">=0.5.8",
65
+ "solid-js": ">=1.9.0"
66
+ },
64
67
  "devDependencies": {
65
68
  "@eslint/js": "^10.0.1",
66
- "@opencode-ai/plugin-v2": "npm:@opencode-ai/plugin@0.0.0-beta-18593",
67
- "@opencode-ai/theme": "0.0.0-beta-18593",
69
+ "@opencode/client": "0.0.0-beta-19425",
70
+ "@opencode/plugin": "0.0.0-beta-19425",
71
+ "@opencode/schema": "0.0.0-beta-19425",
72
+ "@opencode/theme": "0.0.0-beta-19425",
68
73
  "@opentui/core": "0.5.9",
74
+ "@opentui/solid": "0.5.9",
69
75
  "@types/bun": "^1.3.13",
76
+ "@types/semver": "^7.8.0",
70
77
  "eslint": "^10.3.0",
78
+ "semver": "^7.8.5",
79
+ "solid-js": "1.9.12",
71
80
  "typescript": "^6.0.3",
72
81
  "typescript-eslint": "^8.59.2"
73
82
  },
74
83
  "engines": {
75
- "opencode": ">=1.17.1"
84
+ "opencode": ">=0.0.0-beta-0 <0.0.0 || >=1.17.1"
76
85
  },
77
86
  "publishConfig": {
78
87
  "access": "public"
package/src/i18n.ts ADDED
@@ -0,0 +1,532 @@
1
+ export type GoalLocale = "en" | "zh-CN"
2
+
3
+ type LocaleEnvironment = {
4
+ LC_ALL?: string
5
+ LANG?: string
6
+ }
7
+
8
+ export type GoalMessages = {
9
+ commands: {
10
+ goalDescription: string
11
+ pauseDescription: string
12
+ resumeDescription: string
13
+ }
14
+ tools: {
15
+ getGoal: string
16
+ getGoalHistory: string
17
+ listAllGoals: string
18
+ createGoal: string
19
+ setGoal: string
20
+ updateGoalObjective: string
21
+ updateGoal: string
22
+ updateGoalStatus: string
23
+ clearGoal: string
24
+ objective: string
25
+ modelObjective: string
26
+ updatedObjective: string
27
+ tokenBudget: string
28
+ maxAutoTurns: string
29
+ maxDurationSeconds: string
30
+ editStatus: string
31
+ closeStatus: string
32
+ evidence: string
33
+ blocker: string
34
+ activePausedStatus: string
35
+ }
36
+ notices: {
37
+ planModeCreate: string
38
+ limitedGoal: string
39
+ duplicateGoal: string
40
+ conflictingGoal: string
41
+ restrictedGoal: string
42
+ cannotResumeInPlan: string
43
+ }
44
+ reports: {
45
+ achieved: string
46
+ unmet: string
47
+ timeUsed: string
48
+ tokenUsage: string
49
+ evidence: string
50
+ blocker: string
51
+ seconds: string
52
+ }
53
+ tui: {
54
+ title: string
55
+ commandDescription: string
56
+ refresh: string
57
+ refreshDescription: string
58
+ history: string
59
+ historyDescription: string
60
+ pause: string
61
+ pauseDescription: string
62
+ resume: string
63
+ resumeDescription: string
64
+ clear: string
65
+ clearDescription: string
66
+ refreshPrompt: string
67
+ historyPrompt: string
68
+ pausePrompt: string
69
+ resumePrompt: string
70
+ clearPrompt: string
71
+ openSession: string
72
+ noGoal: string
73
+ objective: string
74
+ status: string
75
+ timeUsed: string
76
+ time: string
77
+ tokens: string
78
+ autoContinues: string
79
+ tokensRemaining: string
80
+ durationLimit: string
81
+ noProgressTurns: string
82
+ latestCheckpoint: string
83
+ checkpoint: string
84
+ stopReason: string
85
+ stop: string
86
+ lastStatus: string
87
+ completionEvidence: string
88
+ blocker: string
89
+ achieved: string
90
+ unmet: string
91
+ }
92
+ }
93
+
94
+ const EN_MESSAGES: GoalMessages = {
95
+ commands: {
96
+ goalDescription: "Set or view the long-running session goal",
97
+ pauseDescription: "Pause the current long-running session goal",
98
+ resumeDescription: "Resume the current long-running session goal",
99
+ },
100
+ tools: {
101
+ getGoal:
102
+ "Get the current goal for this OpenCode session, including status, observed token usage, elapsed-time usage, " +
103
+ "budgets, checkpoints, and history.",
104
+ getGoalHistory: "Get the current goal lifecycle history and recent checkpoints for this OpenCode session.",
105
+ listAllGoals:
106
+ "List up to 50 public goal summaries across all sessions in this state file, ordered by most recently updated " +
107
+ "first. Elapsed time is the last persisted value; total and truncated report omitted older goals.",
108
+ createGoal:
109
+ "Create a goal only when explicitly requested by the user or system/developer instructions; do not infer goals " +
110
+ "from ordinary tasks. If any non-closed goal exists, this returns the existing goal as either reused or " +
111
+ "conflicting and must not be retried. While the session is in Plan mode, the goal is recorded as paused and " +
112
+ "execution requires the user to switch to Build mode.",
113
+ setGoal:
114
+ "Set a new goal when the user explicitly asks the agent to formulate and set its own goal. The model should " +
115
+ "write the objective itself based on the user's explicit request. If any non-closed goal exists, this returns " +
116
+ "the existing goal as either reused or conflicting and must not be retried. While the session is in Plan mode, " +
117
+ "the goal is recorded as paused and execution requires the user to switch to Build mode.",
118
+ updateGoalObjective: "Edit the current OpenCode goal objective when the user explicitly asks to edit or replace it.",
119
+ updateGoal:
120
+ "Close the existing goal only after an audit against real evidence. Use status complete only when the objective " +
121
+ "is achieved and no required work remains, and include evidence. Use status unmet only when the objective " +
122
+ "cannot be achieved or is blocked, and include the blocker. Do not close a goal merely because work is stopping.",
123
+ updateGoalStatus:
124
+ "Pause or resume the current OpenCode goal when the user explicitly asks to pause or resume it. Resuming is not " +
125
+ "allowed while the session is in Plan mode; the user must switch to Build mode first.",
126
+ clearGoal: "Clear the current OpenCode goal for this session when the user explicitly asks to clear it.",
127
+ objective: "The concrete objective to start pursuing.",
128
+ modelObjective: "The model-formulated concrete objective to start pursuing.",
129
+ updatedObjective: "The updated concrete objective.",
130
+ tokenBudget: "Optional positive token budget.",
131
+ maxAutoTurns: "Optional per-goal auto-continue limit.",
132
+ maxDurationSeconds: "Optional per-goal duration limit.",
133
+ editStatus: "Whether the edited goal should be active or paused.",
134
+ closeStatus: "Required. complete means achieved; unmet means blocked or impossible.",
135
+ evidence: "Required when status is complete. Summarize the concrete evidence verified.",
136
+ blocker: "Required when status is unmet. Explain the concrete blocker or impossibility.",
137
+ activePausedStatus: "active resumes a goal; paused pauses it without clearing it.",
138
+ },
139
+ notices: {
140
+ planModeCreate:
141
+ "Goal recorded while the session is in Plan mode, so execution is paused. Do not start implementation work " +
142
+ 'now. Ask the user to switch to Build mode and resume the goal (for example with "/goal resume") to begin execution.',
143
+ limitedGoal:
144
+ "Safety limit reached. Do not start or continue substantive work for this goal. Summarize useful progress, " +
145
+ "remaining work, and blockers, then wait for the user to resume or edit the goal.",
146
+ duplicateGoal:
147
+ "This non-closed goal already exists. Do not call create_goal or set_goal again. The existing objective and " +
148
+ "limits were preserved; repeated-call arguments were not applied. Use the returned goal state and continue only " +
149
+ "when its status permits execution.",
150
+ conflictingGoal:
151
+ "A different non-closed goal already exists. Do not call create_goal or set_goal again. Report the conflict " +
152
+ "instead of replacing the goal; edit, clear, complete, or mark it unmet only when explicitly requested.",
153
+ restrictedGoal:
154
+ "Goal execution is not allowed from the current restricted agent or while the goal is paused for Plan mode. " +
155
+ "Switch to Build mode and resume the goal before doing substantive work.",
156
+ cannotResumeInPlan:
157
+ "cannot resume the goal while the session is in Plan mode; ask the user to switch to Build mode and resume the " +
158
+ "goal from there",
159
+ },
160
+ reports: {
161
+ achieved: "Goal achieved.",
162
+ unmet: "Goal unmet.",
163
+ timeUsed: "Time used",
164
+ tokenUsage: "Token usage",
165
+ evidence: "Evidence",
166
+ blocker: "Blocker",
167
+ seconds: "seconds",
168
+ },
169
+ tui: {
170
+ title: "Goal",
171
+ commandDescription: "View, pause, resume, or clear the long-running session goal",
172
+ refresh: "Refresh",
173
+ refreshDescription: "Ask the agent to read the current goal state",
174
+ history: "History",
175
+ historyDescription: "Ask the agent to show lifecycle history",
176
+ pause: "Pause",
177
+ pauseDescription: "Pause auto-continuation without clearing",
178
+ resume: "Resume",
179
+ resumeDescription: "Resume the goal and continue",
180
+ clear: "Clear",
181
+ clearDescription: "Ask the agent to clear this session goal",
182
+ refreshPrompt: "Call get_goal for this session and report the current goal state briefly.",
183
+ historyPrompt: "Call get_goal_history for this session and report the current goal history briefly.",
184
+ pausePrompt: 'Pause the current session goal by calling update_goal_status with status "paused". Report the result briefly.',
185
+ resumePrompt:
186
+ 'Resume the current session goal by calling update_goal_status with status "active", then continue working toward it.',
187
+ clearPrompt: "Clear the current session goal by calling clear_goal. Report whether a goal was cleared.",
188
+ openSession: "Open a session before viewing goal state.",
189
+ noGoal: "No recent goal state found in this session.",
190
+ objective: "Objective",
191
+ status: "Status",
192
+ timeUsed: "Time used",
193
+ time: "Time",
194
+ tokens: "Tokens",
195
+ autoContinues: "Auto-continues",
196
+ tokensRemaining: "Tokens remaining",
197
+ durationLimit: "Duration limit",
198
+ noProgressTurns: "No-progress turns",
199
+ latestCheckpoint: "Latest checkpoint",
200
+ checkpoint: "Checkpoint",
201
+ stopReason: "Stop reason",
202
+ stop: "Stop",
203
+ lastStatus: "Last status",
204
+ completionEvidence: "Completion evidence",
205
+ blocker: "Blocker",
206
+ achieved: "Goal achieved",
207
+ unmet: "Goal unmet",
208
+ },
209
+ }
210
+
211
+ const ZH_CN_MESSAGES: GoalMessages = {
212
+ commands: {
213
+ goalDescription: "设置或查看当前会话的长期目标",
214
+ pauseDescription: "暂停当前会话的长期目标",
215
+ resumeDescription: "继续当前会话的长期目标",
216
+ },
217
+ tools: {
218
+ getGoal: "获取当前 OpenCode 会话的目标,包括状态、已观察到的 token 使用量、已用时间、预算、检查点和历史记录。",
219
+ getGoalHistory: "获取当前 OpenCode 会话的目标生命周期历史和最近的检查点。",
220
+ listAllGoals:
221
+ "列出此状态文件中所有会话里最近更新的最多 50 个公开目标摘要。已用时间采用最后一次持久化的值;total 和 truncated 字段用于说明是否省略了更早的目标。",
222
+ createGoal:
223
+ "仅当用户或 system/developer 指令明确要求时创建目标,不要从普通任务中推断目标。" +
224
+ "如果已有未关闭目标,则返回该目标并标记为复用或冲突,不得重试。" +
225
+ "在 Plan 模式下创建目标时,目标会以暂停状态记录;用户切换到 Build 模式后才能执行。",
226
+ setGoal:
227
+ "仅当用户明确要求 Agent 自行制定并设置目标时创建新目标。模型应依据用户的明确请求自行撰写目标。" +
228
+ "如果已有未关闭目标,则返回该目标并标记为复用或冲突,不得重试。" +
229
+ "在 Plan 模式下创建目标时,目标会以暂停状态记录;用户切换到 Build 模式后才能执行。",
230
+ updateGoalObjective: "仅当用户明确要求编辑或替换目标时,修改当前 OpenCode 目标的内容。",
231
+ updateGoal:
232
+ "只有在依据真实证据完成审计后才能关闭现有目标。仅当目标已经达成且没有剩余必需工作时使用 complete,并提供证据;仅当目标无法达成或被阻塞时使用 unmet,并提供阻塞原因。不要仅因为准备停止工作就关闭目标。",
233
+ updateGoalStatus:
234
+ "仅当用户明确要求暂停或继续目标时,暂停或继续当前 OpenCode 目标。在 Plan 模式下不能继续目标;用户必须先切换到 Build 模式。",
235
+ clearGoal: "仅当用户明确要求清除目标时,清除当前 OpenCode 会话的目标。",
236
+ objective: "要开始执行的具体目标。",
237
+ modelObjective: "由模型制定、要开始执行的具体目标。",
238
+ updatedObjective: "更新后的具体目标。",
239
+ tokenBudget: "可选的正数 token 预算。",
240
+ maxAutoTurns: "可选的单目标自动继续次数上限。",
241
+ maxDurationSeconds: "可选的单目标持续时间上限。",
242
+ editStatus: "编辑后的目标应处于 active 还是 paused 状态。",
243
+ closeStatus: "必填。complete 表示已达成;unmet 表示被阻塞或无法完成。",
244
+ evidence: "status 为 complete 时必填。概述已核验的具体证据。",
245
+ blocker: "status 为 unmet 时必填。说明具体阻塞原因或无法完成的原因。",
246
+ activePausedStatus: "active 表示继续目标;paused 表示暂停但不清除目标。",
247
+ },
248
+ notices: {
249
+ planModeCreate:
250
+ '目标已在 Plan 模式下记录,因此执行被暂停。现在不要开始实现工作。请让用户切换到 Build 模式并继续目标(例如使用 "/goal resume")后再开始执行。',
251
+ limitedGoal:
252
+ "已达到安全限制。不要开始或继续此目标的实质性工作。请总结已有进展、剩余工作和阻塞项,然后等待用户继续或编辑目标。",
253
+ duplicateGoal:
254
+ "这个未关闭目标已经存在。不要再次调用 create_goal 或 set_goal。现有目标内容和限制已保留,重复调用的参数没有应用。请使用返回的目标状态,并且只在其状态允许执行时继续。",
255
+ conflictingGoal:
256
+ "已有另一个未关闭目标。不要再次调用 create_goal 或 set_goal,也不要替换现有目标;请报告冲突。只有在用户明确要求时,才可编辑、清除、完成目标或将其标记为 unmet。",
257
+ restrictedGoal:
258
+ "当前受限 Agent 或 Plan 模式暂停状态不允许执行目标。请先切换到 Build 模式并继续目标,再进行实质性工作。",
259
+ cannotResumeInPlan: "会话处于 Plan 模式时不能继续目标;请让用户切换到 Build 模式后再继续该目标",
260
+ },
261
+ reports: {
262
+ achieved: "目标已达成。",
263
+ unmet: "目标未达成。",
264
+ timeUsed: "已用时间",
265
+ tokenUsage: "Token 使用量",
266
+ evidence: "证据",
267
+ blocker: "阻塞原因",
268
+ seconds: "秒",
269
+ },
270
+ tui: {
271
+ title: "目标",
272
+ commandDescription: "查看、暂停、继续或清除当前会话的长期目标",
273
+ refresh: "刷新",
274
+ refreshDescription: "让 Agent 读取当前目标状态",
275
+ history: "历史",
276
+ historyDescription: "让 Agent 显示目标生命周期历史",
277
+ pause: "暂停",
278
+ pauseDescription: "暂停自动继续,但不清除目标",
279
+ resume: "继续",
280
+ resumeDescription: "继续目标并接着执行",
281
+ clear: "清除",
282
+ clearDescription: "让 Agent 清除当前会话目标",
283
+ refreshPrompt: "调用 get_goal 获取此会话的当前目标,并用简体中文简要报告目标状态。",
284
+ historyPrompt: "调用 get_goal_history 获取此会话的当前目标历史,并用简体中文简要报告。",
285
+ pausePrompt: '调用 update_goal_status 并将 status 设为 "paused",暂停当前会话目标。用简体中文简要报告结果。',
286
+ resumePrompt:
287
+ '调用 update_goal_status 并将 status 设为 "active",继续当前会话目标,然后继续推进该目标。请使用简体中文回复用户。',
288
+ clearPrompt: "调用 clear_goal 清除当前会话目标,并用简体中文报告是否成功清除了目标。",
289
+ openSession: "请先打开一个会话,再查看目标状态。",
290
+ noGoal: "此会话中没有最近的目标状态。",
291
+ objective: "目标",
292
+ status: "状态",
293
+ timeUsed: "已用时间",
294
+ time: "时间",
295
+ tokens: "Token",
296
+ autoContinues: "自动继续次数",
297
+ tokensRemaining: "剩余 Token",
298
+ durationLimit: "持续时间上限",
299
+ noProgressTurns: "无进展轮数",
300
+ latestCheckpoint: "最新检查点",
301
+ checkpoint: "检查点",
302
+ stopReason: "停止原因",
303
+ stop: "停止",
304
+ lastStatus: "最近状态",
305
+ completionEvidence: "完成证据",
306
+ blocker: "阻塞原因",
307
+ achieved: "目标已达成",
308
+ unmet: "目标未达成",
309
+ },
310
+ }
311
+
312
+ function normalizeLocaleCandidate(value: string | null | undefined): GoalLocale | null {
313
+ if (!value?.trim()) return null
314
+ const normalized = value.trim().replaceAll("_", "-").split(".")[0]!.split("@")[0]!.toLowerCase()
315
+ if (normalized === "c" || normalized === "posix") return null
316
+ if (normalized === "zh" || normalized.startsWith("zh-")) return "zh-CN"
317
+ if (normalized === "en" || normalized.startsWith("en-")) return "en"
318
+ return null
319
+ }
320
+
321
+ function processEnvironment(): LocaleEnvironment {
322
+ if (typeof process === "undefined") return {}
323
+ return {
324
+ LC_ALL: process.env.LC_ALL,
325
+ LANG: process.env.LANG,
326
+ }
327
+ }
328
+
329
+ function systemLocale() {
330
+ try {
331
+ return Intl.DateTimeFormat().resolvedOptions().locale
332
+ } catch {
333
+ return undefined
334
+ }
335
+ }
336
+
337
+ export function resolveLocale(
338
+ explicit?: string | null,
339
+ environment: LocaleEnvironment = processEnvironment(),
340
+ osLocale: string | undefined = systemLocale(),
341
+ ): GoalLocale {
342
+ const configured = explicit?.trim()
343
+ if (!configured) return "en"
344
+ if (configured.toLowerCase() !== "auto") return normalizeLocaleCandidate(configured) ?? "en"
345
+
346
+ for (const candidate of [environment.LC_ALL, environment.LANG, osLocale]) {
347
+ const locale = normalizeLocaleCandidate(candidate)
348
+ if (locale) return locale
349
+ }
350
+ return "en"
351
+ }
352
+
353
+ export function messagesFor(locale: GoalLocale): GoalMessages {
354
+ return locale === "zh-CN" ? ZH_CN_MESSAGES : EN_MESSAGES
355
+ }
356
+
357
+ const STATUS_PRESENTATIONS: Record<GoalLocale, Record<string, string>> = {
358
+ en: {
359
+ active: "active",
360
+ paused: "paused",
361
+ budgetLimited: "budget limited",
362
+ usageLimited: "usage limited",
363
+ complete: "complete",
364
+ unmet: "unmet",
365
+ },
366
+ "zh-CN": {
367
+ active: "进行中",
368
+ paused: "已暂停",
369
+ budgetLimited: "预算已达上限",
370
+ usageLimited: "使用量已达上限",
371
+ complete: "已完成",
372
+ unmet: "未达成",
373
+ },
374
+ }
375
+
376
+ /** Formats protocol status values only at user-facing presentation boundaries. */
377
+ export function presentGoalStatus(status: string, locale: GoalLocale): string {
378
+ return STATUS_PRESENTATIONS[locale][status] ?? status
379
+ }
380
+
381
+ /**
382
+ * Formats stop reasons produced by this plugin. Unknown values are user-authored
383
+ * or externally supplied text and must be returned verbatim.
384
+ */
385
+ export function presentGoalStopReason(reason: string, locale: GoalLocale): string {
386
+ if (locale !== "zh-CN") return reason
387
+
388
+ const direct: Record<string, string> = {
389
+ paused: "已暂停",
390
+ blocked: "已阻塞",
391
+ "plan mode": "Plan 模式",
392
+ "no progress": "无进展",
393
+ "auto-continue failures": "自动继续失败",
394
+ "goal limit reached": "已达到目标限制",
395
+ "token budget reached": "已达到 Token 预算",
396
+ "max auto-continues reached": "已达到自动继续次数上限",
397
+ "max duration reached": "已达到持续时间上限",
398
+ }
399
+ if (direct[reason]) return direct[reason]
400
+
401
+ const tokenBudget = /^token budget reached \((\d+)\/(\d+)\)$/.exec(reason)
402
+ if (tokenBudget) return `已达到 Token 预算(${tokenBudget[1]}/${tokenBudget[2]})`
403
+ const autoContinues = /^max auto-continues reached \((\d+)\)$/.exec(reason)
404
+ if (autoContinues) return `已达到自动继续次数上限(${autoContinues[1]})`
405
+ const duration = /^max duration reached \((\d+)s\)$/.exec(reason)
406
+ if (duration) return `已达到持续时间上限(${duration[1]} 秒)`
407
+ return reason
408
+ }
409
+
410
+ /**
411
+ * Formats status text generated by this plugin. Unknown text can come from a
412
+ * user-authored blocker, checkpoint, or older plugin and stays byte-for-byte
413
+ * intact at the presentation boundary.
414
+ */
415
+ export function presentGoalLastStatus(status: string, locale: GoalLocale): string {
416
+ if (locale !== "zh-CN") return status
417
+
418
+ const direct: Record<string, string> = {
419
+ "Goal set.": "目标已设置。",
420
+ "Goal recorded from Plan mode; execution paused until resumed from Build mode.":
421
+ "目标已在 Plan 模式下记录;执行已暂停,需在 Build 模式下继续。",
422
+ "Goal objective updated; execution paused while the session is in Plan mode.":
423
+ "目标内容已更新;会话处于 Plan 模式,因此执行已暂停。",
424
+ "Goal objective updated and resumed.": "目标内容已更新并继续执行。",
425
+ "Goal objective updated and paused.": "目标内容已更新并暂停。",
426
+ "Auto-continue paused while the session is in Plan mode.": "会话处于 Plan 模式,因此自动继续已暂停。",
427
+ "Goal resumed.": "目标已继续。",
428
+ "Goal paused.": "目标已暂停。",
429
+ "Goal completed.": "目标已完成。",
430
+ "Goal marked unmet.": "目标已标记为未达成。",
431
+ "Auto-continue attempt canceled before delivery.": "自动继续尝试已在发送前取消。",
432
+ "Auto-continue prompt sent.": "自动继续提示已发送。",
433
+ "Auto-continue prompt failed repeatedly. Resume the goal to retry.": "自动继续提示反复失败。请继续目标后重试。",
434
+ "Goal execution is paused while the session is in Plan mode. Switch to Build mode and resume the goal to continue.":
435
+ "会话处于 Plan 模式,因此目标执行已暂停。请切换到 Build 模式并继续目标。",
436
+ }
437
+ if (direct[status]) return direct[status]
438
+
439
+ const lowProgressPausePattern =
440
+ /^Auto-continue paused after (\d+) low-progress continuation turn\(s\)\. Resume the goal to retry\.$/
441
+ const lowProgressPause = lowProgressPausePattern.exec(status)
442
+ if (lowProgressPause) return `自动继续已在 ${lowProgressPause[1]} 个低进展轮次后暂停。请继续目标后重试。`
443
+
444
+ const lowProgress = /^Low-progress continuation turn detected \((\d+)\/(\d+|unbounded)\)\.$/.exec(status)
445
+ if (lowProgress) {
446
+ const limit = lowProgress[2] === "unbounded" ? "不限" : lowProgress[2]
447
+ return `检测到低进展的继续轮次(${lowProgress[1]}/${limit})。`
448
+ }
449
+
450
+ const reserved = /^Auto-continue (\d+) reserved\.$/.exec(status)
451
+ if (reserved) return `已预留第 ${reserved[1]} 次自动继续。`
452
+ const failed = /^Auto-continue failed (\d+) time\(s\)\.$/.exec(status)
453
+ if (failed) return `自动继续已失败 ${failed[1]} 次。`
454
+ const pausedAfterFailures = /^Paused after (\d+) auto-continue failure\(s\)\.$/.exec(status)
455
+ if (pausedAfterFailures) return `已在 ${pausedAfterFailures[1]} 次自动继续失败后暂停。`
456
+
457
+ const wrapUp = /^(.*); wrap-up required\.$/.exec(status)
458
+ if (wrapUp) return `${presentGoalStopReason(wrapUp[1]!, locale)};需要收尾。`
459
+ return status
460
+ }
461
+
462
+ const HISTORY_TYPE_PRESENTATIONS: Record<GoalLocale, Record<string, string>> = {
463
+ en: {},
464
+ "zh-CN": {
465
+ created: "已创建",
466
+ updated: "已更新",
467
+ paused: "已暂停",
468
+ resumed: "已继续",
469
+ completed: "已完成",
470
+ unmet: "未达成",
471
+ autoContinue: "自动继续",
472
+ checkpoint: "检查点",
473
+ warning: "警告",
474
+ limited: "已受限",
475
+ error: "错误",
476
+ },
477
+ }
478
+
479
+ export function presentGoalHistoryType(type: string, locale: GoalLocale): string {
480
+ return HISTORY_TYPE_PRESENTATIONS[locale][type] ?? type
481
+ }
482
+
483
+ /** Localizes only plugin-owned history framing and preserves embedded user text. */
484
+ export function presentGoalHistoryDetail(detail: string, locale: GoalLocale): string {
485
+ if (locale !== "zh-CN") return detail
486
+ const lastStatus = presentGoalLastStatus(detail, locale)
487
+ if (lastStatus !== detail) return lastStatus
488
+
489
+ if (detail === "Goal set with default continuation limits.") return "目标已按默认继续限制设置。"
490
+ const objectiveUpdate = /^Goal objective updated: (.*)$/.exec(detail)
491
+ if (objectiveUpdate) return `目标内容已更新:${objectiveUpdate[1]}`
492
+
493
+ const configuredLimits = /^Goal set with (.*)\.$/.exec(detail)
494
+ if (configuredLimits) {
495
+ const limits = configuredLimits[1]!
496
+ .split(", ")
497
+ .map((value) => {
498
+ const tokenBudget = /^(\d+) token budget$/.exec(value)
499
+ if (tokenBudget) return `Token 预算 ${tokenBudget[1]}`
500
+ const autoContinues = /^(\d+) auto-continue limit$/.exec(value)
501
+ if (autoContinues) return `自动继续次数上限 ${autoContinues[1]}`
502
+ const duration = /^(\d+)s duration limit$/.exec(value)
503
+ if (duration) return `持续时间上限 ${duration[1]} 秒`
504
+ return value
505
+ })
506
+ .join(",")
507
+ return `目标已设置,限制为:${limits}。`
508
+ }
509
+
510
+ const finalHandoff = /^(\w+): (.*); requested final handoff\.$/.exec(detail)
511
+ if (finalHandoff) {
512
+ return `${presentGoalStatus(finalHandoff[1]!, locale)}:${presentGoalStopReason(finalHandoff[2]!, locale)};已请求最终交接。`
513
+ }
514
+ return detail
515
+ }
516
+
517
+ type PresentableGoalHistory = {
518
+ history: Array<{ type: string; detail: string; timestamp: number }>
519
+ }
520
+
521
+ export function formatGoalHistoryPresentation(goal: PresentableGoalHistory | null, locale: GoalLocale): string {
522
+ if (!goal) return locale === "zh-CN" ? "此会话没有可用的目标历史。" : "No goal history is available for this session."
523
+ if (goal.history.length === 0) return locale === "zh-CN" ? "尚未记录目标历史。" : "No goal history recorded yet."
524
+ return goal.history
525
+ .map((entry) => {
526
+ const timestamp = new Date(entry.timestamp * 1000).toISOString()
527
+ const type = presentGoalHistoryType(entry.type, locale)
528
+ const detail = presentGoalHistoryDetail(entry.detail, locale)
529
+ return `- [${timestamp}] ${type}: ${detail}`
530
+ })
531
+ .join("\n")
532
+ }