superun-ai-cli 0.1.10 → 0.1.13

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 (71) hide show
  1. package/README.md +128 -2
  2. package/dist/api/agent-command-api.js +11 -1
  3. package/dist/api/agent-command-api.js.map +1 -1
  4. package/dist/api/agent-query-api.d.ts +6 -0
  5. package/dist/api/agent-query-api.js +18 -0
  6. package/dist/api/agent-query-api.js.map +1 -1
  7. package/dist/commands/chat/index.js +4 -0
  8. package/dist/commands/chat/index.js.map +1 -1
  9. package/dist/commands/chat/message.d.ts +1 -1
  10. package/dist/commands/chat/message.js +64 -5
  11. package/dist/commands/chat/message.js.map +1 -1
  12. package/dist/commands/chat/review.d.ts +4 -0
  13. package/dist/commands/chat/review.js +7 -0
  14. package/dist/commands/chat/review.js.map +1 -0
  15. package/dist/commands/chat/send.js +4 -1
  16. package/dist/commands/chat/send.js.map +1 -1
  17. package/dist/commands/chat/style.js +1 -1
  18. package/dist/commands/chat/test.d.ts +4 -0
  19. package/dist/commands/chat/test.js +7 -0
  20. package/dist/commands/chat/test.js.map +1 -0
  21. package/dist/commands/chat/wait.js +8 -1
  22. package/dist/commands/chat/wait.js.map +1 -1
  23. package/dist/commands/system/update.d.ts +1 -1
  24. package/dist/commands/system/update.js +1 -1
  25. package/dist/commands/system/update.js.map +1 -1
  26. package/dist/contracts/cli-output.d.ts +23 -1
  27. package/dist/conversation/auto-test-tasks.d.ts +14 -0
  28. package/dist/conversation/auto-test-tasks.js +120 -0
  29. package/dist/conversation/auto-test-tasks.js.map +1 -0
  30. package/dist/conversation/auto-test.d.ts +14 -0
  31. package/dist/conversation/auto-test.js +25 -0
  32. package/dist/conversation/auto-test.js.map +1 -0
  33. package/dist/conversation/check-context.d.ts +20 -0
  34. package/dist/conversation/check-context.js +67 -0
  35. package/dist/conversation/check-context.js.map +1 -0
  36. package/dist/conversation/check-question-context.d.ts +4 -0
  37. package/dist/conversation/check-question-context.js +57 -0
  38. package/dist/conversation/check-question-context.js.map +1 -0
  39. package/dist/conversation/code-review.d.ts +14 -0
  40. package/dist/conversation/code-review.js +26 -0
  41. package/dist/conversation/code-review.js.map +1 -0
  42. package/dist/conversation/next-actions.d.ts +3 -1
  43. package/dist/conversation/next-actions.js +167 -10
  44. package/dist/conversation/next-actions.js.map +1 -1
  45. package/dist/conversation/round-selector.d.ts +2 -0
  46. package/dist/conversation/round-selector.js +9 -2
  47. package/dist/conversation/round-selector.js.map +1 -1
  48. package/dist/conversation/session-waiter.js +6 -4
  49. package/dist/conversation/session-waiter.js.map +1 -1
  50. package/dist/conversation/task-progress-reader.d.ts +9 -2
  51. package/dist/conversation/task-progress-reader.js +54 -3
  52. package/dist/conversation/task-progress-reader.js.map +1 -1
  53. package/dist/interactions/handlers/reply-universal-ask.js +25 -1
  54. package/dist/interactions/handlers/reply-universal-ask.js.map +1 -1
  55. package/dist/output/task-reminder.d.ts +12 -0
  56. package/dist/output/task-reminder.js +35 -0
  57. package/dist/output/task-reminder.js.map +1 -0
  58. package/dist/output/writer.d.ts +6 -1
  59. package/dist/output/writer.js +42 -1
  60. package/dist/output/writer.js.map +1 -1
  61. package/dist/runner.js +21 -1
  62. package/dist/runner.js.map +1 -1
  63. package/dist/runtime.d.ts +4 -0
  64. package/dist/runtime.js +219 -33
  65. package/dist/runtime.js.map +1 -1
  66. package/dist/update/version-gate.js +43 -56
  67. package/dist/update/version-gate.js.map +1 -1
  68. package/package.json +1 -1
  69. package/dist/update/update-lock.d.ts +0 -1
  70. package/dist/update/update-lock.js +0 -57
  71. package/dist/update/update-lock.js.map +0 -1
