qlogicagent 2.17.9 → 2.18.1
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 +21 -21
- package/dist/cli.js +1 -1566
- package/dist/index.js +445 -421
- package/dist/orchestration.js +7 -7
- package/dist/protocol.js +1 -1
- package/dist/skills/builtin/desktop-screenshot/SKILL.md +62 -0
- package/dist/skills/builtin/office-doc-reading/SKILL.md +19 -0
- package/dist/skills/mcp/astraclaw-native-mcp-server.js +7 -6
- package/dist/types/agent/tool-loop/artifact-final-contract.d.ts +18 -0
- package/dist/types/agent/tool-loop/loop-helpers.d.ts +9 -0
- package/dist/types/agent/tool-loop.d.ts +14 -5
- package/dist/types/agent/types.d.ts +5 -0
- package/dist/types/cli/acp-session-handlers.d.ts +8 -1
- package/dist/types/cli/acp-session-host.d.ts +5 -1
- package/dist/types/cli/default-project-bootstrap.d.ts +33 -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 +49 -0
- package/dist/types/cli/handlers/turn-handler.d.ts +77 -0
- package/dist/types/cli/media-understanding.d.ts +45 -0
- package/dist/types/cli/permission-rule-persistence.d.ts +16 -0
- package/dist/types/cli/session-context.d.ts +5 -1
- package/dist/types/cli/stdio-acp-request-host.d.ts +1 -1
- package/dist/types/cli/stdio-rpc-handler-hosts.d.ts +4 -0
- package/dist/types/cli/stdio-server.d.ts +17 -1
- package/dist/types/cli/tool-bootstrap-core-registration.d.ts +2 -0
- package/dist/types/cli/turn-core.d.ts +2 -0
- package/dist/types/orchestration/agent-instance.d.ts +4 -0
- package/dist/types/orchestration/delegation-coordinator.d.ts +10 -0
- package/dist/types/orchestration/product-persistence.d.ts +4 -0
- package/dist/types/orchestration/workflow/capability-catalog.d.ts +1 -1
- package/dist/types/orchestration/workflow/workflow-controller.d.ts +1 -1
- package/dist/types/orchestration/workflow/workflow-runtime.d.ts +1 -1
- package/dist/types/protocol/notifications.d.ts +1 -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/index.d.ts +1 -1
- package/dist/types/protocol/wire/notification-payloads.d.ts +26 -0
- package/dist/types/runtime/execution/turn-progress-notifier.d.ts +16 -0
- package/dist/types/runtime/infra/acp-detector.d.ts +40 -6
- package/dist/types/runtime/infra/acp-protocol-adapter.d.ts +14 -0
- package/dist/types/runtime/infra/agent-process.d.ts +16 -0
- package/dist/types/runtime/infra/astraclaw-capabilities.d.ts +2 -2
- package/dist/types/runtime/infra/builtin-skills-seed.d.ts +31 -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/project-data-gc.d.ts +7 -1
- package/dist/types/runtime/infra/skill-resolver.d.ts +15 -0
- 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/permission-contracts.d.ts +5 -2
- package/dist/types/runtime/prompt/environment-context.d.ts +24 -2
- package/dist/types/runtime/prompt/fresh-workspace-evidence.d.ts +1 -2
- package/dist/types/runtime/prompt/identity-section.d.ts +17 -0
- package/dist/types/runtime/session/inbound-upload-persistence.d.ts +20 -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-recovery.d.ts +58 -0
- package/dist/types/runtime/session/session-types.d.ts +30 -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/skills/tools/exec-tool.d.ts +1 -1
- package/dist/types/skills/tools/shell/command-classification.d.ts +1 -0
- package/dist/types/skills/tools/shell/index.d.ts +3 -3
- package/dist/types/skills/tools/shell/powershell-provider.d.ts +8 -3
- package/dist/types/skills/tools/shell/shell-exec.d.ts +18 -0
- package/dist/types/skills/tools/shell/shell-provider.d.ts +67 -5
- package/dist/types/transport/acp-server.d.ts +6 -0
- package/package.json +4 -1
|
@@ -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;
|
|
@@ -5,5 +5,11 @@
|
|
|
5
5
|
* re-indexes it). Only a dir with NEITHER (truly corrupt/orphaned — crashed mid-create) is reclaimed.
|
|
6
6
|
* This makes the registry non-load-bearing for recovery: a lost projects.json never causes data loss.
|
|
7
7
|
* Also prunes stale deletion tombstones. NEVER touches live projects' data or the user's workspace.
|
|
8
|
+
*
|
|
9
|
+
* TRULY ASYNC (not just deferred): the iteration count grows with every project dir ever created on
|
|
10
|
+
* this machine (unbounded on a long-lived box). Following the AcpDetector.scanAsync convention, this
|
|
11
|
+
* uses fs/promises and awaits between each directory — every await yields the event loop, so an RPC
|
|
12
|
+
* that arrives mid-GC is dispatched between directories instead of waiting for the whole sweep. The
|
|
13
|
+
* keep/reclaim judgment is unchanged; only the sync fs calls became awaited yield points.
|
|
8
14
|
*/
|
|
9
|
-
export declare function gcOrphanProjectData(): void
|
|
15
|
+
export declare function gcOrphanProjectData(): Promise<void>;
|
|
@@ -48,6 +48,17 @@ export interface SkillActivationContext {
|
|
|
48
48
|
export declare function resolveSkills(projectRoot?: string, activation?: SkillActivationContext): ResolvedSkill[];
|
|
49
49
|
/** Only the skills active in the given project (for the system-prompt skills catalog / resolution). */
|
|
50
50
|
export declare function resolveActiveSkills(projectRoot?: string, activation?: SkillActivationContext): ResolvedSkill[];
|
|
51
|
+
/**
|
|
52
|
+
* Async, NON-BLOCKING, cached twin of {@link resolveSkills} for the first-token hot path
|
|
53
|
+
* ({@link createAvailableSkillsSection}). Returns ALL store skills annotated with mute/active state,
|
|
54
|
+
* exactly like {@link resolveSkills}, but reads off the event loop and reuses cached parse results when
|
|
55
|
+
* the store is unchanged. The sync {@link resolveSkills} is deliberately left intact for other callers.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resolveSkillsAsync(projectRoot?: string, activation?: SkillActivationContext): Promise<ResolvedSkill[]>;
|
|
58
|
+
/** Async twin of {@link resolveActiveSkills}: only the skills active in the given project context. */
|
|
59
|
+
export declare function resolveActiveSkillsAsync(projectRoot?: string, activation?: SkillActivationContext): Promise<ResolvedSkill[]>;
|
|
60
|
+
/** Test-only: drop the async raw-skill cache so each test observes a cold read. */
|
|
61
|
+
export declare function __clearSkillResolveCacheForTests(): void;
|
|
51
62
|
/**
|
|
52
63
|
* Resolve a single skill by name to its store paths, or null if not present.
|
|
53
64
|
* Resolution honors only the global store (single source of truth) and ignores
|
|
@@ -57,3 +68,7 @@ export declare function resolveSkillPath(name: string): {
|
|
|
57
68
|
baseDir: string;
|
|
58
69
|
filePath: string;
|
|
59
70
|
} | null;
|
|
71
|
+
export declare function skillConditionsMatch(meta: {
|
|
72
|
+
requiredTools?: string[];
|
|
73
|
+
environments?: string[];
|
|
74
|
+
}, activation: SkillActivationContext | undefined): boolean;
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -17,11 +17,33 @@
|
|
|
17
17
|
import { type SystemPromptSection } from "./system-prompt-sections.js";
|
|
18
18
|
import type { TaskDomain } from "./task-domain.js";
|
|
19
19
|
export type ShellType = "bash" | "powershell" | "zsh" | "cmd" | "fish" | "unknown";
|
|
20
|
+
/**
|
|
21
|
+
* Structural snapshot of the shell provider that `exec()` actually drives.
|
|
22
|
+
*
|
|
23
|
+
* Defined LOCALLY (not imported from src/skills/tools/shell) on purpose: the
|
|
24
|
+
* runtime/prompt layer must not depend on the skills layer (architecture
|
|
25
|
+
* boundary). The CLI layer, which may import both, reads the real snapshot via
|
|
26
|
+
* `getActiveShellInfo()` and passes it in here; TS structural typing makes the
|
|
27
|
+
* shapes compatible without a cross-layer import.
|
|
28
|
+
*/
|
|
29
|
+
export interface ShellInfo {
|
|
30
|
+
type: "bash" | "powershell";
|
|
31
|
+
shellPath?: string;
|
|
32
|
+
psEdition?: "5.1" | "7+";
|
|
33
|
+
isGitBashOnWindows?: boolean;
|
|
34
|
+
}
|
|
20
35
|
/**
|
|
21
36
|
* Create a system prompt section that provides environment context.
|
|
22
37
|
* Memoized — computed once per session (environment doesn't change mid-session).
|
|
38
|
+
*
|
|
39
|
+
* @param cwdOverride Working directory to report (defaults to process.cwd()).
|
|
40
|
+
* @param shellInfo Real active-shell facts from the provider (via the CLI).
|
|
41
|
+
* When omitted, the `- Shell:` line and guidance fall back to
|
|
42
|
+
* `detectShellType()` (env-based guess). The shell identity is
|
|
43
|
+
* folded into the memoize key so a different shell doesn't
|
|
44
|
+
* serve a stale cached section for the same cwd.
|
|
23
45
|
*/
|
|
24
|
-
export declare function createEnvironmentContextSection(cwdOverride?: string): SystemPromptSection;
|
|
46
|
+
export declare function createEnvironmentContextSection(cwdOverride?: string, shellInfo?: ShellInfo): SystemPromptSection;
|
|
25
47
|
/**
|
|
26
48
|
* Create the task guidance system prompt section.
|
|
27
49
|
* Domain-aware: selects appropriate guidance based on detected or overridden task domain.
|
|
@@ -52,7 +74,7 @@ export declare function createLanguageSection(language?: string): SystemPromptSe
|
|
|
52
74
|
* makes installed skills actually auto-activate; without it active skills only sit behind the
|
|
53
75
|
* skill_view tool as a passive catalog and never fire on their own.
|
|
54
76
|
*/
|
|
55
|
-
export declare function createAvailableSkillsSection(cwdOverride?: string): SystemPromptSection;
|
|
77
|
+
export declare function createAvailableSkillsSection(cwdOverride?: string, availableToolNames?: string[]): SystemPromptSection;
|
|
56
78
|
/**
|
|
57
79
|
* Detect the response language from the user's current-turn text so the strong
|
|
58
80
|
* explicit-language branch of createLanguageSection actually fires.
|
|
@@ -11,9 +11,8 @@ 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;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness-owned identity section — 每轮(含轻量聊天轮)注入,使身份/型号回答与
|
|
3
|
+
* 入口渠道无关。审计结论:正常轮此前没有任何身份 section,桌面端答 "我是 Claude"
|
|
4
|
+
* (模型训练身份在真空中浮现)、微信端答 "qlogicagent, qlogic 团队"(硬编码人设
|
|
5
|
+
* 锚点 + .qlogicagent 路径 token 引发的编造)。此 section 是两者的唯一事实来源。
|
|
6
|
+
*/
|
|
7
|
+
import { type SystemPromptSection } from "./system-prompt-sections.js";
|
|
8
|
+
export interface IdentityInfo {
|
|
9
|
+
/** 产品名(运营可配置),缺省 AstraClaw */
|
|
10
|
+
productName?: string;
|
|
11
|
+
/** 可选的品牌/构建方说明行(运营可配置) */
|
|
12
|
+
vendorLine?: string;
|
|
13
|
+
provider?: string;
|
|
14
|
+
/** 本轮解析出的真实模型 id */
|
|
15
|
+
model?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function createIdentitySection(info: IdentityInfo): SystemPromptSection;
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
* transport cache; only long-term persistence is skipped).
|
|
13
13
|
*/
|
|
14
14
|
import type { ChatMessage } from "../../protocol/wire/index.js";
|
|
15
|
+
export interface PersistedUpload {
|
|
16
|
+
url: string;
|
|
17
|
+
localPath?: string;
|
|
18
|
+
}
|
|
19
|
+
/** 同 persistInboundUploadUrl,但返回持久化后的本地绝对路径(供模型直接 read/exec)。 */
|
|
20
|
+
export declare function persistInboundUploadUrlDetailed(url: string, projectId: string, sessionId: string): Promise<PersistedUpload>;
|
|
15
21
|
/**
|
|
16
22
|
* Persist a single inbound upload URL to projectData uploads and return the rewritten proxy URL.
|
|
17
23
|
* Non-transport URLs (already-persisted, remote, file://) and any failure return the input unchanged.
|
|
@@ -22,3 +28,17 @@ export declare function persistInboundUploadUrl(url: string, projectId: string,
|
|
|
22
28
|
* resumed history already carries persisted local URLs). Mutates and returns the message array.
|
|
23
29
|
*/
|
|
24
30
|
export declare function persistInboundUploadsForTurn(messages: ChatMessage[], projectId: string | undefined, sessionId: string | undefined): Promise<ChatMessage[]>;
|
|
31
|
+
/**
|
|
32
|
+
* 在任意模型可见文本中查找文档类传输 URL,落盘并追加本地路径注记。
|
|
33
|
+
* 供两条真实投递路径复用:gateway 烤进 user text 的清单,以及
|
|
34
|
+
* turn-handler 的 attachmentModelContext(拼接到 LLM 视图的附件上下文)。
|
|
35
|
+
*/
|
|
36
|
+
export declare function appendLocalDocumentNotesToText(text: string, projectId: string | undefined, sessionId: string | undefined): Promise<string>;
|
|
37
|
+
/**
|
|
38
|
+
* 把最后一条用户消息文本里的文档类传输 URL 抓下来落盘,并在消息末尾追加本地
|
|
39
|
+
* 路径注记 —— 模型从"短命 URL"起步变成从"可 read/exec 的本地文件"起步。
|
|
40
|
+
* image/video/audio 已由 persistInboundUploadsForTurn 处理,这里只管文档。
|
|
41
|
+
* 仅覆盖清单被烤进 user text 的 gateway 及 legacy 会话;新版 turn 的文档引用
|
|
42
|
+
* 走 attachmentModelContext,由 turn-handler 调 appendLocalDocumentNotesToText。
|
|
43
|
+
*/
|
|
44
|
+
export declare function persistInboundDocumentRefsForTurn(messages: ChatMessage[], projectId: string | undefined, sessionId: string | undefined): Promise<ChatMessage[]>;
|
|
@@ -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,58 @@
|
|
|
1
|
+
export declare function isSideEffectTool(name: string): boolean;
|
|
2
|
+
/** 取最后一条 user 消息的纯文本前若干字,供崩溃恢复 UI 提示(多模态/空则无预览)。 */
|
|
3
|
+
export declare function extractLastUserPreview(messages: ReadonlyArray<{
|
|
4
|
+
role: string;
|
|
5
|
+
content: unknown;
|
|
6
|
+
}>): string | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* turn 开始:写 in-flight 标记。fire-and-forget 语义 —— 绝不阻塞或抛错打断 turn。
|
|
9
|
+
* projectRoot 未解析(无项目上下文)时静默跳过。
|
|
10
|
+
*/
|
|
11
|
+
export declare function markTurnInflight(sessionId: string, projectRoot: string | undefined, turnId: string, messages: ReadonlyArray<{
|
|
12
|
+
role: string;
|
|
13
|
+
content: unknown;
|
|
14
|
+
}>): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* 首个副作用工具执行时置位 hadSideEffectTool(仅首次,避免每次工具调用写盘)。
|
|
17
|
+
* 安全档不据此自动续,仅用于 L4 的"继续可能重复"提示 + 为智能档铺路。
|
|
18
|
+
*/
|
|
19
|
+
export declare function markInflightSideEffect(sessionId: string, projectRoot: string | undefined): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* turn 结束(finally 到达 = 有始有终,哪怕失败/用户中断):清标记 + 中断计数归零。
|
|
22
|
+
* finally 未到达(进程被硬杀/崩溃)→ 标记残留,由 L3 启动扫描接住。
|
|
23
|
+
*/
|
|
24
|
+
export declare function clearTurnInflight(sessionId: string, projectRoot: string | undefined): Promise<void>;
|
|
25
|
+
interface SessionMetaLocation {
|
|
26
|
+
sessionId: string;
|
|
27
|
+
metadataPath: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 遍历当前 owner profile 下所有 project 的所有 session 的 metadata.json 绝对路径。
|
|
31
|
+
* 直接扫 projectData/<projectId>/sessions/<sessionId>/ —— 用绝对路径,绕开 projectRoot↔projectId
|
|
32
|
+
* 解析歧义(未注册 project 的残留 session 也覆盖到)。L2 清标记 + L3 启动扫描共用。
|
|
33
|
+
*/
|
|
34
|
+
export declare function enumerateSessionMetadataFiles(): Promise<SessionMetaLocation[]>;
|
|
35
|
+
/**
|
|
36
|
+
* L2 优雅关闭:清所有 session 残留的 in-flight 标记 + 中断计数归零。
|
|
37
|
+
* 优雅关闭 == 主动清标记 == hermes 的 .clean_shutdown 语义(更简、单一事实源)。
|
|
38
|
+
* 直接原子改写 metadata.json(不经 updateSessionMetadata 的 projectRoot 解析)。返回清理数。
|
|
39
|
+
*/
|
|
40
|
+
export declare function clearAllInflightMarkers(): Promise<number>;
|
|
41
|
+
export interface CrashedTurnInfo {
|
|
42
|
+
sessionId: string;
|
|
43
|
+
turnId: string;
|
|
44
|
+
startedAt: string;
|
|
45
|
+
lastUserPreview?: string;
|
|
46
|
+
hadSideEffectTool: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* L3 启动扫描:找崩溃残留的 in-flight 标记,处理双校验/熔断,返回待恢复列表。
|
|
50
|
+
* agent 每次 initialize 调用 —— gateway 重启 or agent respawn 都覆盖(标记本身是事实源)。
|
|
51
|
+
*
|
|
52
|
+
* - 双校验(借鉴 codex #12382,spec §7):transcript 最后一条 assistant ts ≥ marker.startedAt →
|
|
53
|
+
* 该轮其实已完成、只是清标记那步没落盘 → 静默清、不误报。
|
|
54
|
+
* - 熔断:连续崩溃中断达 MAX_INTERRUPTED_RECOVERY → 放弃(清标记 + recoveryAbandonedAt + fail-loud)。
|
|
55
|
+
* - 否则:保留标记 + 递增中断计数,收进待恢复列表(交给 UI 让用户手动续)。
|
|
56
|
+
*/
|
|
57
|
+
export declare function scanForCrashedTurns(): Promise<CrashedTurnInfo[]>;
|
|
58
|
+
export {};
|