@songsid/agend 2.1.3 → 2.1.4-beta.10
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/LICENSE +21 -0
- package/README.md +2 -2
- package/README.zh-TW.md +2 -2
- package/dist/backend/antigravity.d.ts +1 -0
- package/dist/backend/antigravity.js +1 -0
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +1 -0
- package/dist/backend/claude-code.js +24 -2
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.d.ts +1 -0
- package/dist/backend/codex.js +43 -4
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/gemini-cli.d.ts +1 -0
- package/dist/backend/gemini-cli.js +1 -0
- package/dist/backend/gemini-cli.js.map +1 -1
- package/dist/backend/grok.d.ts +1 -0
- package/dist/backend/grok.js +1 -0
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro.d.ts +1 -0
- package/dist/backend/kiro.js +1 -0
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/mock.d.ts +1 -0
- package/dist/backend/mock.js +1 -0
- package/dist/backend/mock.js.map +1 -1
- package/dist/backend/opencode.d.ts +40 -0
- package/dist/backend/opencode.js +83 -5
- package/dist/backend/opencode.js.map +1 -1
- package/dist/backend/types.d.ts +10 -0
- package/dist/backend/types.js +7 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.js +24 -7
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-server.js +6 -20
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.js +1 -1
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +1 -1
- package/dist/classic-channel-manager.d.ts +34 -3
- package/dist/classic-channel-manager.js +190 -31
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +74 -23
- package/dist/cli.js.map +1 -1
- package/dist/completion-install.d.ts +70 -0
- package/dist/completion-install.js +152 -0
- package/dist/completion-install.js.map +1 -0
- package/dist/completion.d.ts +7 -1
- package/dist/completion.js +14 -4
- package/dist/completion.js.map +1 -1
- package/dist/config-validator.js +35 -7
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/daemon.d.ts +53 -9
- package/dist/daemon.js +222 -86
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-context.d.ts +8 -0
- package/dist/fleet-manager.d.ts +136 -5
- package/dist/fleet-manager.js +1088 -316
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/backend-providers/SKILL.md +114 -0
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +3 -1
- package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +52 -0
- package/dist/general-knowledge/skills/development-workflow/SKILL.md +28 -0
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -0
- package/dist/general-knowledge/skills/fleet-health/SKILL.md +1 -0
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +1 -0
- package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +1 -0
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +1 -0
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -0
- package/dist/general-knowledge/skills/scheduling/SKILL.md +1 -0
- package/dist/general-knowledge/skills/session-management/SKILL.md +1 -0
- package/dist/general-knowledge/skills/tui-effort/SKILL.md +1 -0
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +30 -0
- package/dist/instance-lifecycle.d.ts +10 -0
- package/dist/instance-lifecycle.js +50 -33
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +12 -0
- package/dist/instructions.js +33 -0
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +130 -0
- package/dist/locale.js.map +1 -1
- package/dist/outbound-schemas.d.ts +2 -1
- package/dist/outbound-schemas.js +1 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/quickstart.js +29 -2
- package/dist/quickstart.js.map +1 -1
- package/dist/restart-progress.d.ts +7 -1
- package/dist/restart-progress.js +27 -6
- package/dist/restart-progress.js.map +1 -1
- package/dist/setup-wizard.d.ts +4 -2
- package/dist/setup-wizard.js +2 -2
- package/dist/setup-wizard.js.map +1 -1
- package/dist/tool-progress.d.ts +40 -0
- package/dist/tool-progress.js +289 -0
- package/dist/tool-progress.js.map +1 -0
- package/dist/topic-commands.d.ts +26 -0
- package/dist/topic-commands.js +117 -1
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-monitor.d.ts +15 -2
- package/dist/transcript-monitor.js +63 -17
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +106 -0
- package/dist/transcript-sources.js +428 -0
- package/dist/transcript-sources.js.map +1 -0
- package/dist/types.d.ts +8 -0
- package/dist/ui/dashboard.html +5 -4
- package/dist/update-marker.d.ts +30 -0
- package/dist/update-marker.js +71 -20
- package/dist/update-marker.js.map +1 -1
- package/dist/update-progress.d.ts +6 -0
- package/dist/update-progress.js +29 -0
- package/dist/update-progress.js.map +1 -0
- package/dist/web-api.js +2 -2
- package/dist/web-api.js.map +1 -1
- package/dist/workflow-templates/default.md +2 -1
- package/package.json +2 -2
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-providers
|
|
3
|
+
description: Configure custom model providers when creating or editing AgEnD Codex or OpenCode instances. Use for local, OpenAI-compatible, GLM, or other non-default provider endpoints and for backend_options provider selection.
|
|
4
|
+
roles: [general]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Backend Providers
|
|
8
|
+
|
|
9
|
+
AgEnD selects a provider; the backend CLI owns endpoint and credential definitions.
|
|
10
|
+
|
|
11
|
+
## Choose the backend
|
|
12
|
+
|
|
13
|
+
| Backend | Provider configuration |
|
|
14
|
+
|---|---|
|
|
15
|
+
| Codex | Select with `backend_options.codex.provider`; define the provider in Codex `config.toml`. |
|
|
16
|
+
| OpenCode | Define the provider in `opencode.json`; use model ID `<provider>/<model>`. |
|
|
17
|
+
| Claude Code, Kiro CLI, Antigravity, Grok | Provider is fixed by the CLI; do not set `backend_options` for provider selection. |
|
|
18
|
+
|
|
19
|
+
## Create a Codex custom-provider instance
|
|
20
|
+
|
|
21
|
+
1. Define the provider in the fleet user's `~/.codex/config.toml`. The table name is the provider ID used by AgEnD:
|
|
22
|
+
|
|
23
|
+
```toml
|
|
24
|
+
[model_providers.glm]
|
|
25
|
+
name = "GLM"
|
|
26
|
+
base_url = "https://provider.example.com/v1"
|
|
27
|
+
env_key = "GLM_API_KEY"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Keep credentials out of `config.toml`. `env_key` names an environment variable; it is not the secret itself.
|
|
31
|
+
|
|
32
|
+
2. Put the credential in the fleet process environment before startup. `~/.agend/.env` is the usual location:
|
|
33
|
+
|
|
34
|
+
```dotenv
|
|
35
|
+
GLM_API_KEY=replace-with-the-real-secret
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Restart the fleet after changing its environment. Exporting a variable in an unrelated shell after the fleet has started does not update the running fleet process.
|
|
39
|
+
|
|
40
|
+
3. Create the instance with the MCP tool:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"directory": "/home/user/projects/my-project",
|
|
45
|
+
"topic_name": "glm-worker",
|
|
46
|
+
"backend": "codex",
|
|
47
|
+
"model": "GLM-5.2",
|
|
48
|
+
"backend_options": {
|
|
49
|
+
"codex": {
|
|
50
|
+
"provider": "glm"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The equivalent per-instance fleet.yaml override is:
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
instances:
|
|
60
|
+
glm-worker:
|
|
61
|
+
working_directory: /home/user/projects/my-project
|
|
62
|
+
backend: codex
|
|
63
|
+
model: GLM-5.2
|
|
64
|
+
backend_options:
|
|
65
|
+
codex:
|
|
66
|
+
provider: glm
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
AgEnD copies the user's Codex settings into the instance-isolated `CODEX_HOME`, then launches Codex with `model_provider="glm"`. Provider IDs may contain only letters, digits, `_`, and `-`.
|
|
70
|
+
|
|
71
|
+
## Configure OpenCode
|
|
72
|
+
|
|
73
|
+
Define the endpoint, SDK adapter, credentials, and models in OpenCode's `opencode.json` provider block:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"$schema": "https://opencode.ai/config.json",
|
|
78
|
+
"provider": {
|
|
79
|
+
"glm": {
|
|
80
|
+
"npm": "@ai-sdk/openai-compatible",
|
|
81
|
+
"name": "GLM",
|
|
82
|
+
"options": {
|
|
83
|
+
"baseURL": "https://provider.example.com/v1",
|
|
84
|
+
"apiKey": "{env:GLM_API_KEY}"
|
|
85
|
+
},
|
|
86
|
+
"models": {
|
|
87
|
+
"GLM-5.2": {
|
|
88
|
+
"name": "GLM-5.2"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Then use the fully qualified model ID in AgEnD:
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
instances:
|
|
100
|
+
glm-opencode:
|
|
101
|
+
working_directory: /home/user/projects/my-project
|
|
102
|
+
backend: opencode
|
|
103
|
+
model: glm/GLM-5.2
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
OpenCode provider IDs and model IDs come from its own config. Do not use `backend_options.codex.provider` for OpenCode.
|
|
107
|
+
|
|
108
|
+
## Verify and troubleshoot
|
|
109
|
+
|
|
110
|
+
- Run `validate_config` before reload or restart.
|
|
111
|
+
- Use `describe_instance` to verify the effective backend and model after creation.
|
|
112
|
+
- If Codex says the provider is unknown, verify the `[model_providers.<id>]` table is in `~/.codex/config.toml` before the instance starts.
|
|
113
|
+
- If authentication fails, verify the variable named by `env_key` exists in the fleet process environment; never paste the secret into fleet.yaml or `backend_options`.
|
|
114
|
+
- A model-metadata fallback warning can be normal for a custom model with no built-in metadata. Provider connection, authentication, or unsupported-model errors are not normal and should still be investigated.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cross-instance-messaging
|
|
3
3
|
description: Fire-and-queue cross-instance tools — send once, never resend on queued
|
|
4
|
+
roles: [general, worker]
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
## How to send
|
|
@@ -18,5 +19,6 @@ Use fleet tools only (`send_to_instance`, `delegate_task`, `request_information`
|
|
|
18
19
|
|
|
19
20
|
## Task flow
|
|
20
21
|
|
|
21
|
-
- `delegate_task` → silent work → `report_result` (zero ack-only pings)
|
|
22
|
+
- Coordinator flow: `delegate_task` → silent work → `report_result` (zero ack-only pings).
|
|
23
|
+
- Worker flow: finish with `report_result`, or use `request_information` when blocked. A normal worker must not call `delegate_task` or re-delegate work.
|
|
22
24
|
- Cross-instance traffic is `[from:name]` → answer with `send_to_instance` / `report_result`, never `reply`
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: delegation-playbook
|
|
3
|
+
description: Delegation protocol, loop prevention, parallel vs sequential execution, result/failure handling, team management, and instance configuration tips for the fleet coordinator
|
|
4
|
+
roles: [general]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Delegation Protocol
|
|
8
|
+
|
|
9
|
+
Every delegation via send_to_instance() MUST include:
|
|
10
|
+
|
|
11
|
+
1. Task scope — what exactly to do, bounded clearly
|
|
12
|
+
2. Expected output — what to return and in what form
|
|
13
|
+
3. Policy reminder — "Follow Development Workflow policy" (for code tasks)
|
|
14
|
+
|
|
15
|
+
### Loop Prevention
|
|
16
|
+
|
|
17
|
+
- Never re-delegate a task back to the instance that sent it to you
|
|
18
|
+
- If a task has bounced 3 times, stop and solve locally or reduce scope
|
|
19
|
+
|
|
20
|
+
### Execution Strategy
|
|
21
|
+
|
|
22
|
+
- Parallel — use only when tasks are independent with no shared state
|
|
23
|
+
- Sequential — use when one task's output feeds into the next
|
|
24
|
+
|
|
25
|
+
## Result Handling
|
|
26
|
+
|
|
27
|
+
When an instance reports back, classify the outcome:
|
|
28
|
+
|
|
29
|
+
- Success → Summarize key results for user. Omit internal coordination noise.
|
|
30
|
+
- Partial → State what succeeded, what remains, proposed next steps.
|
|
31
|
+
- Failure → Retry up to 2 times. If still failing: try alternative instance, reduce scope, or return partial result clearly marked.
|
|
32
|
+
- No response → Ping again after reasonable wait. If still silent: report to user with options.
|
|
33
|
+
|
|
34
|
+
## Shared Decisions
|
|
35
|
+
|
|
36
|
+
Use post_decision() / list_decisions() for any choice that affects more than 1 instance, changes an API contract, introduces a new dependency, or alters deployment process.
|
|
37
|
+
|
|
38
|
+
When instances disagree, collect both viewpoints, make a decision, and record it via post_decision.
|
|
39
|
+
|
|
40
|
+
## Team Management
|
|
41
|
+
|
|
42
|
+
- Always check existing teams before creating new ones
|
|
43
|
+
- Default to ephemeral teams (created for a specific task, dissolved after completion)
|
|
44
|
+
- Clean up ephemeral teams and instances after task completion
|
|
45
|
+
|
|
46
|
+
## Instance Configuration Tips
|
|
47
|
+
|
|
48
|
+
When users create specialized instances, suggest these configurations:
|
|
49
|
+
|
|
50
|
+
- **Reviewer instances**: Add `pre_task_command: "/chat load reviewer-base"` to reset context before each review, preventing influence from previous conversations.
|
|
51
|
+
- **Collab mode**: For multi-bot channels, use `/collab` to enable @mention-based triggering.
|
|
52
|
+
- **Cost control**: Set per-instance `cost_guard` for expensive backends.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: development-workflow
|
|
3
|
+
description: The fleet-wide code-change policy the coordinator enforces when delegating code tasks — stages, review pairing, merge conditions
|
|
4
|
+
roles: [general]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
All code changes across the fleet should follow this workflow.
|
|
8
|
+
The coordinator enforces compliance but does not perform these steps directly.
|
|
9
|
+
Remind instances of this policy when delegating code tasks.
|
|
10
|
+
|
|
11
|
+
## Workflow Stages
|
|
12
|
+
|
|
13
|
+
Design Proposed → Design Approved → Implementation → Submit for Review → Under Review → Approved → Merge
|
|
14
|
+
|
|
15
|
+
## Policy Rules
|
|
16
|
+
|
|
17
|
+
1. Design before code — developer sends design proposal to reviewer before implementation. Consensus required before proceeding.
|
|
18
|
+
2. Challenger pairing — every code task should have a developer + reviewer. Reviewer actively questions decisions and finds risks.
|
|
19
|
+
3. Verify by execution — backend/CLI changes must be tested by running them. Do not trust documentation alone.
|
|
20
|
+
4. Independent review — every merge requires code review from someone other than the author.
|
|
21
|
+
5. Root cause first — bug fixes require confirmed root cause before proposing a fix.
|
|
22
|
+
6. Merge conditions: tests pass, reviewer approved, branch and worktree cleaned up.
|
|
23
|
+
|
|
24
|
+
## Specialist Instance Rules
|
|
25
|
+
|
|
26
|
+
- Execute within defined scope only
|
|
27
|
+
- Return structured output: result, assumptions, uncertainties, verification status
|
|
28
|
+
- Do NOT create new instances without coordinator approval
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: worker-collaboration
|
|
3
|
+
description: Complete assigned fleet work as a worker, report the result to the assigning instance, and request missing information without taking over coordinator duties.
|
|
4
|
+
roles: [worker]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Worker Collaboration
|
|
8
|
+
|
|
9
|
+
## Complete assigned work
|
|
10
|
+
|
|
11
|
+
- Treat the assignment as the full scope unless the sender explicitly expands it.
|
|
12
|
+
- Work without acknowledgment-only messages. Silence means the task is in progress.
|
|
13
|
+
- Do not call `delegate_task`; delegation and fleet orchestration belong to General.
|
|
14
|
+
|
|
15
|
+
## Request missing information
|
|
16
|
+
|
|
17
|
+
Use `request_information` only when a concrete missing fact blocks safe progress. Ask the assigning instance one focused question and preserve the task correlation ID when the tool supports it.
|
|
18
|
+
|
|
19
|
+
Do not resend after a timeout without checking delivery evidence. A timeout can mean the request was queued or delivered while the caller stopped waiting.
|
|
20
|
+
|
|
21
|
+
## Report the result
|
|
22
|
+
|
|
23
|
+
Call `report_result` exactly once when the assignment is complete or genuinely blocked. Send it to the assigning instance with the original correlation ID. Include:
|
|
24
|
+
|
|
25
|
+
- the conclusion or delivered artifact;
|
|
26
|
+
- material changes and file paths;
|
|
27
|
+
- tests or checks run and their results;
|
|
28
|
+
- remaining risk, blocker, or follow-up, if any.
|
|
29
|
+
|
|
30
|
+
Do not put user-facing results only in terminal text; the fleet report is the delivery channel.
|
|
@@ -49,6 +49,10 @@ export interface LifecycleContext {
|
|
|
49
49
|
touchActivity(name: string): void;
|
|
50
50
|
sendHangNotification(name: string, unchangedForMs?: number): Promise<void>;
|
|
51
51
|
notifyInstanceTopic(name: string, text: string): void;
|
|
52
|
+
/** Notify the blocked instance and offer an interactive assist action in General. */
|
|
53
|
+
notifyInteractivePrompt(name: string, kind: string): Promise<void>;
|
|
54
|
+
/** Notify a clean CLI exit and offer an admin-only restart action in General. */
|
|
55
|
+
notifyNormalExit(name: string): Promise<void>;
|
|
52
56
|
/** True for a dynamic ClassicBot channel instance (not a fleet topic). */
|
|
53
57
|
isClassicInstance?(name: string): boolean;
|
|
54
58
|
/** True while the fleet is stopping on purpose or an `agend update` is running. */
|
|
@@ -71,6 +75,10 @@ export interface LifecycleContext {
|
|
|
71
75
|
export interface IncidentEventSource {
|
|
72
76
|
on(event: string, handler: (...args: any[]) => void): unknown;
|
|
73
77
|
requestPauseWhenIdle(): void;
|
|
78
|
+
/** Present on real daemons; hang buttons attach only when it returns one. */
|
|
79
|
+
getHangDetector?(): {
|
|
80
|
+
on(event: string, handler: (...args: any[]) => void): unknown;
|
|
81
|
+
} | null;
|
|
74
82
|
}
|
|
75
83
|
/** Arguments accepted by handleCreate — mirrors CreateInstanceArgs in outbound-schemas.ts
|
|
76
84
|
* plus internal-only fields forwarded by deploy_template (profile-derived). */
|
|
@@ -80,6 +88,8 @@ export interface LifecycleCreateArgs {
|
|
|
80
88
|
description?: string;
|
|
81
89
|
model?: string;
|
|
82
90
|
backend?: string;
|
|
91
|
+
/** Backend-specific launch options, e.g. { codex: { provider: "glm" } }. */
|
|
92
|
+
backend_options?: Record<string, Record<string, unknown>>;
|
|
83
93
|
branch?: string;
|
|
84
94
|
detach?: boolean;
|
|
85
95
|
worktree_path?: string;
|
|
@@ -165,6 +165,39 @@ export class InstanceLifecycle {
|
|
|
165
165
|
* means a real tmux window, which is why this rule went untested before.
|
|
166
166
|
*/
|
|
167
167
|
attachIncidentHandlers(name, daemon) {
|
|
168
|
+
const hangDetector = daemon.getHangDetector?.();
|
|
169
|
+
if (hangDetector) {
|
|
170
|
+
hangDetector.on("hang", safeHandler(async (data) => {
|
|
171
|
+
this.ctx.eventLog?.insert(name, "hang_detected", {});
|
|
172
|
+
this.ctx.logger.warn({ name }, "Instance appears hung");
|
|
173
|
+
if (this.ctx.isPlannedRestart()) {
|
|
174
|
+
// A graceful stop makes every pane look frozen; alerting on that (with
|
|
175
|
+
// a restart button, no less) during `agend update` is pure noise. The
|
|
176
|
+
// same suppression interactive-prompt and normal-exit already apply.
|
|
177
|
+
// Checked BEFORE the task nudge: injecting new work into a CLI that is
|
|
178
|
+
// being shut down would only delay the stop.
|
|
179
|
+
this.ctx.logger.info({ name }, "Hang notification suppressed — planned restart in progress");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Check if instance has claimed tasks — nudge it to continue
|
|
183
|
+
const claimedTasks = this.ctx.listClaimedTasks(name);
|
|
184
|
+
if (claimedTasks.length > 0) {
|
|
185
|
+
const task = claimedTasks[0];
|
|
186
|
+
this.ctx.eventLog?.insert(name, "idle_task_nudge", { taskId: task.id, taskTitle: task.title });
|
|
187
|
+
// Inject nudge message into the instance's CLI session
|
|
188
|
+
const ipc = this.ctx.instanceIpcClients.get(name);
|
|
189
|
+
if (ipc?.connected) {
|
|
190
|
+
ipc.send({
|
|
191
|
+
type: "fleet_inbound",
|
|
192
|
+
content: `[system] You have a claimed task: "${task.title}" (#${task.id}). Continue working on it, or use task(done) / task(update, status=blocked) to update status.`,
|
|
193
|
+
meta: { chat_id: "", thread_id: "", ts: new Date().toISOString() },
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
await this.ctx.sendHangNotification(name, data?.unchangedForMs);
|
|
198
|
+
this.ctx.webhookEmit("hang", name);
|
|
199
|
+
}, this.ctx.logger, `hangDetector[${name}]`));
|
|
200
|
+
}
|
|
168
201
|
daemon.on("crash_respawn", safeHandler(() => {
|
|
169
202
|
this.ctx.eventLog?.insert(name, "crash_respawn", {});
|
|
170
203
|
this.ctx.logger.warn({ name }, "Instance crashed and respawned");
|
|
@@ -178,11 +211,22 @@ export class InstanceLifecycle {
|
|
|
178
211
|
this.ctx.eventLog?.insert(name, "snapshot_failed", {});
|
|
179
212
|
this.notifyIncident(name, "snapshot_failed", t("inst.restarted_no_context", name));
|
|
180
213
|
}, this.ctx.logger, `daemon.snapshot_failed[${name}]`));
|
|
181
|
-
daemon.on("supervision_ended", safeHandler((data) => {
|
|
214
|
+
daemon.on("supervision_ended", safeHandler(async (data) => {
|
|
182
215
|
// The instance is dead and nothing will restart it. Say so where the operator
|
|
183
216
|
// is looking, and mark the topic — otherwise messages routed here just queue
|
|
184
217
|
// or fail with a bare ❌ and the dashboard still looks normal.
|
|
185
218
|
this.ctx.eventLog?.insert(name, "supervision_ended", { reason: data.reason });
|
|
219
|
+
if (data.exitCode === 0) {
|
|
220
|
+
this.ctx.logger.info({ name, exitCode: data.exitCode }, "CLI exited normally and will not restart automatically");
|
|
221
|
+
if (this.ctx.isPlannedRestart()) {
|
|
222
|
+
this.ctx.logger.info({ name }, "Normal-exit controls suppressed — planned restart in progress");
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
await this.ctx.notifyNormalExit(name);
|
|
226
|
+
}
|
|
227
|
+
this.ctx.setTopicIcon(name, "red");
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
186
230
|
this.ctx.logger.error({ name, reason: data.reason }, "Instance is no longer supervised");
|
|
187
231
|
this.notifyIncident(name, "supervision_ended", `🛑 ${name} is no longer running and will not be restarted automatically — ${data.reason}.\n${data.remedy}`);
|
|
188
232
|
this.ctx.setTopicIcon(name, "red");
|
|
@@ -244,18 +288,14 @@ export class InstanceLifecycle {
|
|
|
244
288
|
// server.
|
|
245
289
|
this.ctx.restartSingleInstance(name).catch(err => this.ctx.logger.error({ err, name }, "MCP auto-restart failed"));
|
|
246
290
|
}, this.ctx.logger, `daemon.mcp_restart_requested[${name}]`));
|
|
247
|
-
daemon.on("interactive_prompt", safeHandler((data) => {
|
|
291
|
+
daemon.on("interactive_prompt", safeHandler(async (data) => {
|
|
248
292
|
this.ctx.eventLog?.insert(name, "interactive_prompt", { kind: data.kind });
|
|
249
293
|
this.ctx.logger.warn({ name, kind: data.kind, prompt: data.prompt }, "Instance is waiting for interactive terminal input");
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
this.ctx.logger.warn({ name }, "Interactive prompt has no General topic notification target");
|
|
294
|
+
if (this.ctx.isPlannedRestart()) {
|
|
295
|
+
this.ctx.logger.info({ name }, "Interactive prompt notification suppressed — planned restart in progress");
|
|
253
296
|
return;
|
|
254
297
|
}
|
|
255
|
-
|
|
256
|
-
? "sudo password"
|
|
257
|
-
: data.kind === "press_enter" ? "Press Enter" : "Y/N confirmation";
|
|
258
|
-
this.notifyIncident(general, "interactive_prompt", `⚠️ \`${name}\` 正在等待人工輸入(${label})。General 可以透過 tmux attach 查看並協助操作。\n請勿在 Telegram/Discord 傳送密碼。`);
|
|
298
|
+
await this.ctx.notifyInteractivePrompt(name, data.kind);
|
|
259
299
|
}, this.ctx.logger, `daemon.interactive_prompt[${name}]`));
|
|
260
300
|
daemon.on("pty_error", safeHandler((data) => {
|
|
261
301
|
this.ctx.eventLog?.insert(name, "pty_error", { type: data.type, action: data.action });
|
|
@@ -353,30 +393,6 @@ export class InstanceLifecycle {
|
|
|
353
393
|
});
|
|
354
394
|
await daemon.start();
|
|
355
395
|
this.daemons.set(name, daemon);
|
|
356
|
-
const hangDetector = daemon.getHangDetector();
|
|
357
|
-
if (hangDetector) {
|
|
358
|
-
hangDetector.on("hang", safeHandler(async (data) => {
|
|
359
|
-
this.ctx.eventLog?.insert(name, "hang_detected", {});
|
|
360
|
-
this.ctx.logger.warn({ name }, "Instance appears hung");
|
|
361
|
-
// Check if instance has claimed tasks — nudge it to continue
|
|
362
|
-
const claimedTasks = this.ctx.listClaimedTasks(name);
|
|
363
|
-
if (claimedTasks.length > 0) {
|
|
364
|
-
const task = claimedTasks[0];
|
|
365
|
-
this.ctx.eventLog?.insert(name, "idle_task_nudge", { taskId: task.id, taskTitle: task.title });
|
|
366
|
-
// Inject nudge message into the instance's CLI session
|
|
367
|
-
const ipc = this.ctx.instanceIpcClients.get(name);
|
|
368
|
-
if (ipc?.connected) {
|
|
369
|
-
ipc.send({
|
|
370
|
-
type: "fleet_inbound",
|
|
371
|
-
content: `[system] You have a claimed task: "${task.title}" (#${task.id}). Continue working on it, or use task(done) / task(update, status=blocked) to update status.`,
|
|
372
|
-
meta: { chat_id: "", thread_id: "", ts: new Date().toISOString() },
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
await this.ctx.sendHangNotification(name, data?.unchangedForMs);
|
|
377
|
-
this.ctx.webhookEmit("hang", name);
|
|
378
|
-
}, this.ctx.logger, `hangDetector[${name}]`));
|
|
379
|
-
}
|
|
380
396
|
daemon.on("auto_pause_requested", safeHandler(async () => {
|
|
381
397
|
await this.pause(name);
|
|
382
398
|
}, this.ctx.logger, `autoPause[${name}]`));
|
|
@@ -786,6 +802,7 @@ export class InstanceLifecycle {
|
|
|
786
802
|
...(systemPrompt ? { systemPrompt } : {}),
|
|
787
803
|
...(args.model ? { model: args.model } : {}),
|
|
788
804
|
...(args.backend ? { backend: args.backend } : {}),
|
|
805
|
+
...(args.backend_options ? { backend_options: args.backend_options } : {}),
|
|
789
806
|
...(args.model_failover ? { model_failover: args.model_failover } : {}),
|
|
790
807
|
...(args.tool_set ? { tool_set: args.tool_set } : {}),
|
|
791
808
|
...(args.skipPermissions != null ? { skipPermissions: args.skipPermissions } : {}),
|