@@ -0,0 +1,35 @@
1
+ /** 任务进行中的通用定时提醒,由顶层输出管理生命周期。@author xiuyu.yi */
2
+ import { randomUUID } from 'node:crypto';
3
+ export class TaskReminder {
4
+ emit;
5
+ timer;
6
+ constructor(emit) {
7
+ this.emit = emit;
8
+ }
9
+ start() {
10
+ if (this.timer)
11
+ return;
12
+ const taskId = randomUUID();
13
+ let sequence = 0;
14
+ this.timer = setInterval(() => {
15
+ try {
16
+ this.emit({
17
+ id: `${taskId}:${++sequence}`,
18
+ message: '任务还在进行中,可以去 superun.ai 查看详情。',
19
+ url: 'https://superun.ai',
20
+ });
21
+ }
22
+ catch {
23
+ // 提醒输出失败只停止提醒,不中断业务执行。
24
+ this.stop();
25
+ }
26
+ }, 5 * 60 * 1000);
27
+ this.timer.unref();
28
+ }
29
+ stop() {
30
+ if (this.timer)
31
+ clearInterval(this.timer);
32
+ this.timer = undefined;
33
+ }
34
+ }
35
+ //# sourceMappingURL=task-reminder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-reminder.js","sourceRoot":"","sources":["../../src/output/task-reminder.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQzC,MAAM,OAAO,YAAY;IAGM;IAFrB,KAAK,CAAkC;IAE/C,YAA6B,IAA4C;QAA5C,SAAI,GAAJ,IAAI,CAAwC;IAAG,CAAC;IAE7E,KAAK;QACH,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,WAAW,CACtB,GAAG,EAAE;YACH,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,CAAC;oBACR,EAAE,EAAE,GAAG,MAAM,IAAI,EAAE,QAAQ,EAAE;oBAC7B,OAAO,EAAE,8BAA8B;oBACvC,GAAG,EAAE,oBAAoB;iBAC1B,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,uBAAuB;gBACvB,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,CAAC;QACH,CAAC,EACD,CAAC,GAAG,EAAE,GAAG,IAAI,CACd,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,KAAK;YAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;IACzB,CAAC;CACF"}
@@ -4,12 +4,17 @@ export declare class OutputWriter {
4
4
  private readonly stderr;
5
5
  private written;
6
6
  private readonly secrets;
7
+ private readonly shownMessages;
8
+ private disposed;
9
+ private readonly taskReminder;
7
10
  constructor(stdout?: (text: string) => void, stderr?: (text: string) => void);
8
11
  registerSecret(secret: string): void;
9
12
  redact(text: string): string;
10
13
  log(text: string): void;
14
+ dispose(): void;
15
+ startTaskReminder(): void;
11
16
  /** stderr 输出过程事件,stdout 仍只输出一次最终结果。 */
12
- progress(data: Pick<CreationResult, 'sessionId' | 'messageId' | 'taskProgress'>): void;
17
+ progress(data: Pick<CreationResult, 'sessionId' | 'messageId' | 'taskProgress'> & Partial<Pick<CreationResult, 'autoTest' | 'codeReview' | 'messages'>>): void;
13
18
  write(data: unknown): void;
14
19
  fail(error: unknown): number;
15
20
  private emit;
@@ -1,12 +1,23 @@
1
1
  /** stdout 单结果输出,敏感值统一脱敏。@author xiuyu.yi */
2
2
  import { SCHEMA_VERSION } from '../config/constants.js';
3
3
  import { CliError, EXIT_CODES } from './exit-codes.js';
4
+ import { TaskReminder } from './task-reminder.js';
4
5
  const SENSITIVE_KEY = /^(?:pat|token|access[-_]?token|private[-_]?token|gatewayToken|authorization|password|secret|secrets|secretValues|apiKey|api_key|rowKey|authToken)$/i;
5
6
  export class OutputWriter {
6
7
  stdout;
7
8
  stderr;
8
9
  written = false;
9
10
  secrets = new Set();
11
+ shownMessages = new Map();
12
+ disposed = false;
13
+ taskReminder = new TaskReminder((data) => {
14
+ this.stderr(`${this.serialize({
15
+ schemaVersion: SCHEMA_VERSION,
16
+ event: 'task_reminder',
17
+ data,
18
+ instruction: '立即原样展示 data.message,将 superun.ai 链接到 data.url。同一 data.id 不重复展示;不同 ID 是每隔 5 分钟的再次提醒,文案相同也要展示。继续等待当前任务,不结束等待、不重新提交任务。',
19
+ })}\n`);
20
+ });
10
21
  constructor(stdout = (text) => process.stdout.write(text), stderr = (text) => process.stderr.write(text)) {
11
22
  this.stdout = stdout;
12
23
  this.stderr = stderr;
@@ -24,8 +35,35 @@ export class OutputWriter {
24
35
  log(text) {
25
36
  this.stderr(`${this.redact(text)}\n`);
26
37
  }
38
+ dispose() {
39
+ this.disposed = true;
40
+ this.taskReminder.stop();
41
+ }
42
+ startTaskReminder() {
43
+ if (!this.written && !this.disposed)
44
+ this.taskReminder.start();
45
+ }
27
46
  /** stderr 输出过程事件,stdout 仍只输出一次最终结果。 */
28
47
  progress(data) {
48
+ const checkName = data.codeReview ? '代码审查' : data.autoTest ? '自动测试' : undefined;
49
+ if (checkName) {
50
+ const messages = data.messages?.filter((message) => {
51
+ if (message.role !== 'assistant' || !message.text.trim())
52
+ return false;
53
+ const key = `${data.sessionId}:${message.id}`;
54
+ if (this.shownMessages.get(key) === message.text)
55
+ return false;
56
+ this.shownMessages.set(key, message.text);
57
+ return true;
58
+ }) ?? [];
59
+ if (messages.length)
60
+ this.stderr(`${this.serialize({
61
+ schemaVersion: SCHEMA_VERSION,
62
+ event: 'conversation_message',
63
+ data: { sessionId: data.sessionId, messageId: data.messageId, messages },
64
+ instruction: `及时展示这些${checkName}或修复的实际对话消息,保留发现的问题与修复前告知;同一消息 id 更新正文,不重复追加。该事件只是过程,继续等待当前命令,不表示${checkName}或修复已全部完成,不展示继续创作或上线运营菜单。`,
65
+ })}\n`);
66
+ }
29
67
  if (!data.taskProgress)
30
68
  return;
31
69
  this.stderr(`${this.serialize({
@@ -36,7 +74,9 @@ export class OutputWriter {
36
74
  messageId: data.messageId,
37
75
  taskProgress: data.taskProgress,
38
76
  },
39
- instruction: '每次查询都展示 data.taskProgress.markdown 中当前开发中的功能及步骤,即使 changed 为 false 也展示;不要从历史消息补回已完成的功能或旧步骤。支持原位更新时按卡片 id 更新,否则每次展示当前快照。不追加执行详情或工具次数。这是过程更新,继续等待当前命令的最终结果,不代表整个任务结束,不重复提交任务。',
77
+ instruction: checkName
78
+ ? `展示 data.taskProgress.markdown 中实际任务进度,不把功能步骤数当作${checkName}的问题数或通过数。及时展示 conversation_message 中发现的问题和修复说明。继续等待当前命令的最终结果,不重复提交任务、不展示操作菜单。`
79
+ : '每次查询都展示 data.taskProgress.markdown 中当前开发中的功能及步骤,即使 changed 为 false 也展示;不要从历史消息补回已完成的功能或旧步骤。支持原位更新时按卡片 id 更新,否则每次展示当前快照。不追加执行详情或工具次数。这是过程更新,继续等待当前命令的最终结果,不代表整个任务结束,不重复提交任务。',
40
80
  })}\n`);
41
81
  }
42
82
  write(data) {
@@ -60,6 +100,7 @@ export class OutputWriter {
60
100
  emit(value) {
61
101
  if (this.written)
62
102
  return;
103
+ this.dispose();
63
104
  this.written = true;
64
105
  this.stdout(`${this.serialize(value)}\n`);
65
106
  }
@@ -1 +1 @@
1
- {"version":3,"file":"writer.js","sourceRoot":"","sources":["../../src/output/writer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"writer.js","sourceRoot":"","sources":["../../src/output/writer.ts"],"names":[],"mappings":"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"}
package/dist/runner.js CHANGED
@@ -14,7 +14,11 @@ class Reexecuted extends Error {
14
14
  }
15
15
  export async function runCli(argv, options = {}) {
16
16
  const output = options.output ?? new OutputWriter(), controller = new AbortController();
17
- const interrupt = () => controller.abort();
17
+ let afterInput;
18
+ const interrupt = () => {
19
+ output.dispose();
20
+ controller.abort();
21
+ };
18
22
  process.once('SIGINT', interrupt);
19
23
  process.once('SIGTERM', interrupt);
20
24
  const checkVersion = options.checkVersion ?? versionGate;
@@ -33,6 +37,19 @@ export async function runCli(argv, options = {}) {
33
37
  },
34
38
  update: () => versionGate(argv, output, true),
35
39
  });
40
+ // 认证完成后,仅为持续等待的业务命令启用旁路提醒;不改动各命令的执行逻辑。
41
+ program.hook('preAction', (_root, command) => {
42
+ const commandOptions = command.opts();
43
+ if (!(commandOptions.wait === true || (command.name() === 'wait' && command.parent?.name() === 'chat')))
44
+ return;
45
+ // 等待用户从标准输入提供需求或回答时不计时,也不额外读取输入流。
46
+ if (commandOptions.input === '-' && !process.stdin.readableEnded) {
47
+ afterInput = () => output.startTaskReminder();
48
+ process.stdin.once('end', afterInput);
49
+ }
50
+ else
51
+ output.startTaskReminder();
52
+ });
36
53
  await program.parseAsync(argv, { from: 'user' });
37
54
  return 0;
38
55
  }
@@ -47,6 +64,9 @@ export async function runCli(argv, options = {}) {
47
64
  return output.fail(error);
48
65
  }
49
66
  finally {
67
+ output.dispose();
68
+ if (afterInput)
69
+ process.stdin.off('end', afterInput);
50
70
  process.off('SIGINT', interrupt);
51
71
  process.off('SIGTERM', interrupt);
52
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,MAAM,UAAW,SAAQ,KAAK;IACP;IAArB,YAAqB,QAAgB;QACnC,KAAK,CAAC,YAAY,CAAC,CAAC;QADD,aAAQ,GAAR,QAAQ,CAAQ;IAErC,CAAC;CACF;AAOD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAmB,EAAE,UAAyB,EAAE;IAC3E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,YAAY,EAAE,EACjD,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC3C,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACnC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,WAAW,CAAC;IACzD,MAAM,OAAO,GAAmB;QAC9B,MAAM;QACN,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI,QAAQ,EAAE;QAC5C,WAAW,EAAE,OAAO,CAAC,WAAW;KACjC,CAAC;IACF,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,EAAE;YACrC,cAAc,EAAE,KAAK,IAAI,EAAE;gBACzB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAChD,IAAI,MAAM,CAAC,UAAU;oBAAE,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;YACpE,CAAC;YACD,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC;SAC9C,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACjD,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,UAAU;YAAE,OAAO,KAAK,CAAC,QAAQ,CAAC;QACvD,IAAI,KAAK,YAAY,cAAc,EAAE,CAAC;YACpC,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC;gBAAE,OAAO,CAAC,CAAC;YACnC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,kBAAkB,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACpC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
package/dist/runtime.d.ts CHANGED
@@ -17,6 +17,7 @@ import type { GuidanceResult } from './conversation/next-actions.js';
17
17
  import { SessionWaiter } from './conversation/session-waiter.js';
18
18
  import type { WaitOptions } from './conversation/session-waiter.js';
19
19
  import { TaskProgressReader } from './conversation/task-progress-reader.js';
20
+ import { AutoTestTasks } from './conversation/auto-test-tasks.js';
20
21
  export declare class CreationRuntime {
21
22
  readonly client: ApiClient;
22
23
  readonly output: OutputWriter;
@@ -30,11 +31,13 @@ export declare class CreationRuntime {
30
31
  readonly previewVersions: PreviewVersionApi;
31
32
  readonly waiter: SessionWaiter;
32
33
  readonly taskProgress: TaskProgressReader;
34
+ readonly autoTestTasks: AutoTestTasks;
33
35
  private demoSnapshotSync?;
34
36
  constructor(client: ApiClient, output: OutputWriter);
35
37
  load(sessionId: string): Promise<SessionView>;
36
38
  choices(sessionId: string, anchor?: string, view?: SessionView): Promise<Array<Choice>>;
37
39
  inspect(view: SessionView, styleTarget?: StyleWaitTarget): Promise<CreationResult>;
40
+ resolveChecks(view: SessionView): Promise<Pick<CreationResult, 'autoTest' | 'codeReview'>>;
38
41
  state(sessionId: string): Promise<CreationResult>;
39
42
  withGuidance<T extends GuidanceResult>(result: T): T & {
40
43
  nextActions: import("./contracts/cli-output.js").NextAction[];
@@ -45,6 +48,7 @@ export declare class CreationRuntime {
45
48
  } | undefined;
46
49
  };
47
50
  wait(sessionId: string, options: WaitOptions): Promise<CreationResult>;
51
+ private approveStylePlan;
48
52
  accepted(response: JsonObject, sessionId: string | undefined, wait: boolean, options: WaitOptions): Promise<CreationResult>;
49
53
  generateStyles(sessionId: string, content: string, count: number, preReplyMessageId?: string): Promise<JsonObject>;
50
54
  selectStyle(sessionId: string, choiceId: string): Promise<JsonObject>;
package/dist/runtime.js CHANGED
@@ -14,14 +14,21 @@ import { toolId as sourceToolId, toolData } from './interactions/context.js';
14
14
  import { integrationKey } from './interactions/handlers/reply-plugin.js';
15
15
  import { isStyleSelected, projectChoices, styleBranchAnchor, styleChoiceLabel, styleWaitTarget, } from './interactions/parsers/style-selection.js';
16
16
  import { resolveState } from './conversation/state-resolver.js';
17
- import { buildNextActions, hasCompletedCreationResult } from './conversation/next-actions.js';
17
+ import { buildNextActions, hasCompletedCreationResult, pendingStylePlanApproval, } from './conversation/next-actions.js';
18
18
  import { demoGenerationStatus, findModifiedDemoPreview, isInitialDemoRound, projectDemoPreview, } from './conversation/demo-preview.js';
19
- import { currentRound, roundMessage } from './conversation/round-selector.js';
19
+ import { currentRound, roundMessage, roundContainingMessage } from './conversation/round-selector.js';
20
20
  import { SessionWaiter } from './conversation/session-waiter.js';
21
21
  import { CliError } from './output/exit-codes.js';
22
22
  import { dispatchReply } from './interactions/handlers/index.js';
23
23
  import { TaskProgressReader } from './conversation/task-progress-reader.js';
24
24
  import { submittedTaskProgress, taskProgressSnapshot } from './conversation/task-progress.js';
25
+ import { autoTestContext, AUTO_TEST_SPEC } from './conversation/auto-test.js';
26
+ import { projectText } from './conversation/text-projector.js';
27
+ import { applySnapshot } from './conversation/session-waiter.js';
28
+ import { AutoTestTasks } from './conversation/auto-test-tasks.js';
29
+ import { codeReviewContext, CODE_REVIEW_SPEC } from './conversation/code-review.js';
30
+ import { readCheckQuestionContext } from './conversation/check-question-context.js';
31
+ import { checkContextFromExtra, reportReference } from './conversation/check-context.js';
25
32
  // 仅独立演示版本等待快照同步,研发主线直接使用稳定预览地址。
26
33
  const DEMO_SNAPSHOT_SYNC_MS = 15_000;
27
34
  export class CreationRuntime {
@@ -37,6 +44,7 @@ export class CreationRuntime {
37
44
  previewVersions;
38
45
  waiter;
39
46
  taskProgress;
47
+ autoTestTasks;
40
48
  demoSnapshotSync;
41
49
  constructor(client, output) {
42
50
  this.client = client;
@@ -50,6 +58,7 @@ export class CreationRuntime {
50
58
  this.publish = new PublishApi(client);
51
59
  this.previewVersions = new PreviewVersionApi(client);
52
60
  this.taskProgress = new TaskProgressReader(client);
61
+ this.autoTestTasks = new AutoTestTasks(client);
53
62
  this.waiter = new SessionWaiter({
54
63
  conversation: this.conversation,
55
64
  command: this.command,
@@ -83,13 +92,22 @@ export class CreationRuntime {
83
92
  return projectChoices(await this.query.parallel(sessionId, preReplyMessageId), preReplyMessageId, this.client.config.endpoint);
84
93
  }
85
94
  async inspect(view, styleTarget) {
95
+ const { autoTest, codeReview } = await this.resolveChecks(view);
96
+ const check = codeReview ?? autoTest;
86
97
  const anchor = styleTarget?.preReplyMessageId ?? styleBranchAnchor(view);
87
98
  const choices = anchor ? await this.choices(view.session.sessionId, anchor, view) : [];
88
99
  view = {
89
100
  ...view,
90
101
  activeSubagentWork: await this.taskProgress.hasPendingSubagentWork(view.session.sessionId),
91
102
  };
92
- const bindings = collectInteractions(view, !styleTarget && isStyleSelected(view, choices));
103
+ const pendingReports = codeReview
104
+ ? await this.taskProgress.pendingReviewReports(view, codeReview.previousSourceMessageId)
105
+ : autoTest
106
+ ? await this.autoTestTasks.pending(view)
107
+ : [];
108
+ if (pendingReports.length)
109
+ view.activeSubagentWork = true;
110
+ const bindings = collectInteractions(view, !styleTarget && isStyleSelected(view, choices)).filter((binding) => !check || binding.interaction.kind !== 'SELECT_FEATURES');
93
111
  const processingIds = new Set();
94
112
  for (const binding of bindings) {
95
113
  if (binding.interaction.kind !== 'PLUGIN_ACTION')
@@ -102,38 +120,78 @@ export class CreationRuntime {
102
120
  processingIds.add(binding.interaction.interactionId);
103
121
  }
104
122
  const visibleBindings = bindings.filter((binding) => !processingIds.has(binding.interaction.interactionId));
105
- const taskProgress = await this.taskProgress.read(view, choices, visibleBindings.map((binding) => binding.interaction));
123
+ const taskProgress = await this.taskProgress.read(view, choices, visibleBindings.map((binding) => binding.interaction), { autoTest: !!autoTest, codeReview: !!codeReview });
106
124
  let result;
107
125
  try {
108
126
  result = resolveState({ ...view, automaticWork: processingIds.size > 0 }, visibleBindings, choices, styleTarget);
109
127
  }
110
128
  catch (error) {
111
- if (error instanceof CliError)
129
+ if (check && error instanceof CliError && error.code === 'BUSINESS_ERROR') {
130
+ result = {
131
+ state: 'FAILED',
132
+ sessionId: view.session.sessionId,
133
+ messageId: roundMessage(view, currentRound(view))?.messageId,
134
+ replyMessageId: currentRound(view)?.anchorUserMessageId,
135
+ messages: check.reportMessages,
136
+ progress: [],
137
+ interactions: [],
138
+ };
139
+ check.error = text(error.details.errorType) ?? error.message;
140
+ }
141
+ else if (error instanceof CliError)
112
142
  throw new CliError(error.code, error.message, { ...error.details, taskProgress });
113
- throw error;
143
+ else
144
+ throw error;
145
+ }
146
+ result.taskProgress = pendingReports.length
147
+ ? taskProgressSnapshot(view.session.sessionId, [...new Map([...taskProgress.tasks, ...pendingReports].map((task) => [task.id, task])).values()], taskProgress.warnings)
148
+ : taskProgress;
149
+ result.cursor = { ...result.cursor, progressRevision: result.taskProgress.revision };
150
+ if (check) {
151
+ // 审查和测试独立于开发阶段和功能推荐,不能落到普通研发完成菜单。
152
+ const questionContext = await readCheckQuestionContext(this.client, view, result.interactions);
153
+ if (questionContext.length) {
154
+ result.messages = [
155
+ ...new Map([...result.messages, ...questionContext].map((message) => [message.id, message])).values(),
156
+ ];
157
+ check.reportMessages = [
158
+ ...new Map([...check.reportMessages, ...questionContext].map((message) => [message.id, message])).values(),
159
+ ];
160
+ }
161
+ if (autoTest)
162
+ result.autoTest = autoTest;
163
+ if (codeReview)
164
+ result.codeReview = codeReview;
165
+ return result;
114
166
  }
115
- result.taskProgress = taskProgress;
116
- result.cursor = { ...result.cursor, progressRevision: taskProgress.revision };
117
167
  const roundExtra = roundMessage(view, currentRound(view))?.roundExtra ?? {};
118
168
  const planningChoiceId = text(roundExtra.cliPlanAfterStyle);
169
+ const started = enabled(roundExtra.startDevelopment);
170
+ const planApproved = enabled(roundExtra.architecturePlanApproved);
119
171
  if (planningChoiceId &&
120
172
  !styleTarget &&
121
173
  isStyleSelected(view, choices) &&
122
- isInitialDemoRound(view) &&
123
- !enabled(roundExtra.startDevelopment)) {
174
+ ((isInitialDemoRound(view) && !started) || (started && !planApproved))) {
124
175
  result.stylePlanning = { choiceId: planningChoiceId };
125
- // 只隐藏演示过渡消息;需要用户处理的问题仍按原文展示。
126
- if (!result.interactions.length) {
176
+ // 演示和规划都静默衔接,真正需要补充信息的问题仍按原文展示。
177
+ if (['RUNNING', 'QUEUED', 'COMPLETED', 'NEEDS_INPUT'].includes(result.state) &&
178
+ (!result.interactions.length || pendingStylePlanApproval(result))) {
127
179
  const choice = choices.find((item) => item.choiceId === planningChoiceId);
128
180
  result.messages = [
129
181
  {
130
182
  id: `${result.replyMessageId}:style-planning`,
131
183
  role: 'assistant',
132
184
  text: choice
133
- ? `已采用${styleChoiceLabel(choice)},正在生成研发规划,完成后请你确认。`
134
- : '风格已选定,正在生成研发规划,完成后请你确认。',
185
+ ? `已采用${styleChoiceLabel(choice)},正在整理开发功能清单。`
186
+ : '风格已选定,正在整理开发功能清单。',
135
187
  },
136
188
  ];
189
+ if (result.taskProgress) {
190
+ const roundId = currentRound(view)?.roundId;
191
+ result.taskProgress = taskProgressSnapshot(view.session.sessionId, result.taskProgress.tasks.map((task) => task.kind === 'message' && task.roundId === roundId
192
+ ? { ...task, title: '整理开发功能清单', status: 'running', detail: undefined }
193
+ : task), result.taskProgress.warnings);
194
+ }
137
195
  }
138
196
  }
139
197
  if (result.state === 'COMPLETED' && isStyleSelected(view, choices) && isInitialDemoRound(view)) {
@@ -146,8 +204,6 @@ export class CreationRuntime {
146
204
  return { ...result, state: 'RUNNING' };
147
205
  result.demo = demo;
148
206
  }
149
- const started = enabled(roundExtra.startDevelopment);
150
- const planApproved = enabled(roundExtra.architecturePlanApproved);
151
207
  const modifiedDemo = result.state === 'COMPLETED' &&
152
208
  !started &&
153
209
  !isInitialDemoRound(view) &&
@@ -183,7 +239,7 @@ export class CreationRuntime {
183
239
  ? 'COMPLETED'
184
240
  : 'DEVELOPING',
185
241
  };
186
- if (result.development.stage === 'PLAN_REVIEW')
242
+ if (result.development.stage === 'PLAN_REVIEW' && !result.stylePlanning)
187
243
  result.attachments = (await this.query.attachments(view.session.sessionId, ['README.md'])).filter((attachment) => attachment.name === 'README.md');
188
244
  const current = currentRound(view);
189
245
  const onlyFeatureSelection = result.interactions.every((item) => item.kind === 'SELECT_FEATURES');
@@ -201,17 +257,103 @@ export class CreationRuntime {
201
257
  }
202
258
  return result;
203
259
  }
260
+ async resolveChecks(view) {
261
+ const current = currentRound(view);
262
+ if (!current)
263
+ return {};
264
+ let sourceId = current.anchorUserMessageId;
265
+ let sourceRound = current;
266
+ const visited = new Set();
267
+ // 审查回投后可能再委派修复;逐层沿真实关联回溯,不能取最近的历史审查轮。
268
+ for (let depth = 0; depth < 8; depth++) {
269
+ if (visited.has(sourceId))
270
+ break;
271
+ visited.add(sourceId);
272
+ // 隐藏或并入其他轮的用户消息仍有持久化来源;单消息快照不保证返回渲染轮。
273
+ const source = sourceRound
274
+ ? undefined
275
+ : await this.query.messageReferenceSource(view.session.sessionId, sourceId);
276
+ const contexts = source
277
+ ? {
278
+ autoTest: checkContextFromExtra(source.roundExtra, AUTO_TEST_SPEC, sourceId),
279
+ codeReview: checkContextFromExtra(source.roundExtra, CODE_REVIEW_SPEC, sourceId),
280
+ }
281
+ : {
282
+ autoTest: autoTestContext(view, sourceId),
283
+ codeReview: codeReviewContext(view, sourceId),
284
+ };
285
+ if (contexts.autoTest && contexts.codeReview)
286
+ throw new CliError('PROTOCOL_ERROR', '当前结果同时关联审查与测试,无法确认来源');
287
+ const check = contexts.codeReview ?? contexts.autoTest;
288
+ if (check) {
289
+ if (check.previousSourceMessageId) {
290
+ const readPrevious = (source) => {
291
+ const round = roundContainingMessage(source, check.previousSourceMessageId);
292
+ return round
293
+ ? projectText([round]).messages.filter((item) => item.role === 'assistant')
294
+ : undefined;
295
+ };
296
+ let previous = readPrevious(view);
297
+ if (!previous) {
298
+ const snapshot = await this.conversation.snapshot(view.session.sessionId, check.previousSourceMessageId);
299
+ if (snapshot.pipeline)
300
+ previous = readPrevious(applySnapshot(view, snapshot.pipeline));
301
+ }
302
+ check.previousReportMessages = previous;
303
+ }
304
+ check.sourceMessageId = current.anchorUserMessageId;
305
+ check.reportMessages = projectText([current]).messages.filter((item) => item.role === 'assistant');
306
+ return contexts;
307
+ }
308
+ const reference = source ? reportReference(source) : undefined;
309
+ const childId = reference && 'childId' in reference
310
+ ? reference.childId
311
+ : sourceRound?.meta?.subAgentReport
312
+ ? text(sourceRound.meta.subAgentReportFrom)
313
+ : undefined;
314
+ const parentId = childId
315
+ ? await this.taskProgress.parentReplyMessageId(view.session.sessionId, childId)
316
+ : reference && 'taskId' in reference
317
+ ? await this.autoTestTasks.parentForTask(view.session.sessionId, reference.taskId)
318
+ : sourceRound
319
+ ? await this.autoTestTasks.reportParent(view, sourceId)
320
+ : undefined;
321
+ if (!parentId) {
322
+ const businessType = source?.roundExtra.business_type ??
323
+ view.pipeline.messages.find((message) => message.messageId === sourceId)?.roundExtra?.business_type;
324
+ if (reference ||
325
+ sourceRound?.meta?.subAgentReport ||
326
+ businessType === 'background_task_report' ||
327
+ businessType === 'sub_agent_report')
328
+ throw new CliError('PROTOCOL_ERROR', '暂时无法读取后台回执的来源,请重新查询当前状态');
329
+ return {};
330
+ }
331
+ sourceId = parentId;
332
+ // 合并轮内可能有多次委派;此时按精确消息回溯,不能用宿主轮覆盖它的来源。
333
+ sourceRound = view.pipeline.render?.rounds.find((round) => round.anchorUserMessageId === parentId);
334
+ }
335
+ throw new CliError('PROTOCOL_ERROR', '后台回执的来源链存在循环或层数过多,无法确认当前结果');
336
+ }
204
337
  async state(sessionId) {
205
338
  return this.withGuidance(await this.inspect(await this.load(sessionId)));
206
339
  }
207
340
  withGuidance(result) {
208
- const output = result.taskProgress
209
- ? { ...result, cursor: { ...result.cursor, progressRevision: result.taskProgress.revision } }
341
+ // 对外仍表示正在整理清单;内部保留真实确认交互,由 wait 校验后继续。
342
+ const visible = pendingStylePlanApproval(result)
343
+ ? {
344
+ ...result,
345
+ state: 'RUNNING',
346
+ interactions: [],
347
+ development: result.development ? { ...result.development, stage: 'PLANNING' } : undefined,
348
+ }
210
349
  : result;
350
+ const output = visible.taskProgress
351
+ ? { ...visible, cursor: { ...visible.cursor, progressRevision: visible.taskProgress.revision } }
352
+ : visible;
211
353
  const completed = {
212
354
  ...output,
213
- ...(hasCompletedCreationResult(result)
214
- ? { toolUsage: this.taskProgress.completionSummary(result.sessionId, result.messageId) }
355
+ ...(hasCompletedCreationResult(visible)
356
+ ? { toolUsage: this.taskProgress.completionSummary(visible.sessionId, visible.messageId) }
215
357
  : {}),
216
358
  };
217
359
  return { ...completed, nextActions: buildNextActions(completed, this.client.config) };
@@ -221,17 +363,27 @@ export class CreationRuntime {
221
363
  const remaining = () => deadline === undefined ? undefined : Math.max(0, (deadline - Date.now()) / 1000);
222
364
  let pending = options;
223
365
  let result = await this.waiter.wait(sessionId, { ...pending, timeout: remaining() });
224
- while (result.stylePlanning && result.state === 'COMPLETED') {
366
+ while (result.stylePlanning && (result.state === 'COMPLETED' || pendingStylePlanApproval(result))) {
225
367
  if (deadline !== undefined && Date.now() >= deadline)
226
- return this.withGuidance({ ...result, state: 'RUNNING', waitTimedOut: true });
368
+ return this.withGuidance({
369
+ ...result,
370
+ state: pendingStylePlanApproval(result) ? result.state : 'RUNNING',
371
+ waitTimedOut: true,
372
+ });
227
373
  const choiceId = result.stylePlanning.choiceId;
228
- result = await this.viewDemo(sessionId, { timeout: remaining() }, choiceId);
229
- if (!result.stylePlanning || result.state !== 'COMPLETED')
230
- break;
231
- if (deadline !== undefined && Date.now() >= deadline)
232
- return this.withGuidance({ ...result, state: 'RUNNING', waitTimedOut: true });
233
- // 查看演示与进入研发各自先核对当前轮,失败时保留真实错误,不重发写请求。
234
- const response = await this.startDevelopment(sessionId, choiceId);
374
+ const approval = pendingStylePlanApproval(result);
375
+ let response;
376
+ if (approval)
377
+ response = await this.approveStylePlan(sessionId, choiceId, approval.interactionId);
378
+ else {
379
+ result = await this.viewDemo(sessionId, { timeout: remaining() }, choiceId);
380
+ if (!result.stylePlanning || result.state !== 'COMPLETED')
381
+ break;
382
+ if (deadline !== undefined && Date.now() >= deadline)
383
+ return this.withGuidance({ ...result, state: 'RUNNING', waitTimedOut: true });
384
+ // 查看演示与进入研发各自先核对当前轮,失败时保留真实错误,不重发写请求。
385
+ response = await this.startDevelopment(sessionId, choiceId);
386
+ }
235
387
  pending = {
236
388
  messageId: text(response.messageId),
237
389
  requiredMessageId: text(response.messageId) ?? text(response.replyMessageId),
@@ -239,9 +391,22 @@ export class CreationRuntime {
239
391
  };
240
392
  result = await this.waiter.wait(sessionId, { ...pending, timeout: remaining() });
241
393
  }
242
- // 最终只展示规划或当前问题,不混入静默过渡阶段的演示消息。
394
+ // 最终展示功能清单或实际业务问题,静默步骤的消息不带到下一阶段。
243
395
  return this.withGuidance(result);
244
396
  }
397
+ async approveStylePlan(sessionId, choiceId, interactionId) {
398
+ const latest = await this.inspect(await this.load(sessionId));
399
+ if (latest.stylePlanning?.choiceId !== choiceId ||
400
+ pendingStylePlanApproval(latest)?.interactionId !== interactionId)
401
+ throw new CliError('STALE_INTERACTION', '研发规划或当前问题已变化,请重新查询当前状态', { sessionId });
402
+ return this.command.chat({
403
+ sessionId,
404
+ content: '确认研发规划',
405
+ businessParams: { business_type: 'architecture_plan_approve' },
406
+ // 自动衔接只消费本次初始规划,不影响后续独立的规划调整与确认。
407
+ excludedInheritedRoundExtraKeys: ['cliPlanAfterStyle'],
408
+ });
409
+ }
245
410
  async accepted(response, sessionId, wait, options) {
246
411
  const id = text(response.sessionId) ?? sessionId;
247
412
  if (!id)
@@ -258,7 +423,10 @@ export class CreationRuntime {
258
423
  try {
259
424
  // 接受回执也展示同一项目的其他活跃任务;仅查进度,不推进任务或读取功能清单。
260
425
  const view = { ...(await this.conversation.recently(id)), extra: {} };
261
- const snapshot = await this.taskProgress.read(view, await this.choices(id, undefined, view));
426
+ const snapshot = await this.taskProgress.read(view, await this.choices(id, undefined, view), [], {
427
+ autoTest: ['test', 'repair'].includes(String(response.autoTestPhase)),
428
+ codeReview: ['review', 'repair'].includes(String(response.codeReviewPhase)),
429
+ });
262
430
  const responseIds = [text(response.messageId), text(response.replyMessageId)].filter(Boolean);
263
431
  const round = view.pipeline.render?.rounds.find((item) => responseIds.includes(item.anchorUserMessageId) ||
264
432
  [...item.userItems, ...item.agentItems].some((content) => responseIds.includes(content.source?.messageId)));
@@ -285,6 +453,24 @@ export class CreationRuntime {
285
453
  ? { stylePlanning: { choiceId: String(response.planningChoiceId) } }
286
454
  : {}),
287
455
  cursor: { branchAnchor: text(response.preReplyMessageId) },
456
+ ...(['test', 'repair'].includes(String(response.autoTestPhase))
457
+ ? {
458
+ autoTest: {
459
+ phase: response.autoTestPhase,
460
+ sourceMessageId: text(response.replyMessageId) ?? text(response.messageId) ?? '',
461
+ reportMessages: [],
462
+ },
463
+ }
464
+ : {}),
465
+ ...(['review', 'repair'].includes(String(response.codeReviewPhase))
466
+ ? {
467
+ codeReview: {
468
+ phase: response.codeReviewPhase,
469
+ sourceMessageId: text(response.replyMessageId) ?? text(response.messageId) ?? '',
470
+ reportMessages: [],
471
+ },
472
+ }
473
+ : {}),
288
474
  });
289
475
  }
290
476
  async generateStyles(sessionId, content, count, preReplyMessageId) {
@@ -349,7 +535,7 @@ export class CreationRuntime {
349
535
  retryable: false,
350
536
  });
351
537
  }
352
- const planningNotice = `已采用${styleChoiceLabel(choice)},正在生成研发规划,完成后请你确认。`;
538
+ const planningNotice = `已采用${styleChoiceLabel(choice)},正在整理开发功能清单。`;
353
539
  this.output.log(planningNotice);
354
540
  return { ...response, planningChoiceId: choice.choiceId, planningNotice };
355
541
  }