shennian 0.2.63 → 0.2.64

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 (38) hide show
  1. package/dist/src/agents/adapter.d.ts +6 -0
  2. package/dist/src/agents/claude.js +2 -2
  3. package/dist/src/agents/codex-utils.d.ts +24 -0
  4. package/dist/src/agents/codex-utils.js +195 -0
  5. package/dist/src/agents/codex.d.ts +3 -4
  6. package/dist/src/agents/codex.js +63 -198
  7. package/dist/src/agents/command-spec.d.ts +3 -0
  8. package/dist/src/agents/command-spec.js +3 -0
  9. package/dist/src/agents/opencode.js +2 -2
  10. package/dist/src/agents/pi-context.d.ts +40 -0
  11. package/dist/src/agents/pi-context.js +177 -0
  12. package/dist/src/agents/pi.d.ts +1 -7
  13. package/dist/src/agents/pi.js +39 -186
  14. package/dist/src/agents/platform-instructions.js +3 -0
  15. package/dist/src/commands/daemon-windows.d.ts +16 -0
  16. package/dist/src/commands/daemon-windows.js +99 -0
  17. package/dist/src/commands/daemon.d.ts +1 -8
  18. package/dist/src/commands/daemon.js +2 -96
  19. package/dist/src/manager/prompt.d.ts +1 -1
  20. package/dist/src/manager/prompt.js +4 -1
  21. package/dist/src/native-fusion/opencode-parser.d.ts +29 -0
  22. package/dist/src/native-fusion/opencode-parser.js +121 -0
  23. package/dist/src/native-fusion/parser-common.d.ts +24 -0
  24. package/dist/src/native-fusion/parser-common.js +264 -0
  25. package/dist/src/native-fusion/parsers.d.ts +1 -29
  26. package/dist/src/native-fusion/parsers.js +33 -383
  27. package/dist/src/region.js +4 -3
  28. package/dist/src/session/handlers/chat.js +14 -4
  29. package/dist/src/session/handlers/control.js +1 -3
  30. package/dist/src/session/handlers/fs.d.ts +2 -0
  31. package/dist/src/session/handlers/fs.js +260 -16
  32. package/dist/src/session/handlers/skills.d.ts +5 -0
  33. package/dist/src/session/handlers/skills.js +50 -0
  34. package/dist/src/session/manager.js +17 -1
  35. package/dist/src/session/types.d.ts +10 -0
  36. package/dist/src/skills/registry.d.ts +12 -0
  37. package/dist/src/skills/registry.js +128 -0
  38. package/package.json +1 -1
