dominds 1.24.4 → 1.25.0
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/README.md +48 -1
- package/README.zh.md +48 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +22 -0
- package/dist/dialog-display-state.d.ts +1 -1
- package/dist/dialog-display-state.js +16 -2
- package/dist/dialog-drive-work.js +5 -0
- package/dist/dialog-global-registry.d.ts +9 -8
- package/dist/dialog-global-registry.js +39 -38
- package/dist/dialog-instance-registry.js +7 -0
- package/dist/dialog-interruption.d.ts +2 -0
- package/dist/dialog-interruption.js +6 -0
- package/dist/dialog.d.ts +6 -6
- package/dist/dialog.js +40 -36
- package/dist/docs/dialog-system.md +7 -7
- package/dist/docs/dialog-system.zh.md +4 -4
- package/dist/docs/dlg-drive-algo.zh.md +539 -0
- package/dist/llm/kernel-driver/drive.js +59 -87
- package/dist/llm/kernel-driver/flow.js +687 -205
- package/dist/llm/kernel-driver/loop.js +131 -61
- package/dist/llm/kernel-driver/runtime.js +1 -1
- package/dist/llm/kernel-driver/sideDialog.js +14 -12
- package/dist/llm/kernel-driver/tellask-special.js +24 -23
- package/dist/llm/kernel-driver/types.d.ts +6 -27
- package/dist/llm/kernel-driver/types.js +0 -1
- package/dist/persistence-errors.d.ts +1 -1
- package/dist/persistence.d.ts +60 -18
- package/dist/persistence.js +481 -202
- package/dist/recovery/open-generation-recovery.d.ts +1 -0
- package/dist/recovery/{proceeding-drive.js → open-generation-recovery.js} +25 -25
- package/dist/recovery/reply-delivery-recovery.d.ts +3 -0
- package/dist/recovery/{reply-special.js → reply-delivery-recovery.js} +12 -12
- package/dist/server/websocket-handler.js +4 -4
- package/dist/server.js +4 -4
- package/dist/tools/builtins.js +4 -2
- package/dist/tools/prompts/skills/en/errors.md +2 -0
- package/dist/tools/prompts/skills/en/index.md +1 -0
- package/dist/tools/prompts/skills/en/tools.md +6 -0
- package/dist/tools/prompts/skills/zh/errors.md +2 -0
- package/dist/tools/prompts/skills/zh/index.md +1 -0
- package/dist/tools/prompts/skills/zh/tools.md +6 -0
- package/dist/tools/ripgrep.js +386 -44
- package/dist/tools/skills.d.ts +1 -0
- package/dist/tools/skills.js +81 -1
- package/dist/tools/team_mgmt.js +38 -2
- package/package.json +5 -5
- package/dist/docs/issues/tellask-background-continuation-live-bugs-2026-05-17.zh.md +0 -101
- package/dist/docs/tellask-background-continuation-refactor.zh.md +0 -1146
- package/dist/recovery/proceeding-drive.d.ts +0 -1
- package/dist/recovery/reply-special.d.ts +0 -3
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ Dominds is an AI-powered DevOps framework that creates autonomous agentic teams
|
|
|
69
69
|
|
|
70
70
|
### Prerequisites
|
|
71
71
|
|
|
72
|
-
- **Node.js (with npm bundled)**: Version 24.
|
|
72
|
+
- **Node.js (with npm bundled)**: Version 24.5+ LTS
|
|
73
73
|
- **npm (if used directly)**: Use the npm bundled with your installed Node.js 24 LTS; current minimum in this repo is `11.9.0`, and newer versions are allowed.
|
|
74
74
|
- **LLM provider configured for your team**: Dominds ships with a built-in provider catalog [main/llm/defaults.yaml](./main/llm/defaults.yaml) including Codex (ChatGPT) and Anthropic, plus several Anthropic-compatible endpoints (e.g. MiniMax, Z.ai, BigModel). You’ll need valid credentials for at least one provider.
|
|
75
75
|
- **pnpm (optional)**: Recommended only if you’re developing Dominds itself. Prefer enabling the pinned CLI once via `npm run setup:pm`. If Corepack shims cannot be enabled in your environment, install `pnpm@10` manually.
|
|
@@ -80,6 +80,53 @@ For repo development, initialize the package manager once after `nvm use --lts`:
|
|
|
80
80
|
npm run setup:pm
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
### Proxy Support
|
|
84
|
+
|
|
85
|
+
Dominds can use Node's built-in environment proxy handling on Windows, macOS, and Linux.
|
|
86
|
+
|
|
87
|
+
Default behavior:
|
|
88
|
+
|
|
89
|
+
- `DOMINDS_USE_ENV_PROXY` is enabled by default.
|
|
90
|
+
- When enabled, Dominds calls Node's runtime env-proxy initializer early in CLI startup.
|
|
91
|
+
- Dominds does not set or clear `NODE_USE_ENV_PROXY`; leave that variable for users who intentionally want Node startup-time or child-process behavior.
|
|
92
|
+
- Set `DOMINDS_USE_ENV_PROXY=0` to stop Dominds from auto-enabling env-proxy support.
|
|
93
|
+
|
|
94
|
+
Supported proxy variables:
|
|
95
|
+
|
|
96
|
+
- `HTTP_PROXY`
|
|
97
|
+
- `HTTPS_PROXY`
|
|
98
|
+
- `NO_PROXY`
|
|
99
|
+
- `http_proxy`
|
|
100
|
+
- `https_proxy`
|
|
101
|
+
- `no_proxy`
|
|
102
|
+
|
|
103
|
+
Practical guidance:
|
|
104
|
+
|
|
105
|
+
- Set both `HTTP_PROXY` and `HTTPS_PROXY` when your network uses the same proxy for both schemes. Lowercase forms also work.
|
|
106
|
+
- Use `NO_PROXY` for localhost, loopback, and internal domains that should bypass the proxy.
|
|
107
|
+
- Values should be standard proxy URLs, for example `http://proxy.company.com:8080`.
|
|
108
|
+
- If your shell already exports proxy variables, Dominds will read them too; clear unrelated proxy vars if you want a clean test environment.
|
|
109
|
+
|
|
110
|
+
Examples:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Windows PowerShell
|
|
114
|
+
$env:HTTP_PROXY = "http://proxy.company.com:8080"
|
|
115
|
+
$env:HTTPS_PROXY = "http://proxy.company.com:8080"
|
|
116
|
+
$env:NO_PROXY = "localhost,127.0.0.1,.corp.local"
|
|
117
|
+
dominds
|
|
118
|
+
|
|
119
|
+
# macOS / Linux shell
|
|
120
|
+
export HTTP_PROXY="http://proxy.company.com:8080"
|
|
121
|
+
export HTTPS_PROXY="http://proxy.company.com:8080"
|
|
122
|
+
export NO_PROXY="localhost,127.0.0.1,.corp.local"
|
|
123
|
+
dominds
|
|
124
|
+
|
|
125
|
+
# Disable Dominds env-proxy handling
|
|
126
|
+
export DOMINDS_USE_ENV_PROXY=0
|
|
127
|
+
dominds
|
|
128
|
+
```
|
|
129
|
+
|
|
83
130
|
### Install Dominds
|
|
84
131
|
|
|
85
132
|
```bash
|
package/README.zh.md
CHANGED
|
@@ -23,7 +23,7 @@ Dominds 是一款面向开发运作(DevOps)场景的智能体框架,它将
|
|
|
23
23
|
|
|
24
24
|
### 环境要求
|
|
25
25
|
|
|
26
|
-
- **Node.js(含 npm)**:版本 24.
|
|
26
|
+
- **Node.js(含 npm)**:版本 24.5+ LTS
|
|
27
27
|
- **npm(如直接使用)**:使用当前安装的 Node.js 24 LTS 自带 npm;本仓库当前最低要求为 `11.9.0`,更高版本也允许。
|
|
28
28
|
- **至少一个可用的 LLM 服务提供商**:Dominds 内置提供商目录(路径:[main/llm/defaults.yaml](./main/llm/defaults.yaml)),需为其中至少一个提供商配置有效凭证(通过环境变量设置)。
|
|
29
29
|
- **pnpm(可选)**:仅在开发 Dominds 本体时推荐使用。优先执行一次 `npm run setup:pm` 启用并缓存仓库固定的 pnpm;若你的环境无法启用 Corepack shim,再手动安装 `pnpm@10`。
|
|
@@ -34,6 +34,53 @@ Dominds 是一款面向开发运作(DevOps)场景的智能体框架,它将
|
|
|
34
34
|
npm run setup:pm
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
### 代理支持
|
|
38
|
+
|
|
39
|
+
Dominds 在 Windows、macOS、Linux 上都可以使用 Node 内建的环境变量代理能力。
|
|
40
|
+
|
|
41
|
+
默认行为:
|
|
42
|
+
|
|
43
|
+
- `DOMINDS_USE_ENV_PROXY` 默认启用。
|
|
44
|
+
- 启用后,Dominds 会在 CLI 启动早期调用 Node 的运行时环境代理初始化。
|
|
45
|
+
- Dominds 不会设置或清理 `NODE_USE_ENV_PROXY`;这个变量留给确实需要 Node 启动期行为或子进程继承语义的用户自己管理。
|
|
46
|
+
- 若要让 Dominds 不再自动开启环境代理支持,设置 `DOMINDS_USE_ENV_PROXY=0`。
|
|
47
|
+
|
|
48
|
+
支持的代理变量:
|
|
49
|
+
|
|
50
|
+
- `HTTP_PROXY`
|
|
51
|
+
- `HTTPS_PROXY`
|
|
52
|
+
- `NO_PROXY`
|
|
53
|
+
- `http_proxy`
|
|
54
|
+
- `https_proxy`
|
|
55
|
+
- `no_proxy`
|
|
56
|
+
|
|
57
|
+
实践建议:
|
|
58
|
+
|
|
59
|
+
- 若同一网络对 HTTP/HTTPS 都走同一代理,建议 `HTTP_PROXY` 和 `HTTPS_PROXY` 一起设置。小写变量名同样有效。
|
|
60
|
+
- `NO_PROXY` 用于排除 localhost、回环地址和内网域名。
|
|
61
|
+
- 代理值应使用标准 URL 形式,例如 `http://proxy.company.com:8080`。
|
|
62
|
+
- 如果你的 shell 已经导出了代理变量,Dominds 也会读取它们;想要干净的测试环境时,请清掉无关的代理变量。
|
|
63
|
+
|
|
64
|
+
示例:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Windows PowerShell
|
|
68
|
+
$env:HTTP_PROXY = "http://proxy.company.com:8080"
|
|
69
|
+
$env:HTTPS_PROXY = "http://proxy.company.com:8080"
|
|
70
|
+
$env:NO_PROXY = "localhost,127.0.0.1,.corp.local"
|
|
71
|
+
dominds
|
|
72
|
+
|
|
73
|
+
# macOS / Linux shell
|
|
74
|
+
export HTTP_PROXY="http://proxy.company.com:8080"
|
|
75
|
+
export HTTPS_PROXY="http://proxy.company.com:8080"
|
|
76
|
+
export NO_PROXY="localhost,127.0.0.1,.corp.local"
|
|
77
|
+
dominds
|
|
78
|
+
|
|
79
|
+
# 关闭 Dominds 的环境代理支持
|
|
80
|
+
export DOMINDS_USE_ENV_PROXY=0
|
|
81
|
+
dominds
|
|
82
|
+
```
|
|
83
|
+
|
|
37
84
|
### 安装 Dominds
|
|
38
85
|
|
|
39
86
|
```bash
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -62,8 +62,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
62
62
|
};
|
|
63
63
|
})();
|
|
64
64
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
65
|
+
exports.configureEnvProxySupport = configureEnvProxySupport;
|
|
65
66
|
exports.main = main;
|
|
66
67
|
const fs = __importStar(require("fs"));
|
|
68
|
+
const http = __importStar(require("node:http"));
|
|
67
69
|
const path = __importStar(require("path"));
|
|
68
70
|
const runtime_1 = require("./apps/runtime");
|
|
69
71
|
const dotenv_1 = require("./bootstrap/dotenv");
|
|
@@ -81,6 +83,24 @@ const update_1 = require("./cli/update");
|
|
|
81
83
|
const validate_team_def_1 = require("./cli/validate-team-def");
|
|
82
84
|
const webui_1 = require("./cli/webui");
|
|
83
85
|
require("./tools/builtins");
|
|
86
|
+
function configureEnvProxySupport() {
|
|
87
|
+
const domindsUseEnvProxy = process.env.DOMINDS_USE_ENV_PROXY?.trim();
|
|
88
|
+
if (domindsUseEnvProxy === '0') {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const setGlobalProxyFromEnv = http.setGlobalProxyFromEnv;
|
|
93
|
+
if (typeof setGlobalProxyFromEnv !== 'function') {
|
|
94
|
+
console.error('Error: DOMINDS_USE_ENV_PROXY requires Node.js 24.5+ because http.setGlobalProxyFromEnv() is unavailable.');
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
setGlobalProxyFromEnv(process.env);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
console.error('Error: invalid proxy environment configuration:', err instanceof Error ? err.message : String(err));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
84
104
|
function printHelp() {
|
|
85
105
|
console.log(`
|
|
86
106
|
Dominds CLI - AI-driven DevOps framework with persistent memory
|
|
@@ -163,6 +183,7 @@ async function main() {
|
|
|
163
183
|
}
|
|
164
184
|
}
|
|
165
185
|
(0, dotenv_1.loadRtwsDotenv)({ cwd: process.cwd() });
|
|
186
|
+
configureEnvProxySupport();
|
|
166
187
|
await runSubcommand('webui', []);
|
|
167
188
|
return;
|
|
168
189
|
}
|
|
@@ -214,6 +235,7 @@ async function main() {
|
|
|
214
235
|
// Precedence: `.env` then `.env.local` (later overwrites earlier), and both
|
|
215
236
|
// overwrite any existing process.env values.
|
|
216
237
|
(0, dotenv_1.loadRtwsDotenv)({ cwd: process.cwd() });
|
|
238
|
+
configureEnvProxySupport();
|
|
217
239
|
}
|
|
218
240
|
const shouldLoadApps = subcommand !== 'webui' &&
|
|
219
241
|
subcommand !== 'create' &&
|
|
@@ -51,7 +51,7 @@ export declare function broadcastDisplayStateMarker(dialogId: DialogID, marker:
|
|
|
51
51
|
reason?: DialogInterruptionReason;
|
|
52
52
|
}): void;
|
|
53
53
|
export declare function computeIdleDisplayState(dlg: Dialog): Promise<DialogDisplayState>;
|
|
54
|
-
export declare function refreshRunControlProjectionFromPersistenceFacts(dialogId: DialogID, trigger: 'resume_dialog' | 'resume_all' | 'run_control_snapshot' | 'active_callee_dispatches_changed' | 'q4h_changed'): Promise<DialogLatestFile | null>;
|
|
54
|
+
export declare function refreshRunControlProjectionFromPersistenceFacts(dialogId: DialogID, trigger: 'resume_dialog' | 'resume_all' | 'run_control_snapshot' | 'active_callee_dispatches_changed' | 'q4h_changed' | 'restart_reconciliation'): Promise<DialogLatestFile | null>;
|
|
55
55
|
export declare function reconcileDisplayStatesAfterRestart(): Promise<void>;
|
|
56
56
|
export declare function requestInterruptDialog(dialogId: DialogID, reason: StopRequestedReason): Promise<{
|
|
57
57
|
applied: boolean;
|
|
@@ -776,7 +776,7 @@ async function reconcileDisplayStatesAfterRestart() {
|
|
|
776
776
|
}));
|
|
777
777
|
}
|
|
778
778
|
catch (err) {
|
|
779
|
-
log.warn('Failed to preserve
|
|
779
|
+
log.warn('Failed to preserve open-generation dialog for auto-drive after restart', err, {
|
|
780
780
|
dialogId: dialogId.valueOf(),
|
|
781
781
|
});
|
|
782
782
|
}
|
|
@@ -805,7 +805,21 @@ async function reconcileDisplayStatesAfterRestart() {
|
|
|
805
805
|
}));
|
|
806
806
|
}
|
|
807
807
|
catch (err) {
|
|
808
|
-
log.warn('Failed to reconcile
|
|
808
|
+
log.warn('Failed to reconcile open-generation dialog after restart', err, {
|
|
809
|
+
dialogId: dialogId.valueOf(),
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
if (latest.userWait?.kind === 'awaiting_user_answer' ||
|
|
815
|
+
latest.pendingRuntimePrompt !== undefined ||
|
|
816
|
+
latest.executionMarker?.kind === 'interrupted' ||
|
|
817
|
+
isNonIdleDisplayProjection(latest.displayState)) {
|
|
818
|
+
try {
|
|
819
|
+
await refreshRunControlProjectionFromPersistenceFacts(dialogId, 'restart_reconciliation');
|
|
820
|
+
}
|
|
821
|
+
catch (err) {
|
|
822
|
+
log.warn('Failed to refresh stale run-control projection after restart', err, {
|
|
809
823
|
dialogId: dialogId.valueOf(),
|
|
810
824
|
});
|
|
811
825
|
}
|
|
@@ -3,6 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.hasRecoverableGenerationBeyondFinalResponse = hasRecoverableGenerationBeyondFinalResponse;
|
|
4
4
|
exports.hasDurableDriveWork = hasDurableDriveWork;
|
|
5
5
|
const dialog_generation_run_1 = require("./dialog-generation-run");
|
|
6
|
+
// Backend drive work is a wake routing filter only. It must stay deliberately shallow: this module
|
|
7
|
+
// says "some persisted continuation source may need a handler", not "the dialog has business value
|
|
8
|
+
// to drive". Each concrete continuation handler must locally re-read and claim its own business
|
|
9
|
+
// facts before starting or continuing a generation. Do not add generic de-dup/fingerprint/cleanup
|
|
10
|
+
// policy here; that would merge unrelated business continuations into one spaghetti gate.
|
|
6
11
|
function hasResultArrivalTrigger(latest) {
|
|
7
12
|
return latest.nextStep.triggers.some((trigger) => trigger.kind === 'result_arrival');
|
|
8
13
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { type MainDialog } from './dialog';
|
|
2
2
|
export type DriveTriggerEvent = Readonly<{
|
|
3
3
|
type: 'drive_trigger_evt';
|
|
4
|
-
action: '
|
|
4
|
+
action: 'queue_root_drive' | 'clear_root_drive_queue' | 'active_run_cleared';
|
|
5
5
|
rootId: string;
|
|
6
6
|
entryFound: boolean;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
previousDriveQueued: boolean | null;
|
|
8
|
+
nextDriveQueued: boolean;
|
|
9
9
|
source: string;
|
|
10
10
|
reason: string;
|
|
11
11
|
emittedAtMs: number;
|
|
@@ -20,7 +20,8 @@ declare class GlobalDialogRegistry {
|
|
|
20
20
|
* Runtime-owned roots keyed by rootId.
|
|
21
21
|
*
|
|
22
22
|
* Do not add an API that enumerates this map for backend driving. A dialog becomes driveable only
|
|
23
|
-
* through an explicit business/runtime
|
|
23
|
+
* through an explicit business/runtime scheduling event (`queueRootDrive`, result arrival,
|
|
24
|
+
* active-run clear, etc.).
|
|
24
25
|
* Reintroducing "scan every loaded root and see what happens" makes hidden polling loops easy to
|
|
25
26
|
* create and obscures the business event that should own the next action.
|
|
26
27
|
*/
|
|
@@ -40,12 +41,12 @@ declare class GlobalDialogRegistry {
|
|
|
40
41
|
private compactSparseQueuedRootsIfNeeded;
|
|
41
42
|
private publishDriveTrigger;
|
|
42
43
|
waitForDriveTrigger(): Promise<DriveTriggerEvent>;
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
queueRootDrive(rootId: string, meta?: DriveTriggerMeta): void;
|
|
45
|
+
clearRootDriveQueue(rootId: string, meta?: DriveTriggerMeta): void;
|
|
45
46
|
notifyActiveRunCleared(rootId: string, meta?: DriveTriggerMeta): void;
|
|
46
47
|
noteActiveRunBlockedQueuedDrive(rootId: string): void;
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
hasPendingActiveRunClearedDrive(rootId: string): boolean;
|
|
49
|
+
isRootDriveQueued(rootId: string): boolean;
|
|
49
50
|
getLastDriveTrigger(rootId: string): DriveTriggerEvent | undefined;
|
|
50
51
|
consumeQueuedMainDialogs(): MainDialog[];
|
|
51
52
|
get size(): number;
|
|
@@ -12,7 +12,8 @@ class GlobalDialogRegistry {
|
|
|
12
12
|
* Runtime-owned roots keyed by rootId.
|
|
13
13
|
*
|
|
14
14
|
* Do not add an API that enumerates this map for backend driving. A dialog becomes driveable only
|
|
15
|
-
* through an explicit business/runtime
|
|
15
|
+
* through an explicit business/runtime scheduling event (`queueRootDrive`, result arrival,
|
|
16
|
+
* active-run clear, etc.).
|
|
16
17
|
* Reintroducing "scan every loaded root and see what happens" makes hidden polling loops easy to
|
|
17
18
|
* create and obscures the business event that should own the next action.
|
|
18
19
|
*/
|
|
@@ -45,24 +46,24 @@ class GlobalDialogRegistry {
|
|
|
45
46
|
}
|
|
46
47
|
this.entries.set(mainDialog.id.rootId, {
|
|
47
48
|
mainDialog,
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
driveQueued: false,
|
|
50
|
+
activeRunClearedDrivePending: false,
|
|
50
51
|
});
|
|
51
52
|
void (async () => {
|
|
52
53
|
try {
|
|
53
54
|
const hasPendingNextStepTriggers = await persistence_1.DialogPersistence.hasPendingNextStepTriggers(mainDialog.id);
|
|
54
|
-
const
|
|
55
|
-
if (hasPendingNextStepTriggers ||
|
|
56
|
-
this.
|
|
55
|
+
const wakeQueueEntries = await persistence_1.DialogPersistence.loadWakeQueueEntries(mainDialog.id);
|
|
56
|
+
if (hasPendingNextStepTriggers || wakeQueueEntries.length > 0) {
|
|
57
|
+
this.queueRootDrive(mainDialog.id.rootId, {
|
|
57
58
|
source: 'dialog_registry_hydration',
|
|
58
59
|
reason: hasPendingNextStepTriggers
|
|
59
60
|
? 'persisted_next_step_triggers'
|
|
60
|
-
: '
|
|
61
|
+
: 'persisted_wake_queue',
|
|
61
62
|
});
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
catch (error) {
|
|
65
|
-
log.warn('Failed to hydrate persisted drive
|
|
66
|
+
log.warn('Failed to hydrate persisted root drive queue for registered main dialog', error, {
|
|
66
67
|
rootId: mainDialog.id.rootId,
|
|
67
68
|
selfId: mainDialog.id.selfId,
|
|
68
69
|
});
|
|
@@ -116,8 +117,8 @@ class GlobalDialogRegistry {
|
|
|
116
117
|
action: args.action,
|
|
117
118
|
rootId: args.rootId,
|
|
118
119
|
entryFound: args.entryFound,
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
previousDriveQueued: args.previousDriveQueued,
|
|
121
|
+
nextDriveQueued: args.nextDriveQueued,
|
|
121
122
|
source: args.meta.source,
|
|
122
123
|
reason: args.meta.reason,
|
|
123
124
|
emittedAtMs: Date.now(),
|
|
@@ -135,47 +136,47 @@ class GlobalDialogRegistry {
|
|
|
135
136
|
this.driveTriggerSubChan = (0, evt_1.createSubChan)(this.driveTriggerPubChan);
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
|
-
|
|
139
|
+
queueRootDrive(rootId, meta) {
|
|
139
140
|
const triggerMeta = meta ?? {
|
|
140
141
|
source: 'unknown',
|
|
141
142
|
reason: 'unspecified',
|
|
142
143
|
};
|
|
143
144
|
const entry = this.entries.get(rootId);
|
|
144
|
-
const
|
|
145
|
+
const previousDriveQueued = entry ? entry.driveQueued : null;
|
|
145
146
|
if (entry) {
|
|
146
|
-
entry.
|
|
147
|
-
// A fresh queueing
|
|
148
|
-
entry.
|
|
147
|
+
entry.driveQueued = true;
|
|
148
|
+
// A fresh root drive queueing supersedes any earlier "drive once active run clears" debt.
|
|
149
|
+
entry.activeRunClearedDrivePending = false;
|
|
149
150
|
this.enqueueRoot(rootId);
|
|
150
151
|
}
|
|
151
152
|
this.publishDriveTrigger({
|
|
152
|
-
action: '
|
|
153
|
+
action: 'queue_root_drive',
|
|
153
154
|
rootId,
|
|
154
155
|
entryFound: entry !== undefined,
|
|
155
|
-
|
|
156
|
-
|
|
156
|
+
previousDriveQueued,
|
|
157
|
+
nextDriveQueued: true,
|
|
157
158
|
meta: triggerMeta,
|
|
158
159
|
});
|
|
159
160
|
}
|
|
160
|
-
|
|
161
|
+
clearRootDriveQueue(rootId, meta) {
|
|
161
162
|
const triggerMeta = meta ?? {
|
|
162
163
|
source: 'unknown',
|
|
163
164
|
reason: 'unspecified',
|
|
164
165
|
};
|
|
165
166
|
const entry = this.entries.get(rootId);
|
|
166
|
-
const
|
|
167
|
+
const previousDriveQueued = entry ? entry.driveQueued : null;
|
|
167
168
|
if (entry) {
|
|
168
|
-
entry.
|
|
169
|
-
entry.
|
|
169
|
+
entry.driveQueued = false;
|
|
170
|
+
entry.activeRunClearedDrivePending = false;
|
|
170
171
|
this.queuedRootIdSet.delete(rootId);
|
|
171
172
|
this.compactSparseQueuedRootsIfNeeded();
|
|
172
173
|
}
|
|
173
174
|
this.publishDriveTrigger({
|
|
174
|
-
action: '
|
|
175
|
+
action: 'clear_root_drive_queue',
|
|
175
176
|
rootId,
|
|
176
177
|
entryFound: entry !== undefined,
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
previousDriveQueued,
|
|
179
|
+
nextDriveQueued: false,
|
|
179
180
|
meta: triggerMeta,
|
|
180
181
|
});
|
|
181
182
|
}
|
|
@@ -188,20 +189,20 @@ class GlobalDialogRegistry {
|
|
|
188
189
|
if (!entry) {
|
|
189
190
|
return;
|
|
190
191
|
}
|
|
191
|
-
if (!entry.
|
|
192
|
-
entry.
|
|
192
|
+
if (!entry.activeRunClearedDrivePending) {
|
|
193
|
+
entry.activeRunClearedDrivePending = false;
|
|
193
194
|
return;
|
|
194
195
|
}
|
|
195
|
-
const
|
|
196
|
-
entry.
|
|
197
|
-
entry.
|
|
196
|
+
const currentDriveQueued = entry.driveQueued;
|
|
197
|
+
entry.activeRunClearedDrivePending = false;
|
|
198
|
+
entry.driveQueued = true;
|
|
198
199
|
this.enqueueRoot(rootId);
|
|
199
200
|
this.publishDriveTrigger({
|
|
200
201
|
action: 'active_run_cleared',
|
|
201
202
|
rootId,
|
|
202
203
|
entryFound: true,
|
|
203
|
-
|
|
204
|
-
|
|
204
|
+
previousDriveQueued: currentDriveQueued,
|
|
205
|
+
nextDriveQueued: entry.driveQueued,
|
|
205
206
|
meta: triggerMeta,
|
|
206
207
|
});
|
|
207
208
|
}
|
|
@@ -210,13 +211,13 @@ class GlobalDialogRegistry {
|
|
|
210
211
|
if (!entry) {
|
|
211
212
|
return;
|
|
212
213
|
}
|
|
213
|
-
entry.
|
|
214
|
+
entry.activeRunClearedDrivePending = true;
|
|
214
215
|
}
|
|
215
|
-
|
|
216
|
-
return this.entries.get(rootId)?.
|
|
216
|
+
hasPendingActiveRunClearedDrive(rootId) {
|
|
217
|
+
return this.entries.get(rootId)?.activeRunClearedDrivePending === true;
|
|
217
218
|
}
|
|
218
|
-
|
|
219
|
-
return this.entries.get(rootId)?.
|
|
219
|
+
isRootDriveQueued(rootId) {
|
|
220
|
+
return this.entries.get(rootId)?.driveQueued === true;
|
|
220
221
|
}
|
|
221
222
|
getLastDriveTrigger(rootId) {
|
|
222
223
|
return this.lastDriveTriggerByRootId.get(rootId);
|
|
@@ -234,7 +235,7 @@ class GlobalDialogRegistry {
|
|
|
234
235
|
if (!entry) {
|
|
235
236
|
continue;
|
|
236
237
|
}
|
|
237
|
-
entry.
|
|
238
|
+
entry.driveQueued = false;
|
|
238
239
|
queued.push(entry.mainDialog);
|
|
239
240
|
}
|
|
240
241
|
this.compactQueuedRootsIfNeeded();
|
|
@@ -35,6 +35,13 @@ async function resolvePendingRuntimePromptForRestore(args) {
|
|
|
35
35
|
if (alreadyPersisted) {
|
|
36
36
|
if (args.status === 'running') {
|
|
37
37
|
await persistence_1.DialogPersistence.clearPendingRuntimePrompt(args.dialogId, pending.msgId, args.status);
|
|
38
|
+
const latestAfterClear = await persistence_1.DialogPersistence.loadDialogLatest(args.dialogId, args.status);
|
|
39
|
+
if (latestAfterClear) {
|
|
40
|
+
await persistence_1.DialogPersistence.syncWakeQueueForDialogLatest(args.dialogId, latestAfterClear, args.status);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
await persistence_1.DialogPersistence.removeWakeQueueEntriesForDialog(args.dialogId, args.status);
|
|
44
|
+
}
|
|
38
45
|
}
|
|
39
46
|
return { pendingRuntimePrompt: undefined };
|
|
40
47
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DialogInterruptionReason } from '@longrun-ai/kernel/types/display-state';
|
|
2
|
+
import type { DialogExecutionMarker } from '@longrun-ai/kernel/types/storage';
|
|
2
3
|
/**
|
|
3
4
|
* Decides whether a finalized stopped dialog should expose manual Continue.
|
|
4
5
|
*
|
|
@@ -17,3 +18,4 @@ import type { DialogInterruptionReason } from '@longrun-ai/kernel/types/display-
|
|
|
17
18
|
*/
|
|
18
19
|
export declare function isInterruptionReasonManualResumeEligible(reason: DialogInterruptionReason): boolean;
|
|
19
20
|
export declare function doesInterruptionReasonRequireExplicitResume(reason: DialogInterruptionReason): boolean;
|
|
21
|
+
export declare function isInterruptedDialogBlockedWithoutExplicitResume(marker: DialogExecutionMarker | undefined, explicitResumeAuthorized: boolean): boolean;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isInterruptionReasonManualResumeEligible = isInterruptionReasonManualResumeEligible;
|
|
4
4
|
exports.doesInterruptionReasonRequireExplicitResume = doesInterruptionReasonRequireExplicitResume;
|
|
5
|
+
exports.isInterruptedDialogBlockedWithoutExplicitResume = isInterruptedDialogBlockedWithoutExplicitResume;
|
|
5
6
|
/**
|
|
6
7
|
* Decides whether a finalized stopped dialog should expose manual Continue.
|
|
7
8
|
*
|
|
@@ -53,3 +54,8 @@ function doesInterruptionReasonRequireExplicitResume(reason) {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
}
|
|
57
|
+
function isInterruptedDialogBlockedWithoutExplicitResume(marker, explicitResumeAuthorized) {
|
|
58
|
+
return (marker?.kind === 'interrupted' &&
|
|
59
|
+
doesInterruptionReasonRequireExplicitResume(marker.reason) &&
|
|
60
|
+
!explicitResumeAuthorized);
|
|
61
|
+
}
|
package/dist/dialog.d.ts
CHANGED
|
@@ -150,7 +150,7 @@ export declare abstract class Dialog {
|
|
|
150
150
|
protected _lastUserLanguageCode: LanguageCode;
|
|
151
151
|
protected _lastContextHealth?: ContextHealthSnapshot;
|
|
152
152
|
protected _lastContextHealthGenseq?: number;
|
|
153
|
-
protected
|
|
153
|
+
protected _queuedPrompts: DialogQueuedPromptState[];
|
|
154
154
|
protected _driveIntents: DriveIntent[];
|
|
155
155
|
protected _activeRunControlSpec?: DialogRunControlSpec;
|
|
156
156
|
protected _newCourseHook?: NewCourseHook;
|
|
@@ -327,7 +327,7 @@ export declare abstract class Dialog {
|
|
|
327
327
|
private setPendingRuntimePrompt;
|
|
328
328
|
private mergePromptQ4HAnswerCallId;
|
|
329
329
|
private enqueueQueuedPromptState;
|
|
330
|
-
private
|
|
330
|
+
private peekLatestQueuedPrompt;
|
|
331
331
|
queueUserPromptAtGenerationBoundary(options: {
|
|
332
332
|
prompt: string;
|
|
333
333
|
contentItems?: DialogQueuedUserGenerationBoundaryState['contentItems'];
|
|
@@ -372,9 +372,9 @@ export declare abstract class Dialog {
|
|
|
372
372
|
skipTaskdoc?: boolean;
|
|
373
373
|
calleeDialogReplyTarget: DialogCalleeReplyTarget;
|
|
374
374
|
}): Promise<DialogQueuedPromptState>;
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
375
|
+
hasQueuedPrompt(): boolean;
|
|
376
|
+
peekQueuedPrompt(): DialogQueuedPromptState | undefined;
|
|
377
|
+
takeQueuedPrompt(): DialogQueuedPromptState | undefined;
|
|
378
378
|
setActiveRunControlSpec(spec?: DialogRunControlSpec): void;
|
|
379
379
|
getActiveRunControlSpec(): DialogRunControlSpec | undefined;
|
|
380
380
|
setNewCourseHook(hook?: NewCourseHook): void;
|
|
@@ -392,7 +392,7 @@ export declare abstract class Dialog {
|
|
|
392
392
|
reason?: string;
|
|
393
393
|
skipRunControlHook?: boolean;
|
|
394
394
|
skipEnqueueIntent?: boolean;
|
|
395
|
-
}): Promise<
|
|
395
|
+
}): Promise<DialogRuntimePrompt>;
|
|
396
396
|
receiveFuncResult(result: FuncResultMsg): Promise<void>;
|
|
397
397
|
receiveTellaskResult(result: TellaskResultMsg): Promise<void>;
|
|
398
398
|
receiveTellaskCarryover(result: TellaskCarryoverMsg): Promise<void>;
|