qlogicagent 2.17.10 → 2.18.2
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/dist/agent.js +25 -21
- package/dist/cli.js +1 -1
- package/dist/index.js +451 -435
- package/dist/protocol.js +1 -1
- package/dist/skills/mcp/astraclaw-native-mcp-server.js +6 -5
- package/dist/types/agent/tool-loop/compression-pipeline.d.ts +2 -0
- package/dist/types/agent/tool-loop/loop-helpers.d.ts +1 -0
- package/dist/types/agent/tool-loop/tool-failure-policy.d.ts +13 -1
- package/dist/types/agent/tool-loop.d.ts +14 -0
- package/dist/types/agent/types.d.ts +7 -0
- package/dist/types/cli/handlers/agents-handler.d.ts +9 -0
- package/dist/types/cli/handlers/config-handler.d.ts +5 -0
- package/dist/types/cli/handlers/files-handler.d.ts +7 -0
- package/dist/types/cli/handlers/session-handler.d.ts +43 -0
- package/dist/types/cli/handlers/turn-handler.d.ts +12 -0
- package/dist/types/cli/handlers/workflow-handler.d.ts +1 -0
- package/dist/types/cli/permission-rule-persistence.d.ts +16 -0
- package/dist/types/cli/stdio-rpc-handler-hosts.d.ts +4 -0
- package/dist/types/cli/stdio-server.d.ts +16 -1
- package/dist/types/cli/turn-core.d.ts +2 -0
- package/dist/types/orchestration/agent-instance.d.ts +11 -1
- package/dist/types/orchestration/agent-roster.d.ts +12 -0
- package/dist/types/orchestration/dag-scheduler.d.ts +3 -0
- package/dist/types/orchestration/delegation-coordinator.d.ts +16 -1
- package/dist/types/orchestration/goal-loop-coordinator.d.ts +4 -1
- package/dist/types/orchestration/goal-mode-adapters.d.ts +3 -1
- package/dist/types/orchestration/goal-run-types.d.ts +2 -0
- package/dist/types/orchestration/product-budget.d.ts +13 -0
- package/dist/types/orchestration/product-persistence.d.ts +4 -0
- package/dist/types/orchestration/product-planner.d.ts +7 -2
- package/dist/types/orchestration/product-run-coordinator.d.ts +1 -0
- package/dist/types/orchestration/workflow/budget-permission-gate.d.ts +9 -0
- package/dist/types/orchestration/workflow/node-schema.d.ts +4 -1
- package/dist/types/orchestration/workflow/workflow-trigger.d.ts +5 -1
- package/dist/types/protocol/wire/acp-agent-management.d.ts +16 -0
- package/dist/types/protocol/wire/agent-events.d.ts +2 -2
- package/dist/types/protocol/wire/agent-methods.d.ts +3 -0
- package/dist/types/protocol/wire/gateway-rpc.d.ts +1 -0
- package/dist/types/runtime/execution/streaming-tool-executor.d.ts +2 -0
- package/dist/types/runtime/execution/tool-result-storage.d.ts +6 -4
- package/dist/types/runtime/infra/acp-detector.d.ts +35 -5
- package/dist/types/runtime/infra/acp-protocol-adapter.d.ts +23 -1
- package/dist/types/runtime/infra/agent-process.d.ts +15 -0
- package/dist/types/runtime/infra/external-agent-pool.d.ts +12 -0
- package/dist/types/runtime/infra/llmrouter-catalog.d.ts +26 -6
- package/dist/types/runtime/infra/model-registry.d.ts +43 -2
- package/dist/types/runtime/infra/user-dirs.d.ts +21 -0
- package/dist/types/runtime/permission-model.d.ts +1 -1
- package/dist/types/runtime/ports/agent-runtime-ports.d.ts +4 -1
- package/dist/types/runtime/ports/permission-contracts.d.ts +5 -2
- package/dist/types/runtime/prompt/environment-context.d.ts +10 -0
- package/dist/types/runtime/prompt/fresh-workspace-evidence.d.ts +1 -4
- package/dist/types/runtime/prompt/identity-section.d.ts +1 -1
- package/dist/types/runtime/session/inbound-upload-persistence.d.ts +8 -0
- package/dist/types/runtime/session/native/claude-session-source.d.ts +23 -0
- package/dist/types/runtime/session/native/codex-index.d.ts +21 -0
- package/dist/types/runtime/session/native/codex-session-source.d.ts +31 -0
- package/dist/types/runtime/session/native/cwd-normalize.d.ts +53 -0
- package/dist/types/runtime/session/native/ephemeral-rollout-sweep.d.ts +13 -0
- package/dist/types/runtime/session/native/native-cwd-agents.d.ts +17 -0
- package/dist/types/runtime/session/native/native-session-source.d.ts +89 -0
- package/dist/types/runtime/session/native/native-title.d.ts +1 -0
- package/dist/types/runtime/session/native/register-builtin-sources.d.ts +1 -0
- package/dist/types/runtime/session/session-resource-key.d.ts +28 -0
- package/dist/types/runtime/session/session-transcript-store.d.ts +2 -0
- package/dist/types/runtime/session/session-types.d.ts +37 -0
- package/dist/types/skills/permissions/hook-runner.d.ts +9 -0
- package/dist/types/skills/permissions/settings-watcher.d.ts +7 -0
- package/dist/types/transport/acp-server.d.ts +6 -0
- package/package.json +4 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ModelCapabilityProfile, ModelEntry, ModelRegistry, ReasoningMode } from "./model-registry.js";
|
|
1
|
+
import type { ModelCapabilityProfile, ModelEntry, ModelPurpose, ModelRegistry, ReasoningMode } from "./model-registry.js";
|
|
2
2
|
import type { ConfigPort } from "../ports/index.js";
|
|
3
3
|
export declare const LLMROUTER_CATALOG_UNAVAILABLE_MESSAGE = "\u65E0\u6CD5\u8FDE\u63A5 llmrouter \u6A21\u578B\u76EE\u5F55";
|
|
4
4
|
export declare const LLMROUTER_PROVIDER_ID = "llmrouter";
|
|
@@ -97,14 +97,34 @@ export declare function refreshRegistryFromLlmrouterApiKey(registry: ModelRegist
|
|
|
97
97
|
forceCatalog?: boolean;
|
|
98
98
|
}): Promise<ModelEntry[]>;
|
|
99
99
|
export declare function resetLlmrouterCatalogClientCache(): void;
|
|
100
|
+
export interface DefaultBindingsResult {
|
|
101
|
+
/** 本次真正写入的绑定(用途 → modelId)。 */
|
|
102
|
+
applied: Array<{
|
|
103
|
+
purpose: ModelPurpose;
|
|
104
|
+
modelId: string;
|
|
105
|
+
}>;
|
|
106
|
+
/** 跳过的用途 + 原因(已有配置不覆盖 / 该用途 llmrouter 无可用模型)。 */
|
|
107
|
+
skipped: Array<{
|
|
108
|
+
purpose: ModelPurpose;
|
|
109
|
+
reason: "already-bound" | "no-model";
|
|
110
|
+
}>;
|
|
111
|
+
}
|
|
100
112
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
113
|
+
* 零配置默认绑定:给一组「安全」用途补上 llmrouter 默认模型 —— **只补空白**,已有 binding 的用途一律
|
|
114
|
+
* 不动(尊重用户/老配置)。每用途三级挑选:①服务端推荐(模型 capability_profile.default_for 声明本
|
|
115
|
+
* 用途)优先 → ②回退硬编码偏好子串(DEFAULT_BINDING_PREFERENCES,向后兼容) → ③再回退该用途第一个
|
|
116
|
+
* 可用模型;都没有则不绑。**「哪些用途该默认绑」是客户端产品边界(安全白名单),「每用途选哪个模型」
|
|
117
|
+
* 交服务端** —— 两层职责分离,llmrouter 换模型客户端零发版跟随。
|
|
104
118
|
*
|
|
105
|
-
*
|
|
119
|
+
* 触发场景:①agent 启动(设备账号推理 key 注入后,stdio-server) ②刷新目录后(settings refresh)
|
|
120
|
+
* ③手动添加 llmrouter key。"有没有 llmrouter 可用"由 listModels 结果隐含——refresh 成功(key 有效)
|
|
121
|
+
* 才有 llmrouter 模型条目;无则每个用途 no-model 跳过、不绑。key 不在此注册(ModelRegistry 取 llmrouter
|
|
122
|
+
* key 时回退到 QLOGIC_LLMROUTER_INFERENCE_KEY env 或用户 key)。调用方需先刷新过目录。
|
|
106
123
|
*/
|
|
107
|
-
export declare function
|
|
124
|
+
export declare function ensureLlmrouterDefaultBindings(registry: ModelRegistry, opts?: {
|
|
125
|
+
requireInferenceKey?: boolean;
|
|
126
|
+
configPort?: ConfigPort;
|
|
127
|
+
}): DefaultBindingsResult;
|
|
108
128
|
export declare function toModelEntry(model: LlmrouterCatalogModel): ModelEntry | null;
|
|
109
129
|
export declare function toModelEntries(model: LlmrouterCatalogModel): ModelEntry[];
|
|
110
130
|
export {};
|
|
@@ -29,6 +29,13 @@ export interface ModelEntry {
|
|
|
29
29
|
routeIntents: ModelPurpose[];
|
|
30
30
|
baseUrl?: string;
|
|
31
31
|
enabled: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Purposes this model has been individually turned OFF for. Absent/empty = on for all of its
|
|
34
|
+
* routeIntents (still subject to the master `enabled`). Lets one model serve several purposes
|
|
35
|
+
* with independent on/off switches, so disabling it under one purpose group never strands the
|
|
36
|
+
* bindings that use it for another.
|
|
37
|
+
*/
|
|
38
|
+
disabledPurposes?: ModelPurpose[];
|
|
32
39
|
nativeModelId?: string;
|
|
33
40
|
transport?: string;
|
|
34
41
|
contextWindow?: number;
|
|
@@ -40,6 +47,11 @@ export interface ModelEntry {
|
|
|
40
47
|
pricing?: Record<string, unknown>;
|
|
41
48
|
/** Thinking-strength control class for the UI (see ReasoningMode). */
|
|
42
49
|
reasoningMode?: ReasoningMode;
|
|
50
|
+
/**
|
|
51
|
+
* 归一后的「服务端推荐默认用途」(来自 capabilityProfile.default_for)。零配置默认绑定优先选
|
|
52
|
+
* defaultForIntents 命中本用途的模型,读不到则回退硬编码偏好。空/缺 → 无服务端推荐。
|
|
53
|
+
*/
|
|
54
|
+
defaultForIntents?: ModelPurpose[];
|
|
43
55
|
}
|
|
44
56
|
export interface ModelCapabilityProfile {
|
|
45
57
|
tasks?: string[];
|
|
@@ -55,6 +67,12 @@ export interface ModelCapabilityProfile {
|
|
|
55
67
|
requires_confirmation?: boolean;
|
|
56
68
|
default_selectable?: boolean;
|
|
57
69
|
};
|
|
70
|
+
/**
|
|
71
|
+
* 服务端声明:该模型是这些 route_intent 的「推荐默认」(零配置绑定优先读它)。原始 intent 字符串
|
|
72
|
+
* (由 llmrouter 归一为规范名);客户端过滤到已知 ModelPurpose 得 defaultForIntents。空/缺 → 无服务端
|
|
73
|
+
* 推荐 → 客户端回退硬编码偏好(向后兼容:llmrouter 未部署 default_for 时行为与现状完全一致)。
|
|
74
|
+
*/
|
|
75
|
+
default_for?: string[];
|
|
58
76
|
}
|
|
59
77
|
export type PurposeBindings = Partial<Record<ModelPurpose, string>>;
|
|
60
78
|
export interface ModelRegistryConfig {
|
|
@@ -62,6 +80,7 @@ export interface ModelRegistryConfig {
|
|
|
62
80
|
models: Array<{
|
|
63
81
|
id: string;
|
|
64
82
|
enabled?: boolean;
|
|
83
|
+
disabledPurposes?: ModelPurpose[];
|
|
65
84
|
}>;
|
|
66
85
|
bindings: PurposeBindings;
|
|
67
86
|
tunables?: Record<string, unknown>;
|
|
@@ -84,6 +103,7 @@ export declare class ModelRegistry {
|
|
|
84
103
|
private catalogProviders;
|
|
85
104
|
private models;
|
|
86
105
|
private modelEnabledOverrides;
|
|
106
|
+
private modelPurposeDisabled;
|
|
87
107
|
private bindings;
|
|
88
108
|
private injectedKeyStates;
|
|
89
109
|
private settingsPath;
|
|
@@ -106,6 +126,12 @@ export declare class ModelRegistry {
|
|
|
106
126
|
*/
|
|
107
127
|
acceptsInputModality(modality: string, purpose?: ModelPurpose): boolean;
|
|
108
128
|
isAvailable(purpose: ModelPurpose): boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Whether `id` is enabled for `purpose`. The master `enabled` flag is the global gate; on top of it
|
|
131
|
+
* a single purpose can be switched off (modelPurposeDisabled) without touching the model's other
|
|
132
|
+
* purposes. This is what keeps "disable in 文本生成" from stranding the 图像理解 binding.
|
|
133
|
+
*/
|
|
134
|
+
isEnabledForPurpose(id: string, purpose: ModelPurpose): boolean;
|
|
109
135
|
resolveModelForPurpose(purpose: ModelPurpose): string | null;
|
|
110
136
|
addProvider(providerId: string, opts?: {
|
|
111
137
|
baseUrl?: string;
|
|
@@ -132,8 +158,23 @@ export declare class ModelRegistry {
|
|
|
132
158
|
replaceCatalogModels(entries: ModelEntry[]): void;
|
|
133
159
|
replaceProviderModels(providerId: string, entries: ModelEntry[]): void;
|
|
134
160
|
removeModel(id: string): void;
|
|
135
|
-
|
|
136
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Enable a model. With `purpose`, enables it for just that purpose (clearing its per-purpose OFF
|
|
163
|
+
* switch); if the whole model was off, it flips on but keeps its OTHER purposes off so a single
|
|
164
|
+
* "turn on imageUnderstanding" never silently re-enables textGeneration too. Without `purpose`,
|
|
165
|
+
* enables the model globally (on for all of its purposes).
|
|
166
|
+
*/
|
|
167
|
+
enableModel(id: string, purpose?: ModelPurpose): void;
|
|
168
|
+
/**
|
|
169
|
+
* Disable a model. With `purpose`, turns it off for just that purpose; the master flag only flips
|
|
170
|
+
* off once EVERY one of its routeIntents is off, so purpose-agnostic consumers (default-model
|
|
171
|
+
* pickers) still see the model while any purpose stays live. Without `purpose`, disables globally.
|
|
172
|
+
*/
|
|
173
|
+
disableModel(id: string, purpose?: ModelPurpose): void;
|
|
174
|
+
private isMasterEnabled;
|
|
175
|
+
private setMasterEnabled;
|
|
176
|
+
private setPurposeDisabled;
|
|
177
|
+
private purposeDisabledArray;
|
|
137
178
|
listModels(filter?: {
|
|
138
179
|
purpose?: ModelPurpose;
|
|
139
180
|
provider?: string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 跨平台获取用户常见目录(桌面/下载/文档)的真实绝对路径,供 environment-context 注入给 agent——
|
|
3
|
+
* 让「读桌面/下载的文件」这类桌面端最常见请求有一个确定的绝对路径锚点,而不是相对会话工作区瞎找。
|
|
4
|
+
*
|
|
5
|
+
* 各平台权威来源(都不能简单 homedir + "/Desktop" 硬拼——OneDrive/自定义重定向会漏):
|
|
6
|
+
* - Windows: 注册表 User Shell Folders(含 OneDrive/自定义重定向);值是 REG_EXPAND_SZ,含 %VAR% 需展开。
|
|
7
|
+
* 展开用 node 的 process.env(编码正确),所以即便用户名是中文,%USERPROFILE% 模板展开后仍准;
|
|
8
|
+
* 只有用户把桌面手动重定向到硬编码中文路径时,reg 的 OEM 输出可能乱码 → existsSync 兜底回退。
|
|
9
|
+
* - macOS: ~/Desktop 等固定英文路径(Finder 里的本地化名只是显示,真实路径是英文)。
|
|
10
|
+
* - Linux: XDG user-dirs(~/.config/user-dirs.dirs 的 XDG_DESKTOP_DIR 等),回退 ~/Desktop。
|
|
11
|
+
* 一次性探测 + 缓存(目录在进程生命周期内不变)。任何一项失败都回退到 homedir 拼接,绝不抛。
|
|
12
|
+
*/
|
|
13
|
+
export interface UserDirs {
|
|
14
|
+
desktop: string;
|
|
15
|
+
downloads: string;
|
|
16
|
+
documents: string;
|
|
17
|
+
}
|
|
18
|
+
/** 用户桌面/下载/文档的真实绝对路径(缓存)。失败项回退 homedir 拼接,绝不抛。 */
|
|
19
|
+
export declare function getUserDirs(): UserDirs;
|
|
20
|
+
/** 仅测试用:清缓存,让不同平台/环境的断言各自重探。 */
|
|
21
|
+
export declare function resetUserDirsCache(): void;
|
|
@@ -3,7 +3,7 @@ export interface PermissionPromptOption {
|
|
|
3
3
|
optionId: string;
|
|
4
4
|
name: string;
|
|
5
5
|
kind: "allow_once" | "allow_always" | "reject_once" | "reject_always";
|
|
6
|
-
scope?: "once" | "directory";
|
|
6
|
+
scope?: "once" | "directory" | "directory_persistent";
|
|
7
7
|
}
|
|
8
8
|
export interface PermissionModeDescriptor {
|
|
9
9
|
id: PermissionMode;
|
|
@@ -39,6 +39,8 @@ export interface StreamingToolExecutorPortConfig {
|
|
|
39
39
|
signal?: AbortSignal;
|
|
40
40
|
maxConcurrentTools?: number;
|
|
41
41
|
maxExecutionMs?: number;
|
|
42
|
+
/** Turn workspace/cwd — routes oversized tool-result spillover to the session's reachable data dir. */
|
|
43
|
+
projectRoot?: string;
|
|
42
44
|
}
|
|
43
45
|
export interface ContextCompressionEnginePort {
|
|
44
46
|
compressAsync(messages: RuntimeCompressibleMessage[], budget: number, context: {
|
|
@@ -52,9 +54,10 @@ export interface ToolLoopRuntimePorts {
|
|
|
52
54
|
role: string;
|
|
53
55
|
content?: string | unknown;
|
|
54
56
|
tool_call_id?: string;
|
|
55
|
-
}>, state: unknown, sessionId: string): Promise<{
|
|
57
|
+
}>, state: unknown, sessionId: string, projectRoot: string | undefined): Promise<{
|
|
56
58
|
messages: unknown[];
|
|
57
59
|
newlyReplacedCount: number;
|
|
60
|
+
newlyPersistedBytes?: number;
|
|
58
61
|
}>;
|
|
59
62
|
getActiveContextCompressionEngine(): ContextCompressionEnginePort | null;
|
|
60
63
|
compressMessages(messages: RuntimeCompressibleMessage[], options: {
|
|
@@ -105,8 +105,11 @@ export interface ApprovalResponse {
|
|
|
105
105
|
approvalId: string;
|
|
106
106
|
decision: "allow" | "deny";
|
|
107
107
|
updatedInput?: Record<string, unknown>;
|
|
108
|
-
/**
|
|
109
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Approval scope chosen by the user: just this call, the whole directory for
|
|
110
|
+
* this session, or the whole directory persisted across restarts.
|
|
111
|
+
*/
|
|
112
|
+
scope?: "once" | "directory" | "directory_persistent";
|
|
110
113
|
}
|
|
111
114
|
export interface PermissionRuleEnginePort {
|
|
112
115
|
getMode(): PermissionMode;
|
|
@@ -32,6 +32,16 @@ export interface ShellInfo {
|
|
|
32
32
|
psEdition?: "5.1" | "7+";
|
|
33
33
|
isGitBashOnWindows?: boolean;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Dynamic, platform-specific shell guidance lines appended to `# Environment`.
|
|
37
|
+
*
|
|
38
|
+
* When `shellInfo` is present it reflects the REAL active shell (edition +
|
|
39
|
+
* Git-Bash-on-Windows), so the advice matches what commands will actually run
|
|
40
|
+
* against. When absent, we fall back to the env-guessed `shellType` and keep the
|
|
41
|
+
* old one-line PowerShell/CMD note (never crashes, never over-claims an edition
|
|
42
|
+
* we don't know).
|
|
43
|
+
*/
|
|
44
|
+
export declare function shellGuidanceLines(shellInfo: ShellInfo | undefined, shellType: ShellType): string[];
|
|
35
45
|
/**
|
|
36
46
|
* Create a system prompt section that provides environment context.
|
|
37
47
|
* Memoized — computed once per session (environment doesn't change mid-session).
|
|
@@ -11,14 +11,11 @@ export interface ToolNameLike {
|
|
|
11
11
|
}
|
|
12
12
|
export interface FreshWorkspaceEvidencePolicy {
|
|
13
13
|
requiresFreshTool: boolean;
|
|
14
|
-
reason?: "file-read" | "
|
|
14
|
+
reason?: "file-read" | "memory-mutation" | "media-generation" | "directory-list" | "content-search" | "shell" | "web";
|
|
15
15
|
mediaKind?: "image" | "tts" | "music";
|
|
16
|
-
workflowKind?: "artifact-index";
|
|
17
16
|
allowedToolNames: string[];
|
|
18
17
|
}
|
|
19
18
|
export declare function getFreshWorkspaceEvidencePolicy(messages: readonly PromptMessageLike[] | string): FreshWorkspaceEvidencePolicy;
|
|
20
|
-
/** 显式构建/创建类请求(做个游戏/网站/app…)。命中 → 第一回合就强制工具调用。 */
|
|
21
|
-
export declare function looksLikeBuildIntent(messages: readonly PromptMessageLike[] | string): boolean;
|
|
22
19
|
export declare function isLightweightChatTurn(messages: readonly PromptMessageLike[] | string): boolean;
|
|
23
20
|
export declare function isExplicitNoToolTurn(messages: readonly PromptMessageLike[] | string): boolean;
|
|
24
21
|
/**
|
|
@@ -15,6 +15,14 @@ import type { ChatMessage } from "../../protocol/wire/index.js";
|
|
|
15
15
|
export interface PersistedUpload {
|
|
16
16
|
url: string;
|
|
17
17
|
localPath?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Whether the bytes were actually written to durable storage. `true` on success, `false` when a
|
|
20
|
+
* transport upload was found but persisting it FAILED (fetch error / non-ok / write error) — the
|
|
21
|
+
* returned `url` is then the short-lived transport URL that dies with the turn. `undefined` means
|
|
22
|
+
* the input was not a transport upload to begin with (already-persisted / remote / file://), which
|
|
23
|
+
* is not a failure. Honesty over silence: callers use `false` to warn instead of pretending it saved.
|
|
24
|
+
*/
|
|
25
|
+
persisted?: boolean;
|
|
18
26
|
}
|
|
19
27
|
/** 同 persistInboundUploadUrl,但返回持久化后的本地绝对路径(供模型直接 read/exec)。 */
|
|
20
28
|
export declare function persistInboundUploadUrlDetailed(url: string, projectId: string, sessionId: string): Promise<PersistedUpload>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { NativeSessionEntry, NativeSessionRef, NativeSessionSource, TranscriptMessage } from "./native-session-source.js";
|
|
2
|
+
/**
|
|
3
|
+
* Native session source for Claude Code (claude CLI). Reads directly from
|
|
4
|
+
* `~/.claude/projects/<claudeProjectDirName(cwd)>/<sessionUuid>.jsonl` — the same on-disk format
|
|
5
|
+
* Claude Code itself writes, so this is read-only and never touched by qlogicagent.
|
|
6
|
+
*
|
|
7
|
+
* `isSidechain: true` events are subagent side-chains, not part of the user-visible conversation,
|
|
8
|
+
* and are filtered out of both list() (message counting / title / lastActiveAt) and
|
|
9
|
+
* loadTranscript() (transcript body).
|
|
10
|
+
*/
|
|
11
|
+
export declare class ClaudeSessionSource implements NativeSessionSource {
|
|
12
|
+
private readonly home;
|
|
13
|
+
readonly agentId = "claude";
|
|
14
|
+
readonly kind: "claude";
|
|
15
|
+
/** `home` is injectable so tests can point at a temp directory instead of the real user home. */
|
|
16
|
+
constructor(home?: string);
|
|
17
|
+
list(cwd: string, opts?: {
|
|
18
|
+
limit?: number;
|
|
19
|
+
}): Promise<NativeSessionEntry[]>;
|
|
20
|
+
/** A claude native session is exactly the file `<encoded-cwd>/<sessionUuid>.jsonl` — a single stat. */
|
|
21
|
+
has(sessionId: string, cwd: string): Promise<boolean>;
|
|
22
|
+
loadTranscript(ref: NativeSessionRef): Promise<TranscriptMessage[]>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { NativeSessionEntry } from "./native-session-source.js";
|
|
2
|
+
/**
|
|
3
|
+
* Lists codex-native sessions belonging to `normalizedTargetCwd` (already run through
|
|
4
|
+
* {@link normalizeCwd} by the caller), sorted by `lastActiveAt` descending.
|
|
5
|
+
*
|
|
6
|
+
* Synchronous by design — built entirely on sync fs calls, since it's a Cut 3 building block; a
|
|
7
|
+
* future CodexSessionSource adapter (mirroring claude-session-source.ts's shape) would wrap this in
|
|
8
|
+
* the async `NativeSessionSource.list()` contract.
|
|
9
|
+
*
|
|
10
|
+
* `messageCount` is always 0: unlike claude's one-line-per-message transcript format, a codex
|
|
11
|
+
* rollout interleaves many event types per turn, so computing an exact count means fully parsing
|
|
12
|
+
* every candidate rollout's entire event stream — deliberately out of scope here. Revisit if/when a
|
|
13
|
+
* caller actually needs an exact count.
|
|
14
|
+
*
|
|
15
|
+
* Never throws: a missing/unreadable codexHome, sessions dir, session_index.jsonl, or cache file all
|
|
16
|
+
* degrade to "as if empty" rather than propagating an error.
|
|
17
|
+
*/
|
|
18
|
+
export declare function listCodexSessionsByCwd(normalizedTargetCwd: string, opts?: {
|
|
19
|
+
codexHome?: string;
|
|
20
|
+
cacheDir?: string;
|
|
21
|
+
}): NativeSessionEntry[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { NativeSessionEntry, NativeSessionRef, NativeSessionSource, TranscriptMessage } from "./native-session-source.js";
|
|
2
|
+
/**
|
|
3
|
+
* Native session source for Codex (codex CLI / codex_cli_rs) — Cut 3 Task 3.2.
|
|
4
|
+
*
|
|
5
|
+
* `list()` delegates entirely to {@link listCodexSessionsByCwd} (Task 3.1), which already handles
|
|
6
|
+
* cwd filtering, title/lastActiveAt joining against session_index.jsonl, and mtime-keyed caching;
|
|
7
|
+
* this adapter only adds the async `NativeSessionSource.list()` contract and cwd normalization on
|
|
8
|
+
* top of that synchronous building block (mirroring ClaudeSessionSource's shape).
|
|
9
|
+
*
|
|
10
|
+
* `loadTranscript()` reads a single rollout file and keeps only `event_msg` lines whose payload is
|
|
11
|
+
* `user_message` or `agent_message` with `phase: "final_answer"` — see {@link CodexEventMsgLine}'s
|
|
12
|
+
* doc for why every other line/event/payload type in a rollout is intentionally skipped.
|
|
13
|
+
*/
|
|
14
|
+
export declare class CodexSessionSource implements NativeSessionSource {
|
|
15
|
+
private readonly opts;
|
|
16
|
+
readonly agentId = "codex";
|
|
17
|
+
readonly kind: "codex";
|
|
18
|
+
/** `opts` is injectable (codexHome/cacheDir) so tests can point at temp directories instead of
|
|
19
|
+
* the real user home — mirrors listCodexSessionsByCwd's own opts shape. */
|
|
20
|
+
constructor(opts?: {
|
|
21
|
+
codexHome?: string;
|
|
22
|
+
cacheDir?: string;
|
|
23
|
+
});
|
|
24
|
+
list(cwd: string, opts?: {
|
|
25
|
+
limit?: number;
|
|
26
|
+
}): Promise<NativeSessionEntry[]>;
|
|
27
|
+
/** Codex has no id->file mapping without the index (id lives in each rollout's session_meta), so
|
|
28
|
+
* membership goes through the mtime-cached listCodexSessionsByCwd (no full re-scan when warm). */
|
|
29
|
+
has(sessionId: string, cwd: string): Promise<boolean>;
|
|
30
|
+
loadTranscript(ref: NativeSessionRef): Promise<TranscriptMessage[]>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a cwd for CODEX session-cwd equality comparisons (matching a codex rollout's
|
|
3
|
+
* `session_meta.cwd` against our own workspace cwd). Same rule in spirit as projectId derivation
|
|
4
|
+
* (see agent-paths.ts `deriveProjectId`: resolve + lowercase) — resolve to an absolute path, then
|
|
5
|
+
* lowercase and force forward-slash separators so both `E:\Foo\Bar` and `e:/foo/bar` compare equal.
|
|
6
|
+
* (path.resolve() already normalizes `/` to `\` on win32, so the explicit slash replace below is
|
|
7
|
+
* belt-and-suspenders rather than load-bearing — verified via `path.resolve("e:/xiaozhiclaw/openclaw")`
|
|
8
|
+
* === `"e:\\xiaozhiclaw\\openclaw"` on this machine — but kept explicit so the output format doesn't
|
|
9
|
+
* silently depend on that internal, unstated Node behavior.)
|
|
10
|
+
*/
|
|
11
|
+
export declare function normalizeCwd(cwd: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Encode a cwd into the directory name Claude Code uses under `~/.claude/projects/<encoded>`.
|
|
14
|
+
*
|
|
15
|
+
* This is Claude Code's OWN encoding (not ours) — we must replicate it byte-for-byte to locate
|
|
16
|
+
* existing native claude session directories on disk. It has no public spec, so this was reverse
|
|
17
|
+
* engineered two ways and cross-checked against each other:
|
|
18
|
+
*
|
|
19
|
+
* 1. Real directory names from `ls ~/.claude/projects` on this dev machine (see
|
|
20
|
+
* cwd-normalize.test.ts for the samples, notably a workspace with a literal space in its name
|
|
21
|
+
* — "QA Agent Mention Pool 20260701-1325" — which is what falsified the initially-assumed
|
|
22
|
+
* narrower rule).
|
|
23
|
+
* 2. Decompiling the bundled VS Code extension (anthropic.claude-code-2.1.168,
|
|
24
|
+
* extension/extension.js). The relevant minified functions (renamed here for readability;
|
|
25
|
+
* originals were Hv/yv/pOe/cOe):
|
|
26
|
+
*
|
|
27
|
+
* const MAX_LEN = 200;
|
|
28
|
+
* function hashCode(e) { // Java String.hashCode()-style,
|
|
29
|
+
* let t = 0; // 32-bit signed, wraps via `|0`
|
|
30
|
+
* for (let r = 0; r < e.length; r++) t = (t << 5) - t + e.charCodeAt(r) | 0;
|
|
31
|
+
* return t;
|
|
32
|
+
* }
|
|
33
|
+
* function encodeProjectDirName(e) {
|
|
34
|
+
* const t = e.replace(/[^a-zA-Z0-9]/g, "-");
|
|
35
|
+
* if (t.length <= MAX_LEN) return t;
|
|
36
|
+
* return `${t.slice(0, MAX_LEN)}-${Math.abs(hashCode(e)).toString(36)}`;
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* Two things this rule does that a naive "just swap \\/:. for -" guess would get wrong:
|
|
40
|
+
* - The replacement set is "everything that isn't [a-zA-Z0-9]", not a fixed 4-character set —
|
|
41
|
+
* spaces, underscores, parens, plus signs, and non-ASCII (e.g. Chinese) characters are all
|
|
42
|
+
* replaced too. Original casing is preserved (no lowercasing, unlike normalizeCwd above).
|
|
43
|
+
* - Encoded names longer than 200 characters are truncated to 200 chars plus a
|
|
44
|
+
* "-<base36 32-bit hash>" suffix, where the hash is computed over the ORIGINAL
|
|
45
|
+
* (pre-replacement) string, not the truncated one.
|
|
46
|
+
*
|
|
47
|
+
* This encoding is lossy/one-way by construction (e.g. `...\\qlogicagent-hub` and
|
|
48
|
+
* `...\\qlogicagent\\hub` both encode to the same name) — it can only be used to look up a
|
|
49
|
+
* directory for a known cwd, never to recover a cwd from a directory name. If Claude Code changes
|
|
50
|
+
* this encoding in a future version, update this function and the fixtures in
|
|
51
|
+
* cwd-normalize.test.ts together.
|
|
52
|
+
*/
|
|
53
|
+
export declare function claudeProjectDirName(cwd: string): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sweep both native stores. Best-effort: never throws (all I/O is guarded); returns how many rollouts were
|
|
3
|
+
* reclaimed. `home`/`codexHome`/`ttlMs`/`now` are injectable for tests.
|
|
4
|
+
*/
|
|
5
|
+
export declare function sweepEphemeralWorktreeRollouts(opts?: {
|
|
6
|
+
home?: string;
|
|
7
|
+
codexHome?: string;
|
|
8
|
+
ttlMs?: number;
|
|
9
|
+
now?: number;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
claudeDeleted: number;
|
|
12
|
+
codexDeleted: number;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for WHICH third-party ACP backend ids have local, cwd-discoverable
|
|
3
|
+
* native session history ("native-cwd" adapters registered in register-builtin-sources.ts).
|
|
4
|
+
*
|
|
5
|
+
* Consumed by both:
|
|
6
|
+
* - acp-detector.ts (assembleBackendDescriptor) — descriptor's capabilities.sessionHistory
|
|
7
|
+
* - session-handler.ts (resolveAgentSessionHistory) — session.list routing
|
|
8
|
+
*
|
|
9
|
+
* so the two can never drift on which ids are native-adapted (the DRY debt Cut 1 intentionally
|
|
10
|
+
* left behind — see Cut 3 Task 3.0). To add or remove a native-cwd agent: change this set AND
|
|
11
|
+
* register/unregister the matching NativeSessionSource adapter in register-builtin-sources.ts.
|
|
12
|
+
*
|
|
13
|
+
* Zero imports by design — this file must stay a dependency-free leaf so both acp-detector.ts and
|
|
14
|
+
* session-handler.ts (which itself imports ACP_BACKENDS from acp-detector.ts) can import it without
|
|
15
|
+
* risking an import cycle.
|
|
16
|
+
*/
|
|
17
|
+
export declare const NATIVE_CWD_BACKEND_IDS: ReadonlySet<string>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native session source registry.
|
|
3
|
+
*
|
|
4
|
+
* A "native" session source lets a third-party ACP agent (codex/claude) expose ITS OWN
|
|
5
|
+
* CWD-scoped session history — recorded by the agent's own CLI, not by qlogicagent — through
|
|
6
|
+
* the same session.list / session.get surface as our own sessions.
|
|
7
|
+
*
|
|
8
|
+
* Cut 1 shipped only the registry contract with a loose shape; no adapter registered itself, so
|
|
9
|
+
* {@link getNativeSessionSource} always returned undefined and callers (session-handler's
|
|
10
|
+
* resolveSessionSource) fell back to an empty list for native-cwd agents. Cut 2 refines the
|
|
11
|
+
* contract below and adds the claude adapter (see claude-session-source.ts) that calls
|
|
12
|
+
* {@link registerNativeSessionSource}.
|
|
13
|
+
*/
|
|
14
|
+
/** Opaque locator for a single native session's transcript, round-tripped by the frontend without
|
|
15
|
+
* interpretation (session.list returns it, session.loadNativeTranscript consumes it). */
|
|
16
|
+
export interface NativeSessionRef {
|
|
17
|
+
agentId: string;
|
|
18
|
+
kind: "codex" | "claude";
|
|
19
|
+
/** Absolute path to the on-disk transcript file (adapter-specific format). */
|
|
20
|
+
file: string;
|
|
21
|
+
}
|
|
22
|
+
/** One native session list entry — the `source: "native"` variant of session.list's
|
|
23
|
+
* SessionListEntry (see session-types.ts SessionListEntry for the "own" counterpart). */
|
|
24
|
+
export interface NativeSessionEntry {
|
|
25
|
+
sessionId: string;
|
|
26
|
+
source: "native";
|
|
27
|
+
agentId: string;
|
|
28
|
+
title?: string;
|
|
29
|
+
lastActiveAt: string;
|
|
30
|
+
messageCount: number;
|
|
31
|
+
cwd: string;
|
|
32
|
+
nativeRef: NativeSessionRef;
|
|
33
|
+
/** Stamped by handleSessionList from the requested project so the frontend's per-project grouping
|
|
34
|
+
* (project-tree filters by projectId) shows native entries under it. The source discovers by cwd
|
|
35
|
+
* and cannot know the project, so it leaves this unset. */
|
|
36
|
+
projectId?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Minimal transcript-line shape for native session.loadTranscript. This intentionally does NOT
|
|
40
|
+
* import the frontend's `InboundMessagePayload` (ui-next/src/types/gateway.ts) — that type lives
|
|
41
|
+
* in a separate package/repo from this engine and carries many fields irrelevant to a bare
|
|
42
|
+
* transcript line (sessionKey, blocks, attachments, mediaUrl, chatId, ...). Instead this is the
|
|
43
|
+
* minimal subset every native adapter can populate, documented field-for-field against the
|
|
44
|
+
* frontend contract so a caller can widen a TranscriptMessage into an InboundMessagePayload by
|
|
45
|
+
* filling in the rest as optional/absent:
|
|
46
|
+
* - messageId -> InboundMessagePayload.messageId (required)
|
|
47
|
+
* - content -> InboundMessagePayload.content (required)
|
|
48
|
+
* - timestamp -> InboundMessagePayload.timestamp (required)
|
|
49
|
+
* - source -> InboundMessagePayload.source (required, narrowed here to "user"|"assistant"
|
|
50
|
+
* since that's all a transcript line can be)
|
|
51
|
+
* - platform -> InboundMessagePayload.platform (required there; optional here since every
|
|
52
|
+
* current adapter always sets it to "desktop" — kept optional so future adapters
|
|
53
|
+
* aren't forced to fabricate a value)
|
|
54
|
+
*/
|
|
55
|
+
export interface TranscriptMessage {
|
|
56
|
+
messageId: string;
|
|
57
|
+
content: string;
|
|
58
|
+
timestamp: string;
|
|
59
|
+
source: "user" | "assistant";
|
|
60
|
+
platform?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface NativeSessionSource {
|
|
63
|
+
readonly agentId: string;
|
|
64
|
+
readonly kind: "codex" | "claude";
|
|
65
|
+
/** `cwd` is the RAW (un-normalized) project cwd — callers (session-handler's
|
|
66
|
+
* resolveSessionSource, via handleSessionList) pass the REQUESTED project's workspaceDir
|
|
67
|
+
* (params.projectId's, when present) straight through, falling back to getActiveProjectRoot()
|
|
68
|
+
* only when no projectId was given. Each adapter is responsible for whatever
|
|
69
|
+
* normalization/encoding its own on-disk storage requires (e.g. claude's
|
|
70
|
+
* claudeProjectDirName preserves original casing). */
|
|
71
|
+
list(cwd: string, opts?: {
|
|
72
|
+
limit?: number;
|
|
73
|
+
}): Promise<NativeSessionEntry[]>;
|
|
74
|
+
/** Cheap existence check: is `sessionId` an existing native session under `cwd` for this agent?
|
|
75
|
+
* Used on a COLD spawn to decide whether to resume it (ACP session/load) instead of creating a
|
|
76
|
+
* fresh session — so a native conversation regains its authoritative history after pool eviction/
|
|
77
|
+
* restart, not only on the one-shot "继续". Optional so test fakes need not implement it. */
|
|
78
|
+
has?(sessionId: string, cwd: string): Promise<boolean>;
|
|
79
|
+
loadTranscript(ref: NativeSessionRef): Promise<TranscriptMessage[]>;
|
|
80
|
+
}
|
|
81
|
+
/** Register a native session source for an agentId. Last registration for a given id wins. */
|
|
82
|
+
export declare function registerNativeSessionSource(source: NativeSessionSource): void;
|
|
83
|
+
/** Look up a registered native session source by agentId. Undefined when none is registered for
|
|
84
|
+
* that id (no adapter registered itself yet, or the agentId has no native adapter at all). */
|
|
85
|
+
export declare function getNativeSessionSource(agentId?: string): NativeSessionSource | undefined;
|
|
86
|
+
/** Remove a registered native session source by agentId. Symmetric with {@link registerNativeSessionSource};
|
|
87
|
+
* exists primarily so tests can deregister a fake adapter afterward without leaking it into other
|
|
88
|
+
* test cases that share this module-level registry. No-op if nothing is registered for that id. */
|
|
89
|
+
export declare function unregisterNativeSessionSource(agentId: string): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cleanNativeTitle(message: string, maxLen?: number): string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerBuiltinNativeSources(): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for session resource keys (canonical identity) and the derived
|
|
3
|
+
* resident-pool / delegation keys. Forbids scattered ad-hoc string concatenation of session keys
|
|
4
|
+
* (invariant I2, docs/session-identity-alignment-spec.md). Leaf module — imports nothing.
|
|
5
|
+
*/
|
|
6
|
+
/** Canonical session identity: our-minted, stable-from-creation id + its owning agent + project. */
|
|
7
|
+
export interface SessionResource {
|
|
8
|
+
/** Owning agent kind: own ("qlogicagent") / codex / claude / … (= SessionMetadata.agentId). */
|
|
9
|
+
agentId: string;
|
|
10
|
+
/** Owning project id (path-derived / opaque; not minted here). */
|
|
11
|
+
projectId: string;
|
|
12
|
+
/** Our-minted session id, stable from creation — never the native Y (Y lives in nativeBinding). */
|
|
13
|
+
canonicalSessionId: string;
|
|
14
|
+
}
|
|
15
|
+
/** Serialize the canonical session resource to its single opaque key string. */
|
|
16
|
+
export declare function buildSessionResourceKey(r: SessionResource): string;
|
|
17
|
+
/** Parse a key produced by {@link buildSessionResourceKey}; throws on a malformed key. */
|
|
18
|
+
export declare function parseSessionResourceKey(key: string): SessionResource;
|
|
19
|
+
/**
|
|
20
|
+
* Resident external-agent pool key: same agent + workspace + session ⇒ same resident process.
|
|
21
|
+
* `sessionId` may be a canonical id, the WARM_SESSION token, or undefined (defaults to "default").
|
|
22
|
+
* Byte-identical to the historical inline pool key it replaces.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildAgentPoolKey(agentId: string, cwd: string, sessionId?: string): string;
|
|
25
|
+
/** Prefix matching every pool key for `agentId` (agent-scoped eviction, exact id-boundary). */
|
|
26
|
+
export declare function agentPoolKeyPrefix(agentId: string): string;
|
|
27
|
+
/** @mention delegation serialization key for one (agent × conversation) pair. */
|
|
28
|
+
export declare function buildConvKey(agentId: string, canonicalSessionId: string): string;
|
|
@@ -3,6 +3,8 @@ import type { MessageDisplayMetadata, PersistedChatMessage, TokenUsage } from ".
|
|
|
3
3
|
export interface TranscriptLoadResult {
|
|
4
4
|
messages: PersistedChatMessage[];
|
|
5
5
|
corruptLines: number;
|
|
6
|
+
/** 1-based line numbers that failed to parse (bounded to the first MAX_REPORTED_CORRUPT_LINES). */
|
|
7
|
+
corruptLineNumbers: number[];
|
|
6
8
|
}
|
|
7
9
|
export declare function appendMessage(sessionId: string, message: ChatMessage, projectRoot: string, turnId?: string, metadata?: {
|
|
8
10
|
usage?: TokenUsage;
|
|
@@ -18,6 +18,25 @@ export interface InflightTurnMarker {
|
|
|
18
18
|
/** 该轮是否已执行过有副作用的工具(写文件/发消息/外部 API)。安全档不据此自动续,仅用于 UI"可能重复"提示 + 为智能档铺路。 */
|
|
19
19
|
hadSideEffectTool?: boolean;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Native (codex/claude) session binding — present only on native sessions, written once the
|
|
23
|
+
* external agent reports its own session id (Y). Our `sessionId` stays the canonical identity;
|
|
24
|
+
* `nativeSessionId` is an ATTRIBUTE used ONLY when talking to the external CLI (ACP session/load),
|
|
25
|
+
* never as a map/pool/list key. `nativeRef` independently locates the external transcript file so
|
|
26
|
+
* content loads (and the binding is recoverable) without relying on `sessionId === nativeSessionId`.
|
|
27
|
+
*/
|
|
28
|
+
export interface NativeBinding {
|
|
29
|
+
/** External agent's own session id (Y) — used only for ACP session/load resume. */
|
|
30
|
+
nativeSessionId: string;
|
|
31
|
+
/** Locator for the external transcript file (loadTranscript + binding-loss fallback). */
|
|
32
|
+
nativeRef: {
|
|
33
|
+
agentId: string;
|
|
34
|
+
kind: "codex" | "claude";
|
|
35
|
+
file: string;
|
|
36
|
+
};
|
|
37
|
+
/** ISO time the binding was established (first turn the external agent reported Y). */
|
|
38
|
+
boundAt: string;
|
|
39
|
+
}
|
|
21
40
|
export interface SessionMetadata {
|
|
22
41
|
schemaVersion?: number;
|
|
23
42
|
sessionId: string;
|
|
@@ -52,10 +71,18 @@ export interface SessionMetadata {
|
|
|
52
71
|
interruptedTurnCount?: number;
|
|
53
72
|
/** 达熔断阈值放弃恢复的时间戳(UI 显示"多次中断已停止自动恢复")。 */
|
|
54
73
|
recoveryAbandonedAt?: string;
|
|
74
|
+
/** native(codex/claude)会话绑定:外部 Y + nativeRef;own 会话恒 undefined。见 {@link NativeBinding}。 */
|
|
75
|
+
nativeBinding?: NativeBinding;
|
|
55
76
|
}
|
|
56
77
|
export interface PersistedSession {
|
|
57
78
|
metadata: SessionMetadata;
|
|
58
79
|
messages: PersistedChatMessage[];
|
|
80
|
+
/**
|
|
81
|
+
* Count of transcript lines that failed to parse and were skipped on load. Surfaced so the UI can
|
|
82
|
+
* tell the user "N 条消息无法读取" instead of silently resuming a conversation with holes in it.
|
|
83
|
+
* Omitted/0 for a clean transcript.
|
|
84
|
+
*/
|
|
85
|
+
corruptLines?: number;
|
|
59
86
|
}
|
|
60
87
|
export interface MessageDisplayMetadata {
|
|
61
88
|
messageId?: string;
|
|
@@ -65,6 +92,14 @@ export interface MessageDisplayMetadata {
|
|
|
65
92
|
chatId?: string;
|
|
66
93
|
userId?: string;
|
|
67
94
|
userName?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Presentation kind for a persisted message whose ROLE alone doesn't capture how
|
|
97
|
+
* the UI should render it. Currently: "mid_turn_guidance" — a user-authored message
|
|
98
|
+
* injected into a running turn (运行中引导). It stays role:"user" (the model must see
|
|
99
|
+
* it as the user's words), but the desktop renders it as an agent-side "收到引导"
|
|
100
|
+
* receipt line rather than a standalone user bubble.
|
|
101
|
+
*/
|
|
102
|
+
kind?: string;
|
|
68
103
|
}
|
|
69
104
|
export interface PersistedChatMessage extends ChatMessage {
|
|
70
105
|
timestamp?: string;
|
|
@@ -88,6 +123,8 @@ export interface SessionListEntry {
|
|
|
88
123
|
groupKey?: string;
|
|
89
124
|
previousSessionId?: string;
|
|
90
125
|
carryoverSummary?: string | null;
|
|
126
|
+
/** N4: native (codex/claude) binding, surfaced so session.list can shape source:"native" + nativeRef. */
|
|
127
|
+
nativeBinding?: NativeBinding;
|
|
91
128
|
}
|
|
92
129
|
export interface TaskSummaryDeps {
|
|
93
130
|
transport: import("../ports/index.js").LLMTransport;
|