@@ -10,6 +10,12 @@ export type AgentEvent = {
10
10
  args?: Record<string, unknown>;
11
11
  result?: string;
12
12
  message?: string;
13
+ approval?: {
14
+ title?: string;
15
+ content?: string;
16
+ source?: string;
17
+ actionHint?: string;
18
+ };
13
19
  usage?: {
14
20
  inputTokens: number;
15
21
  outputTokens: number;
@@ -1,7 +1,7 @@
1
1
  import { createInterface } from 'node:readline';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { AgentAdapter, registerAgent } from './adapter.js';
4
- import { resolveBuiltinCommand, spawnResolvedCommand, spawnResolvedCommandSync } from './command-spec.js';
4
+ import { resolveBuiltinCommand, spawnAgentCommand, spawnResolvedCommandSync } from './command-spec.js';
5
5
  import { buildAgentProcessEnv } from '../agent-env.js';
6
6
  import { buildPlatformInstructions } from './platform-instructions.js';
7
7
  export function normalizeClaudeModelId(modelId) {
@@ -128,7 +128,7 @@ export class ClaudeAdapter extends AgentAdapter {
128
128
  this.emit('error', new Error('Command "claude" not found. Is Claude Code CLI installed?'));
129
129
  return;
130
130
  }
131
- const proc = spawnResolvedCommand(spec, args, {
131
+ const proc = spawnAgentCommand(spec, args, {
132
132
  cwd: this.workDir ?? undefined,
133
133
  stdio: ['ignore', 'pipe', 'pipe'],
134
134
  env: buildAgentProcessEnv(this.extraEnv),
@@ -0,0 +1,24 @@
1
+ export declare function makeThreadTitle(text: string): string;
2
+ export declare function isCodexCollabAgentToolName(name: string): boolean;
3
+ export declare function isCodexCollabAgentItem(item: Record<string, unknown>): boolean;
4
+ export declare function isCodexLegacyCollabAgentItem(item: {
5
+ type?: string;
6
+ tool?: string;
7
+ }): boolean;
8
+ export declare function normalizeTitleText(text: string): string;
9
+ export declare function normalizeCodexModelId(modelId?: string | null): string | undefined;
10
+ export declare function normalizeCodexReasoningEffort(reasoningEffort?: string | null): string | undefined;
11
+ export declare function isMissingCodexRolloutError(error: unknown): boolean;
12
+ export declare function isCodexUnsupportedEffortError(error: unknown): boolean;
13
+ export declare function extractAppServerErrorMessage(params: Record<string, unknown>): string;
14
+ export declare function isTransientCodexErrorMessage(message: string): boolean;
15
+ export declare function formatTransientCodexStatus(message: string): string;
16
+ export declare function formatCodexErrorMessage(message: string): string;
17
+ export declare function stripGitDirectiveArtifacts(text: string): string;
18
+ export declare function safeStringify(value: unknown): string;
19
+ export declare function normalizeCodexStderr(stderr: string): string;
20
+ export declare function looksLikeMissingNodeOnWindows(text: string): boolean;
21
+ export declare function normalizeTerminalText(text: string): string;
22
+ export declare function looksLikeCodexInteractiveAuthPrompt(text: string): boolean;
23
+ export declare function isIgnorableCodexStderrLine(line: string): boolean;
24
+ export declare function looksLikeFatalCodexStderr(stderr: string): boolean;
@@ -0,0 +1,195 @@
1
+ // @arch docs/architecture/cli/agent-adapters.md
2
+ // @test src/__tests__/codex-adapter.test.ts
3
+ export function makeThreadTitle(text) {
4
+ const firstLine = normalizeTitleText(text).split('\n').find((line) => line.trim()) ?? 'Shennian';
5
+ const title = firstLine.trim().slice(0, 80);
6
+ return title || 'Shennian';
7
+ }
8
+ const CODEX_COLLAB_AGENT_TOOL_NAMES = new Set([
9
+ 'spawnAgent',
10
+ 'sendInput',
11
+ 'resumeAgent',
12
+ 'wait',
13
+ 'closeAgent',
14
+ 'waitAgent',
15
+ ]);
16
+ export function isCodexCollabAgentToolName(name) {
17
+ return CODEX_COLLAB_AGENT_TOOL_NAMES.has(name);
18
+ }
19
+ export function isCodexCollabAgentItem(item) {
20
+ if (item.type === 'collabAgentToolCall')
21
+ return true;
22
+ if (item.type === 'collabAgentMonitor')
23
+ return true;
24
+ const tool = typeof item.tool === 'string' ? item.tool : '';
25
+ if (tool && isCodexCollabAgentToolName(tool))
26
+ return true;
27
+ return typeof item.senderThreadId === 'string' && Array.isArray(item.receiverThreadIds);
28
+ }
29
+ export function isCodexLegacyCollabAgentItem(item) {
30
+ if (item.type === 'collab_agent_tool_call' || item.type === 'collabAgentToolCall')
31
+ return true;
32
+ if (item.type === 'collab_agent_monitor' || item.type === 'collabAgentMonitor')
33
+ return true;
34
+ return !!item.tool && isCodexCollabAgentToolName(item.tool);
35
+ }
36
+ export function normalizeTitleText(text) {
37
+ return text
38
+ .replace(/!\[[^\]]*]\([^)]+\)/g, '')
39
+ .replace(/\s+/g, ' ')
40
+ .trim();
41
+ }
42
+ export function normalizeCodexModelId(modelId) {
43
+ const trimmed = modelId?.trim();
44
+ if (!trimmed)
45
+ return undefined;
46
+ return trimmed.toLowerCase() === 'openai' ? undefined : trimmed;
47
+ }
48
+ export function normalizeCodexReasoningEffort(reasoningEffort) {
49
+ const trimmed = reasoningEffort?.trim();
50
+ return trimmed || undefined;
51
+ }
52
+ export function isMissingCodexRolloutError(error) {
53
+ const message = error instanceof Error ? error.message : String(error ?? '');
54
+ return /\bno rollout found for thread id\b/i.test(message);
55
+ }
56
+ export function isCodexUnsupportedEffortError(error) {
57
+ const message = error instanceof Error ? error.message : String(error ?? '');
58
+ return (/\bunknown field\b.*\beffort\b/i.test(message) ||
59
+ /\binvalid.*\beffort\b/i.test(message) ||
60
+ /\bunsupported.*\beffort\b/i.test(message) ||
61
+ /\breasoning effort\b/i.test(message));
62
+ }
63
+ export function extractAppServerErrorMessage(params) {
64
+ if (typeof params.message === 'string' && params.message.trim())
65
+ return params.message.trim();
66
+ const error = params.error;
67
+ if (typeof error === 'string' && error.trim())
68
+ return error.trim();
69
+ if (typeof error === 'object' && error !== null) {
70
+ const message = error.message;
71
+ if (typeof message === 'string' && message.trim())
72
+ return message.trim();
73
+ }
74
+ return 'codex app-server error';
75
+ }
76
+ export function isTransientCodexErrorMessage(message) {
77
+ return /\bReconnecting\.\.\.\s*\d+\/\d+/i.test(message);
78
+ }
79
+ export function formatTransientCodexStatus(message) {
80
+ const match = message.match(/\bReconnecting\.\.\.\s*(\d+\/\d+)/i);
81
+ if (!match)
82
+ return '';
83
+ return `Codex API 暂时不可用,正在重试 ${match[1]}...`;
84
+ }
85
+ export function formatCodexErrorMessage(message) {
86
+ const statusMatch = message.match(/unexpected status\s+(\d+)\s+([^:,]+)(?::\s*([^,]+))?(?:,\s*url:\s*([^,\s]+))?(?:,\s*request id:\s*([^\s,]+))?/i);
87
+ if (!statusMatch)
88
+ return message;
89
+ const [, status, statusText, detail, url, requestId] = statusMatch;
90
+ const parts = [
91
+ `Codex API 返回 ${status} ${statusText.trim()}`,
92
+ detail?.trim() ? `:${detail.trim()}` : '',
93
+ url ? `\n上游地址:${url}` : '',
94
+ requestId ? `\nrequest id:${requestId}` : '',
95
+ ];
96
+ return parts.join('');
97
+ }
98
+ const GIT_DIRECTIVE_LINE_RE = /^\s*::git-[a-z-]+\{[^\n]*\}\s*$/gm;
99
+ export function stripGitDirectiveArtifacts(text) {
100
+ if (!text)
101
+ return '';
102
+ const normalized = text.replace(/\r\n/g, '\n');
103
+ const hadDirective = /(^|\n)\s*::git-[a-z-]+\{[^\n]*\}\s*(?=\n|$)/.test(normalized);
104
+ if (!hadDirective)
105
+ return normalized;
106
+ const stripped = normalized.replace(GIT_DIRECTIVE_LINE_RE, '');
107
+ if (!stripped.trim())
108
+ return '';
109
+ return stripped.replace(/[ \t]+\n/g, '\n').replace(/\n+$/g, '');
110
+ }
111
+ export function safeStringify(value) {
112
+ if (value == null)
113
+ return '';
114
+ if (typeof value === 'string')
115
+ return value;
116
+ try {
117
+ return JSON.stringify(value);
118
+ }
119
+ catch {
120
+ return String(value);
121
+ }
122
+ }
123
+ export function normalizeCodexStderr(stderr) {
124
+ const normalized = stderr
125
+ .split(/\r?\n/)
126
+ .map((line) => line.trim())
127
+ .filter(Boolean)
128
+ .filter((line) => !isIgnorableCodexStderrLine(line))
129
+ .join('\n');
130
+ if (looksLikeMissingNodeOnWindows(normalized)) {
131
+ return 'Codex 启动失败:系统找不到 node。请重新安装 Node.js,或把 Node.js 安装目录(通常是 C:\\Program Files\\nodejs)加入 Windows PATH 后重启神念。';
132
+ }
133
+ return normalized;
134
+ }
135
+ export function looksLikeMissingNodeOnWindows(text) {
136
+ if (!text)
137
+ return false;
138
+ return (/["']?node["']?/i.test(text) &&
139
+ (/not recognized as an internal or external command/i.test(text) ||
140
+ text.includes('不是内部或外部命令') ||
141
+ text.includes('不是可运行的程序') ||
142
+ text.includes('��������') ||
143
+ text.includes('����')));
144
+ }
145
+ export function normalizeTerminalText(text) {
146
+ const escape = String.fromCharCode(27);
147
+ const bell = String.fromCharCode(7);
148
+ const csiPattern = new RegExp(`${escape}\\[[0-?]*[ -/]*[@-~]`, 'g');
149
+ const oscPattern = new RegExp(`${escape}\\][^${bell}]*(?:${bell}|${escape}\\\\)`, 'g');
150
+ return text
151
+ .replace(csiPattern, ' ')
152
+ .replace(oscPattern, ' ')
153
+ .split('')
154
+ .map((char) => {
155
+ const code = char.charCodeAt(0);
156
+ return (code <= 8 || code === 11 || code === 12 || (code >= 14 && code <= 31) || code === 127) ? ' ' : char;
157
+ })
158
+ .join('')
159
+ .replace(/\s+/g, ' ')
160
+ .trim();
161
+ }
162
+ export function looksLikeCodexInteractiveAuthPrompt(text) {
163
+ if (!text)
164
+ return false;
165
+ const normalized = text.toLowerCase();
166
+ return (normalized.includes('welcome to codex') ||
167
+ normalized.includes('sign in with chatgpt') ||
168
+ normalized.includes('provide your own api key') ||
169
+ normalized.includes('press enter to continue'));
170
+ }
171
+ export function isIgnorableCodexStderrLine(line) {
172
+ return [
173
+ /WARNING: proceeding, even though we could not update PATH:/i,
174
+ /Reading additional input from stdin\.\.\./i,
175
+ /failed to record rollout items: failed to queue rollout items: channel closed/i,
176
+ ].some((pattern) => pattern.test(line));
177
+ }
178
+ export function looksLikeFatalCodexStderr(stderr) {
179
+ if (!stderr)
180
+ return false;
181
+ return [
182
+ /\bunauthorized\b/i,
183
+ /\bforbidden\b/i,
184
+ /\bpermission denied\b/i,
185
+ /\brate limit\b/i,
186
+ /\binsufficient quota\b/i,
187
+ /\bapi key\b/i,
188
+ /\bauth/i,
189
+ /\bnetwork\b/i,
190
+ /\btimeout\b/i,
191
+ /\bnot found\b/i,
192
+ /\berror\b/i,
193
+ /\bfailed\b/i,
194
+ ].some((pattern) => pattern.test(stderr));
195
+ }
@@ -1,5 +1,6 @@
1
1
  import { AgentAdapter } from './adapter.js';
2
2
  import type { ChatAttachmentMeta, ExternalChannelSessionStatus } from '@shennian/wire';
3
+ export { isCodexUnsupportedEffortError, isMissingCodexRolloutError, normalizeCodexModelId, normalizeCodexReasoningEffort, } from './codex-utils.js';
3
4
  export declare class CodexAdapter extends AgentAdapter {
4
5
  private readonly options;
5
6
  readonly type: "codex";
@@ -54,6 +55,8 @@ export declare class CodexAdapter extends AgentAdapter {
54
55
  private handleCodexItemLine;
55
56
  private handleStreamEvent;
56
57
  private emitEvent;
58
+ private isApprovalLikeName;
59
+ private emitApprovalPending;
57
60
  private killProcess;
58
61
  private rejectAllPending;
59
62
  private handleProcessClose;
@@ -66,7 +69,3 @@ export declare class CodexAdapter extends AgentAdapter {
66
69
  private scheduleProcessClose;
67
70
  private clearForceCloseTimer;
68
71
  }
69
- export declare function normalizeCodexModelId(modelId?: string | null): string | undefined;
70
- export declare function normalizeCodexReasoningEffort(reasoningEffort?: string | null): string | undefined;
71
- export declare function isMissingCodexRolloutError(error: unknown): boolean;
72
- export declare function isCodexUnsupportedEffortError(error: unknown): boolean;
@@ -3,9 +3,11 @@
3
3
  import { createInterface } from 'node:readline';
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { AgentAdapter, registerAgent } from './adapter.js';
6
- import { resolveBuiltinCommand, spawnResolvedCommand } from './command-spec.js';
6
+ import { resolveBuiltinCommand, spawnAgentCommand } from './command-spec.js';
7
7
  import { buildAgentProcessEnv } from '../agent-env.js';
8
8
  import { ensurePlatformInstructionsFile } from './platform-instructions.js';
9
+ import { extractAppServerErrorMessage, formatCodexErrorMessage, formatTransientCodexStatus, isCodexCollabAgentItem, isCodexCollabAgentToolName, isCodexLegacyCollabAgentItem, isCodexUnsupportedEffortError, isMissingCodexRolloutError, isTransientCodexErrorMessage, looksLikeCodexInteractiveAuthPrompt, looksLikeFatalCodexStderr, makeThreadTitle, normalizeCodexModelId, normalizeCodexReasoningEffort, normalizeCodexStderr, normalizeTerminalText, safeStringify, stripGitDirectiveArtifacts, } from './codex-utils.js';
10
+ export { isCodexUnsupportedEffortError, isMissingCodexRolloutError, normalizeCodexModelId, normalizeCodexReasoningEffort, } from './codex-utils.js';
9
11
  function buildCodexTextInput(text, attachments) {
10
12
  const images = attachments
11
13
  ?.filter((attachment) => attachment.kind === 'image')
@@ -109,7 +111,7 @@ export class CodexAdapter extends AgentAdapter {
109
111
  this.emit('error', new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));
110
112
  return;
111
113
  }
112
- const proc = spawnResolvedCommand(spec, args, {
114
+ const proc = spawnAgentCommand(spec, args, {
113
115
  cwd: this.workDir ?? undefined,
114
116
  stdio: ['ignore', 'pipe', 'pipe'],
115
117
  env: buildAgentProcessEnv(this.extraEnv),
@@ -161,7 +163,7 @@ export class CodexAdapter extends AgentAdapter {
161
163
  if (modelInstructionsFile) {
162
164
  args.push('-c', `model_instructions_file=${JSON.stringify(modelInstructionsFile)}`);
163
165
  }
164
- const proc = spawnResolvedCommand(spec, args, {
166
+ const proc = spawnAgentCommand(spec, args, {
165
167
  cwd: this.workDir ?? undefined,
166
168
  stdio: ['pipe', 'pipe', 'pipe'],
167
169
  env: buildAgentProcessEnv({ NO_COLOR: '1', ...this.extraEnv }),
@@ -449,6 +451,15 @@ export class CodexAdapter extends AgentAdapter {
449
451
  this.emitText(summary, 'block');
450
452
  break;
451
453
  }
454
+ case 'approvalRequest':
455
+ case 'approval_request':
456
+ case 'permissionRequest':
457
+ case 'permission_request':
458
+ case 'userInputRequired':
459
+ case 'user_input_required': {
460
+ this.emitApprovalPending(item);
461
+ break;
462
+ }
452
463
  }
453
464
  }
454
465
  handleAppServerUsage(params) {
@@ -551,6 +562,17 @@ export class CodexAdapter extends AgentAdapter {
551
562
  }
552
563
  break;
553
564
  }
565
+ case 'approval_request':
566
+ case 'approvalRequest':
567
+ case 'permission_request':
568
+ case 'permissionRequest':
569
+ case 'user_input_required':
570
+ case 'userInputRequired': {
571
+ if (obj.type === 'item.started' || obj.type === 'item.completed') {
572
+ this.emitApprovalPending(item);
573
+ }
574
+ break;
575
+ }
554
576
  case 'error': {
555
577
  if (obj.type === 'item.completed' && item.message) {
556
578
  this.emitEvent({ state: 'error', message: item.message });
@@ -624,6 +646,10 @@ export class CodexAdapter extends AgentAdapter {
624
646
  }
625
647
  case 'function_call':
626
648
  case 'tool_call': {
649
+ if (this.isApprovalLikeName(obj.name)) {
650
+ this.emitApprovalPending(obj);
651
+ break;
652
+ }
627
653
  if (obj.name && isCodexCollabAgentToolName(obj.name))
628
654
  return;
629
655
  this.emitEvent({
@@ -644,6 +670,15 @@ export class CodexAdapter extends AgentAdapter {
644
670
  });
645
671
  break;
646
672
  }
673
+ case 'approval_request':
674
+ case 'approvalRequest':
675
+ case 'permission_request':
676
+ case 'permissionRequest':
677
+ case 'user_input_required':
678
+ case 'userInputRequired': {
679
+ this.emitApprovalPending(obj);
680
+ break;
681
+ }
647
682
  case 'error': {
648
683
  break;
649
684
  }
@@ -668,6 +703,31 @@ export class CodexAdapter extends AgentAdapter {
668
703
  };
669
704
  this.emit('agentEvent', event);
670
705
  }
706
+ isApprovalLikeName(name) {
707
+ if (typeof name !== 'string')
708
+ return false;
709
+ return /approval|permission|confirm|user[_-]?input|request[_-]?user/i.test(name);
710
+ }
711
+ emitApprovalPending(source) {
712
+ const command = typeof source.command === 'string' ? source.command : '';
713
+ const title = typeof source.title === 'string' && source.title.trim()
714
+ ? source.title
715
+ : '需要用户确认';
716
+ const rawContent = typeof source.prompt === 'string' ? source.prompt
717
+ : typeof source.reason === 'string' ? source.reason
718
+ : typeof source.message === 'string' ? source.message
719
+ : command ? `Codex 请求执行命令:${command}`
720
+ : 'Codex 正在等待用户确认后继续。';
721
+ this.emitEvent({
722
+ state: 'approval-pending',
723
+ approval: {
724
+ title,
725
+ content: rawContent,
726
+ source: 'codex',
727
+ actionHint: '当前神念暂不支持远程确认,请回到运行 Codex 的电脑/终端完成确认。',
728
+ },
729
+ });
730
+ }
671
731
  async killProcess() {
672
732
  const proc = this.process;
673
733
  this.appServerReady = null;
@@ -788,199 +848,4 @@ export class CodexAdapter extends AgentAdapter {
788
848
  this.forceCloseTimer = null;
789
849
  }
790
850
  }
791
- function makeThreadTitle(text) {
792
- const firstLine = normalizeTitleText(text).split('\n').find((line) => line.trim()) ?? 'Shennian';
793
- const title = firstLine.trim().slice(0, 80);
794
- return title || 'Shennian';
795
- }
796
- const CODEX_COLLAB_AGENT_TOOL_NAMES = new Set([
797
- 'spawnAgent',
798
- 'sendInput',
799
- 'resumeAgent',
800
- 'wait',
801
- 'closeAgent',
802
- 'waitAgent',
803
- ]);
804
- function isCodexCollabAgentToolName(name) {
805
- return CODEX_COLLAB_AGENT_TOOL_NAMES.has(name);
806
- }
807
- function isCodexCollabAgentItem(item) {
808
- if (item.type === 'collabAgentToolCall')
809
- return true;
810
- if (item.type === 'collabAgentMonitor')
811
- return true;
812
- const tool = typeof item.tool === 'string' ? item.tool : '';
813
- if (tool && isCodexCollabAgentToolName(tool))
814
- return true;
815
- return typeof item.senderThreadId === 'string' && Array.isArray(item.receiverThreadIds);
816
- }
817
- function isCodexLegacyCollabAgentItem(item) {
818
- if (item.type === 'collab_agent_tool_call' || item.type === 'collabAgentToolCall')
819
- return true;
820
- if (item.type === 'collab_agent_monitor' || item.type === 'collabAgentMonitor')
821
- return true;
822
- return !!item.tool && isCodexCollabAgentToolName(item.tool);
823
- }
824
- function normalizeTitleText(text) {
825
- return text
826
- .replace(/!\[[^\]]*]\([^)]+\)/g, '')
827
- .replace(/\s+/g, ' ')
828
- .trim();
829
- }
830
- export function normalizeCodexModelId(modelId) {
831
- const trimmed = modelId?.trim();
832
- if (!trimmed)
833
- return undefined;
834
- return trimmed.toLowerCase() === 'openai' ? undefined : trimmed;
835
- }
836
- export function normalizeCodexReasoningEffort(reasoningEffort) {
837
- const trimmed = reasoningEffort?.trim();
838
- return trimmed || undefined;
839
- }
840
- export function isMissingCodexRolloutError(error) {
841
- const message = error instanceof Error ? error.message : String(error ?? '');
842
- return /\bno rollout found for thread id\b/i.test(message);
843
- }
844
- export function isCodexUnsupportedEffortError(error) {
845
- const message = error instanceof Error ? error.message : String(error ?? '');
846
- return (/\bunknown field\b.*\beffort\b/i.test(message) ||
847
- /\binvalid.*\beffort\b/i.test(message) ||
848
- /\bunsupported.*\beffort\b/i.test(message) ||
849
- /\breasoning effort\b/i.test(message));
850
- }
851
- function extractAppServerErrorMessage(params) {
852
- if (typeof params.message === 'string' && params.message.trim())
853
- return params.message.trim();
854
- const error = params.error;
855
- if (typeof error === 'string' && error.trim())
856
- return error.trim();
857
- if (typeof error === 'object' && error !== null) {
858
- const message = error.message;
859
- if (typeof message === 'string' && message.trim())
860
- return message.trim();
861
- }
862
- return 'codex app-server error';
863
- }
864
- function isTransientCodexErrorMessage(message) {
865
- return /\bReconnecting\.\.\.\s*\d+\/\d+/i.test(message);
866
- }
867
- function formatTransientCodexStatus(message) {
868
- const match = message.match(/\bReconnecting\.\.\.\s*(\d+\/\d+)/i);
869
- if (!match)
870
- return '';
871
- return `Codex API 暂时不可用,正在重试 ${match[1]}...`;
872
- }
873
- function formatCodexErrorMessage(message) {
874
- const statusMatch = message.match(/unexpected status\s+(\d+)\s+([^:,]+)(?::\s*([^,]+))?(?:,\s*url:\s*([^,\s]+))?(?:,\s*request id:\s*([^\s,]+))?/i);
875
- if (!statusMatch)
876
- return message;
877
- const [, status, statusText, detail, url, requestId] = statusMatch;
878
- const parts = [
879
- `Codex API 返回 ${status} ${statusText.trim()}`,
880
- detail?.trim() ? `:${detail.trim()}` : '',
881
- url ? `\n上游地址:${url}` : '',
882
- requestId ? `\nrequest id:${requestId}` : '',
883
- ];
884
- return parts.join('');
885
- }
886
- const GIT_DIRECTIVE_LINE_RE = /^\s*::git-[a-z-]+\{[^\n]*\}\s*$/gm;
887
- function stripGitDirectiveArtifacts(text) {
888
- if (!text)
889
- return '';
890
- const normalized = text.replace(/\r\n/g, '\n');
891
- const hadDirective = /(^|\n)\s*::git-[a-z-]+\{[^\n]*\}\s*(?=\n|$)/.test(normalized);
892
- const stripped = normalized
893
- .replace(GIT_DIRECTIVE_LINE_RE, '')
894
- .replace(/\n{3,}/g, '\n\n');
895
- if (!stripped.trim())
896
- return '';
897
- return hadDirective
898
- ? stripped.replace(/[ \t]+\n/g, '\n').replace(/\n+$/g, '')
899
- : stripped;
900
- }
901
- function safeStringify(value) {
902
- if (value == null)
903
- return '';
904
- if (typeof value === 'string')
905
- return value;
906
- try {
907
- return JSON.stringify(value);
908
- }
909
- catch {
910
- return String(value);
911
- }
912
- }
913
- function normalizeCodexStderr(stderr) {
914
- const normalized = stderr
915
- .split(/\r?\n/)
916
- .map((line) => line.trim())
917
- .filter(Boolean)
918
- .filter((line) => !isIgnorableCodexStderrLine(line))
919
- .join('\n');
920
- if (looksLikeMissingNodeOnWindows(normalized)) {
921
- return 'Codex 启动失败:系统找不到 node。请重新安装 Node.js,或把 Node.js 安装目录(通常是 C:\\Program Files\\nodejs)加入 Windows PATH 后重启神念。';
922
- }
923
- return normalized;
924
- }
925
- function looksLikeMissingNodeOnWindows(text) {
926
- if (!text)
927
- return false;
928
- return (/["']?node["']?/i.test(text) &&
929
- (/not recognized as an internal or external command/i.test(text) ||
930
- text.includes('不是内部或外部命令') ||
931
- text.includes('不是可运行的程序') ||
932
- text.includes('��������') ||
933
- text.includes('����')));
934
- }
935
- function normalizeTerminalText(text) {
936
- const escape = String.fromCharCode(27);
937
- const bell = String.fromCharCode(7);
938
- const csiPattern = new RegExp(`${escape}\\[[0-?]*[ -/]*[@-~]`, 'g');
939
- const oscPattern = new RegExp(`${escape}\\][^${bell}]*(?:${bell}|${escape}\\\\)`, 'g');
940
- return text
941
- .replace(csiPattern, ' ')
942
- .replace(oscPattern, ' ')
943
- .split('')
944
- .map((char) => {
945
- const code = char.charCodeAt(0);
946
- return (code <= 8 || code === 11 || code === 12 || (code >= 14 && code <= 31) || code === 127) ? ' ' : char;
947
- })
948
- .join('')
949
- .replace(/\s+/g, ' ')
950
- .trim();
951
- }
952
- function looksLikeCodexInteractiveAuthPrompt(text) {
953
- if (!text)
954
- return false;
955
- const normalized = text.toLowerCase();
956
- return (normalized.includes('welcome to codex') ||
957
- normalized.includes('sign in with chatgpt') ||
958
- normalized.includes('provide your own api key') ||
959
- normalized.includes('press enter to continue'));
960
- }
961
- function isIgnorableCodexStderrLine(line) {
962
- return [
963
- /WARNING: proceeding, even though we could not update PATH:/i,
964
- /Reading additional input from stdin\.\.\./i,
965
- /failed to record rollout items: failed to queue rollout items: channel closed/i,
966
- ].some((pattern) => pattern.test(line));
967
- }
968
- function looksLikeFatalCodexStderr(stderr) {
969
- if (!stderr)
970
- return false;
971
- return [
972
- /\bunauthorized\b/i,
973
- /\bforbidden\b/i,
974
- /\bpermission denied\b/i,
975
- /\brate limit\b/i,
976
- /\binsufficient quota\b/i,
977
- /\bapi key\b/i,
978
- /\bauth/i,
979
- /\bnetwork\b/i,
980
- /\btimeout\b/i,
981
- /\bnot found\b/i,
982
- /\berror\b/i,
983
- /\bfailed\b/i,
984
- ].some((pattern) => pattern.test(stderr));
985
- }
986
851
  registerAgent('codex', () => new CodexAdapter());
@@ -24,6 +24,9 @@ export declare function buildLaunchSpec(spec: ResolvedCommandSpec, runtimeArgs:
24
24
  export declare function spawnResolvedCommand(spec: ResolvedCommandSpec, runtimeArgs: string[], options?: Omit<SpawnOptions, 'cwd'> & {
25
25
  cwd?: string;
26
26
  }): ChildProcess;
27
+ export declare function spawnAgentCommand(spec: ResolvedCommandSpec, runtimeArgs: string[], options?: Omit<SpawnOptions, 'cwd'> & {
28
+ cwd?: string;
29
+ }): ChildProcess;
27
30
  export declare function spawnResolvedCommandSync(spec: ResolvedCommandSpec, runtimeArgs: string[], options?: Omit<SpawnSyncOptions, 'cwd'> & {
28
31
  cwd?: string;
29
32
  }): import("child_process").SpawnSyncReturns<string | NonSharedBuffer>;
@@ -279,6 +279,9 @@ export function spawnResolvedCommand(spec, runtimeArgs, options = {}) {
279
279
  : {}),
280
280
  });
281
281
  }
282
+ export function spawnAgentCommand(spec, runtimeArgs, options = {}) {
283
+ return spawnResolvedCommand(spec, runtimeArgs, options);
284
+ }
282
285
  export function spawnResolvedCommandSync(spec, runtimeArgs, options = {}) {
283
286
  const launch = buildLaunchSpec(spec, runtimeArgs, options.cwd);
284
287
  return spawnSync(launch.command, launch.args, {
@@ -3,7 +3,7 @@
3
3
  import { createInterface } from 'node:readline';
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { AgentAdapter, registerAgent } from './adapter.js';
6
- import { resolveBuiltinCommand, spawnResolvedCommand } from './command-spec.js';
6
+ import { resolveBuiltinCommand, spawnAgentCommand } from './command-spec.js';
7
7
  import { buildAgentProcessEnv } from '../agent-env.js';
8
8
  function usageFromTokens(tokens) {
9
9
  if (!tokens)
@@ -101,7 +101,7 @@ export class OpenCodeAdapter extends AgentAdapter {
101
101
  this.emit('error', new Error('Command "opencode" not found. Install it with "npm i -g opencode-ai@latest".'));
102
102
  return;
103
103
  }
104
- const proc = spawnResolvedCommand(spec, args, {
104
+ const proc = spawnAgentCommand(spec, args, {
105
105
  cwd: this.workDir ?? undefined,
106
106
  stdio: ['ignore', 'pipe', 'pipe'],
107
107
  env: buildAgentProcessEnv({ NO_COLOR: '1' }),
@@ -0,0 +1,40 @@
1
+ import type { AgentMessage } from '@mariozechner/pi-agent-core';
2
+ import type { Model } from '@mariozechner/pi-ai';
3
+ export declare const PI_DEFAULT_MODEL_ID = "qwen3.6-plus";
4
+ type ShellCommandSpec = {
5
+ file: string;
6
+ args: string[];
7
+ shell: 'bash' | 'powershell';
8
+ };
9
+ export declare function buildShellCommandSpec(command: string, platform?: NodeJS.Platform): ShellCommandSpec;
10
+ export declare function createPiModel(modelId?: string): Model<'openai-completions'>;
11
+ export declare const SYSTEM_PROMPT = "\u4F60\u662F\u795E\u5FF5\u5185\u7F6E\u7F16\u7A0B\u52A9\u624B\uFF0C\u8FD0\u884C\u5728\u7528\u6237\u672C\u5730\u673A\u5668\u4E0A\u3002\n\u4F60\u53EF\u4EE5\u8BFB\u5199\u6587\u4EF6\u3001\u6267\u884C shell \u547D\u4EE4\u3001\u5E2E\u52A9\u7528\u6237\u5B8C\u6210\u7F16\u7A0B\u548C\u7CFB\u7EDF\u7BA1\u7406\u4EFB\u52A1\u3002\n\u5DE5\u4F5C\u76EE\u5F55\u5DF2\u8BBE\u7F6E\uFF0C\u64CD\u4F5C\u6587\u4EF6\u65F6\u4F7F\u7528\u76F8\u5BF9\u8DEF\u5F84\u6216\u7EDD\u5BF9\u8DEF\u5F84\u5747\u53EF\u3002\n\u5F53\u524D shell \u4F1A\u968F\u64CD\u4F5C\u7CFB\u7EDF\u9009\u62E9\uFF1AWindows \u4F7F\u7528 PowerShell\uFF0CmacOS/Linux \u4F7F\u7528 bash\u3002Windows \u4E0B\u9700\u8981\u771F\u5B9E curl \u65F6\u4F7F\u7528 curl.exe\uFF0C\u907F\u514D PowerShell \u7684 curl \u522B\u540D\u3002\n\u4FDD\u6301\u56DE\u590D\u7B80\u6D01\u3001\u51C6\u786E\uFF0C\u4E2D\u6587\u56DE\u590D\u3002";
12
+ export declare const CONTEXT_TOKEN_THRESHOLD = 90000;
13
+ export declare const KEEP_RECENT_MESSAGES = 6;
14
+ export declare const SUMMARY_FILENAME = "summary.json";
15
+ export declare const SNAPSHOT_FILENAME = "snapshot.json";
16
+ export declare const MESSAGES_FILENAME = "messages.jsonl";
17
+ export declare const LEGACY_SUMMARY_FILENAME = "pi-context.json";
18
+ export type PersistedSummary = {
19
+ version: 1;
20
+ summary: string | null;
21
+ summarizedCount: number;
22
+ updatedAt: number;
23
+ };
24
+ export type PersistedSnapshot = {
25
+ version: 1;
26
+ sessionId: string;
27
+ workDir: string;
28
+ summary: string | null;
29
+ summarizedCount: number;
30
+ messages: AgentMessage[];
31
+ updatedAt: number;
32
+ };
33
+ export declare function estimateTokens(messages: AgentMessage[]): number;
34
+ export declare function messagesToText(messages: AgentMessage[]): string;
35
+ export declare function cloneMessages(messages: AgentMessage[]): AgentMessage[];
36
+ export declare function getSessionDir(sessionId: string): string;
37
+ export declare function buildRollingSummary(cachedSummary: string | null, messages: AgentMessage[]): string | null;
38
+ export declare function longestCommonPrefixLength(left: AgentMessage[], right: AgentMessage[]): number;
39
+ export declare function requestProxySummary(proxyUrl: string, authToken: string, prompt: string): Promise<string | null>;
40
+ export {};