@withakay/opencode-autopilot 0.1.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 +147 -0
- package/dist/index.js +899 -0
- package/dist/index.js.map +26 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# @withakay/opencode-autopilot
|
|
2
|
+
|
|
3
|
+
Autopilot mode plugin for OpenCode — autonomous multi-step task execution with safety guarantees.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @withakay/opencode-autopilot
|
|
9
|
+
# or
|
|
10
|
+
bun add @withakay/opencode-autopilot
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Register the plugin in your `opencode.jsonc`:
|
|
16
|
+
|
|
17
|
+
```jsonc
|
|
18
|
+
{
|
|
19
|
+
"plugin": [
|
|
20
|
+
"@withakay/opencode-autopilot"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The plugin registers four tools:
|
|
26
|
+
|
|
27
|
+
- **`autopilot_start`** — Arm autopilot mode for the current session
|
|
28
|
+
- **`autopilot_status`** — Show autopilot status for the current session
|
|
29
|
+
- **`autopilot_stop`** — Stop autopilot mode for the current session
|
|
30
|
+
- **`autopilot_help`** — Show usage instructions
|
|
31
|
+
|
|
32
|
+
### Control Agent
|
|
33
|
+
|
|
34
|
+
Create an agent in `opencode.jsonc` to control the plugin:
|
|
35
|
+
|
|
36
|
+
```jsonc
|
|
37
|
+
{
|
|
38
|
+
"agent": {
|
|
39
|
+
"autopilot": {
|
|
40
|
+
"description": "Control agent for autopilot plugin",
|
|
41
|
+
"mode": "primary",
|
|
42
|
+
"model": "github-copilot/gpt-5-mini",
|
|
43
|
+
"temperature": 0,
|
|
44
|
+
"prompt": "{file:./prompts/autopilot.txt}",
|
|
45
|
+
"tools": {
|
|
46
|
+
"autopilot_start": true,
|
|
47
|
+
"autopilot_status": true,
|
|
48
|
+
"autopilot_stop": true,
|
|
49
|
+
"autopilot_help": true
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Then switch to the Autopilot agent and send your task.
|
|
57
|
+
|
|
58
|
+
### Permission Modes
|
|
59
|
+
|
|
60
|
+
- **`limited`** (default) — Auto-denies all permission requests; blocks on first denial
|
|
61
|
+
- **`allow-all`** — Auto-allows all permission requests
|
|
62
|
+
|
|
63
|
+
## Architecture
|
|
64
|
+
|
|
65
|
+
The plugin implements an OODA (Observe-Orient-Decide-Act) control loop with a formal state machine.
|
|
66
|
+
|
|
67
|
+
### State Machine Phases
|
|
68
|
+
|
|
69
|
+
| Phase | Description |
|
|
70
|
+
|-------|-------------|
|
|
71
|
+
| `OBSERVE` | Ingest new evidence (user input, tool output, approvals, etc.) |
|
|
72
|
+
| `ORIENT` | Reconcile observations against goal; detect completion or blockers |
|
|
73
|
+
| `DECIDE` | Select exactly one next foreground action |
|
|
74
|
+
| `EXECUTE` | Dispatch the selected action |
|
|
75
|
+
| `EVALUATE` | Classify result as progress, non-progress, or failure |
|
|
76
|
+
| `RECOVER` | Attempt bounded recovery (re-plan, alternate tool, etc.) |
|
|
77
|
+
| `BLOCKED` | Waiting for external input (approval, trust, user input) |
|
|
78
|
+
| `STOPPED` | Terminal state (completed, user stop, error, etc.) |
|
|
79
|
+
|
|
80
|
+
### Safety Invariants (S1-S9)
|
|
81
|
+
|
|
82
|
+
| ID | Property | Enforcement |
|
|
83
|
+
|----|----------|-------------|
|
|
84
|
+
| S1 | No unauthorized side effects | Admissibility guard on all effects |
|
|
85
|
+
| S2 | Approval cannot be bypassed | `approvalRequired()` check in DECIDE |
|
|
86
|
+
| S3 | Trust cannot be bypassed | `trustRequired()` check in DECIDE |
|
|
87
|
+
| S4 | BLOCKED/STOPPED are explicit | `block()` and `stop()` require a reason |
|
|
88
|
+
| S5 | No silent denial loss | Denials preserved as observations in state |
|
|
89
|
+
| S6 | No uncontrolled livelock | Non-progress counter with enforced limit |
|
|
90
|
+
| S7 | STOPPED is quiescent | No effects without resume |
|
|
91
|
+
| S8 | Interrupt preemption | INTERRUPT → STOPPED immediately |
|
|
92
|
+
| S9 | State preserved across risky effects | PERSIST_SNAPSHOT before risky dispatch |
|
|
93
|
+
|
|
94
|
+
## Development
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Install dependencies
|
|
98
|
+
bun install
|
|
99
|
+
|
|
100
|
+
# Type check
|
|
101
|
+
bun run typecheck
|
|
102
|
+
|
|
103
|
+
# Run tests
|
|
104
|
+
bun test
|
|
105
|
+
|
|
106
|
+
# Build
|
|
107
|
+
bun run build
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## File Layout
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
src/
|
|
114
|
+
index.ts # Entry point — exports AutopilotPlugin
|
|
115
|
+
plugin.ts # Plugin function (OpenCode hook wiring)
|
|
116
|
+
types/ # Type definitions
|
|
117
|
+
index.ts, mode.ts, phase.ts, stop-reason.ts,
|
|
118
|
+
event.ts, effect.ts, state.ts, reducer.ts
|
|
119
|
+
state/ # State factory, session cache
|
|
120
|
+
index.ts, factory.ts, session-cache.ts
|
|
121
|
+
reducer/ # Pure reducer functions
|
|
122
|
+
index.ts, reduce.ts, integrate.ts, observe.ts,
|
|
123
|
+
orient.ts, decide.ts, evaluate.ts, recover.ts,
|
|
124
|
+
guards.ts, transitions.ts
|
|
125
|
+
events/ # Event validation, factory, Zod schemas
|
|
126
|
+
index.ts, validate.ts, factory.ts, schemas.ts
|
|
127
|
+
effects/ # Effect dispatcher, snapshot persistence
|
|
128
|
+
index.ts, dispatcher.ts, snapshot.ts
|
|
129
|
+
loop/ # Control loop driver
|
|
130
|
+
index.ts, control-loop.ts
|
|
131
|
+
prompts/ # System prompt, continuation, directives
|
|
132
|
+
index.ts, system-prompt.ts, continuation.ts,
|
|
133
|
+
directives.ts, normalize.ts, format.ts
|
|
134
|
+
hooks/ # OpenCode hook handlers
|
|
135
|
+
index.ts, event-handler.ts, permission.ts,
|
|
136
|
+
system-transform.ts, chat-message.ts, tool-after.ts
|
|
137
|
+
tools/ # Tool definitions
|
|
138
|
+
index.ts, help.ts, start.ts, status.ts, stop.ts
|
|
139
|
+
__tests__/ # All test files
|
|
140
|
+
helpers.ts, reducer.test.ts, events.test.ts,
|
|
141
|
+
effects.test.ts, prompts.test.ts, plugin.test.ts,
|
|
142
|
+
safety.test.ts
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
// state/session-cache.ts
|
|
2
|
+
function createSessionStore() {
|
|
3
|
+
return {
|
|
4
|
+
roles: new Map,
|
|
5
|
+
agents: new Map,
|
|
6
|
+
textParts: new Map
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class SessionCache {
|
|
11
|
+
sessions = new Map;
|
|
12
|
+
ensureSession(sessionID) {
|
|
13
|
+
const existing = this.sessions.get(sessionID);
|
|
14
|
+
if (existing) {
|
|
15
|
+
return existing;
|
|
16
|
+
}
|
|
17
|
+
const created = createSessionStore();
|
|
18
|
+
this.sessions.set(sessionID, created);
|
|
19
|
+
return created;
|
|
20
|
+
}
|
|
21
|
+
setRole(sessionID, messageID, role) {
|
|
22
|
+
this.ensureSession(sessionID).roles.set(messageID, role);
|
|
23
|
+
}
|
|
24
|
+
getRole(sessionID, messageID) {
|
|
25
|
+
return this.sessions.get(sessionID)?.roles.get(messageID) ?? null;
|
|
26
|
+
}
|
|
27
|
+
setAgent(sessionID, messageID, agent) {
|
|
28
|
+
this.ensureSession(sessionID).agents.set(messageID, agent);
|
|
29
|
+
}
|
|
30
|
+
getAgent(sessionID, messageID) {
|
|
31
|
+
return this.sessions.get(sessionID)?.agents.get(messageID) ?? null;
|
|
32
|
+
}
|
|
33
|
+
setTextPart(sessionID, partID, messageID, text) {
|
|
34
|
+
this.ensureSession(sessionID).textParts.set(partID, {
|
|
35
|
+
id: partID,
|
|
36
|
+
messageID,
|
|
37
|
+
text
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
getTextPart(sessionID, partID) {
|
|
41
|
+
return this.sessions.get(sessionID)?.textParts.get(partID) ?? null;
|
|
42
|
+
}
|
|
43
|
+
getMessageText(sessionID, messageID) {
|
|
44
|
+
const session = this.sessions.get(sessionID);
|
|
45
|
+
if (!session) {
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
return [...session.textParts.values()].filter((part) => part.messageID === messageID).map((part) => part.text).join("");
|
|
49
|
+
}
|
|
50
|
+
snapshot(sessionID) {
|
|
51
|
+
const session = this.sessions.get(sessionID) ?? createSessionStore();
|
|
52
|
+
return {
|
|
53
|
+
roles: Object.fromEntries(session.roles),
|
|
54
|
+
agents: Object.fromEntries(session.agents),
|
|
55
|
+
textParts: [...session.textParts.values()]
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
cleanup(sessionID) {
|
|
59
|
+
this.sessions.delete(sessionID);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// state/factory.ts
|
|
64
|
+
var DEFAULT_ALLOWED_TOOLS = ["bash", "read", "glob", "grep", "apply_patch"];
|
|
65
|
+
var DEFAULT_CONTEXT_THRESHOLD = 8000;
|
|
66
|
+
var DEFAULT_MAX_CONTINUES = 25;
|
|
67
|
+
var DEFAULT_MAX_STEP_RETRIES = 2;
|
|
68
|
+
var DEFAULT_MAX_GLOBAL_RETRIES = 6;
|
|
69
|
+
var DEFAULT_MAX_NO_PROGRESS = 3;
|
|
70
|
+
var DEFAULT_WORKER_AGENT = "pi";
|
|
71
|
+
function createPlanState() {
|
|
72
|
+
return {
|
|
73
|
+
steps: [],
|
|
74
|
+
open_items: [],
|
|
75
|
+
completed_items: [],
|
|
76
|
+
blocked_items: [],
|
|
77
|
+
dependencies: {},
|
|
78
|
+
stale: false
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function createApprovalState() {
|
|
82
|
+
return {
|
|
83
|
+
status: "idle",
|
|
84
|
+
pending_action: null,
|
|
85
|
+
pending_scope: null,
|
|
86
|
+
approved_scopes: [],
|
|
87
|
+
denied_scopes: [],
|
|
88
|
+
last_feedback: null
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function createTrustState(trustedPaths) {
|
|
92
|
+
return {
|
|
93
|
+
status: trustedPaths.length > 0 ? "trusted" : "untrusted",
|
|
94
|
+
trusted_paths: [...trustedPaths],
|
|
95
|
+
pending_path: null,
|
|
96
|
+
denied_paths: [],
|
|
97
|
+
last_feedback: null
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function createContextState(remainingBudget, threshold) {
|
|
101
|
+
const resolvedBudget = remainingBudget ?? null;
|
|
102
|
+
const compactionNeeded = resolvedBudget !== null ? resolvedBudget <= threshold : false;
|
|
103
|
+
return {
|
|
104
|
+
remaining_budget: resolvedBudget,
|
|
105
|
+
threshold,
|
|
106
|
+
compaction_needed: compactionNeeded,
|
|
107
|
+
compacted_at: null,
|
|
108
|
+
unsafe_to_continue: compactionNeeded
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function createRetryCounters(options) {
|
|
112
|
+
return {
|
|
113
|
+
step_retry_count: 0,
|
|
114
|
+
global_retry_count: 0,
|
|
115
|
+
no_progress_count: 0,
|
|
116
|
+
recovery_attempt_count: 0,
|
|
117
|
+
max_step_retries: options.maxStepRetries ?? DEFAULT_MAX_STEP_RETRIES,
|
|
118
|
+
max_global_retries: options.maxGlobalRetries ?? DEFAULT_MAX_GLOBAL_RETRIES,
|
|
119
|
+
max_no_progress: options.maxNoProgress ?? DEFAULT_MAX_NO_PROGRESS
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function createInitialState(goal, options = {}) {
|
|
123
|
+
const sessionID = options.sessionID ?? "";
|
|
124
|
+
const allowedPaths = options.allowedPaths ?? [];
|
|
125
|
+
return {
|
|
126
|
+
session_id: sessionID,
|
|
127
|
+
mode: options.mode ?? "DISABLED",
|
|
128
|
+
phase: options.phase ?? "STOPPED",
|
|
129
|
+
goal,
|
|
130
|
+
plan_state: createPlanState(),
|
|
131
|
+
completion_evidence: [],
|
|
132
|
+
allowed_tools: [...options.allowedTools ?? DEFAULT_ALLOWED_TOOLS],
|
|
133
|
+
allowed_paths: [...allowedPaths],
|
|
134
|
+
approval_state: createApprovalState(),
|
|
135
|
+
trust_state: createTrustState(options.trustedPaths ?? allowedPaths),
|
|
136
|
+
context_state: createContextState(options.remainingBudget ?? null, options.contextThreshold ?? DEFAULT_CONTEXT_THRESHOLD),
|
|
137
|
+
foreground_action: null,
|
|
138
|
+
background_tasks: [],
|
|
139
|
+
retry_counters: createRetryCounters(options),
|
|
140
|
+
stop_reason: null,
|
|
141
|
+
latest_observations: {
|
|
142
|
+
events: [],
|
|
143
|
+
last_user_input: null,
|
|
144
|
+
last_tool_result: null,
|
|
145
|
+
last_tool_error: null,
|
|
146
|
+
last_interrupt: null
|
|
147
|
+
},
|
|
148
|
+
continuation_count: 0,
|
|
149
|
+
max_continues: options.maxContinues ?? DEFAULT_MAX_CONTINUES,
|
|
150
|
+
worker_agent: options.workerAgent ?? DEFAULT_WORKER_AGENT,
|
|
151
|
+
last_updated_at: null,
|
|
152
|
+
resumable: true
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function createSessionState(sessionID, goal, options = {}) {
|
|
156
|
+
return createInitialState(goal, {
|
|
157
|
+
...options,
|
|
158
|
+
sessionID,
|
|
159
|
+
mode: options.mode ?? "ENABLED",
|
|
160
|
+
phase: options.phase ?? "OBSERVE"
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// prompts/continuation.ts
|
|
165
|
+
function buildContinuationPrompt(options) {
|
|
166
|
+
return [
|
|
167
|
+
`Autopilot continuation ${options.continueCount}/${options.maxContinues}.`,
|
|
168
|
+
"Continue working autonomously on the same task.",
|
|
169
|
+
`Original task: ${options.task}`,
|
|
170
|
+
"Review your latest progress, choose the next concrete step, and keep moving without waiting for the user.",
|
|
171
|
+
"If you are done, provide the result and mark the response complete.",
|
|
172
|
+
"If you are blocked by missing information or denied permissions, explain the blocker and mark the response blocked."
|
|
173
|
+
].join(`
|
|
174
|
+
`);
|
|
175
|
+
}
|
|
176
|
+
// prompts/directives.ts
|
|
177
|
+
var AUTOPILOT_MARKER_RE = /\n?<autopilot\s+status="(continue|complete|blocked)">([\s\S]*?)<\/autopilot>\s*$/i;
|
|
178
|
+
var BLOCKED_HINT_RE = /(need (more|additional) information|cannot continue|can't continue|blocked|waiting for user|please provide|which option|what should i|what would you like)/i;
|
|
179
|
+
function parseAutopilotMarker(text) {
|
|
180
|
+
const match = text.match(AUTOPILOT_MARKER_RE);
|
|
181
|
+
if (!match) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const [, rawStatus, rawReason] = match;
|
|
185
|
+
if (!rawStatus || rawReason === undefined) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const status = rawStatus.toLowerCase();
|
|
189
|
+
return {
|
|
190
|
+
status,
|
|
191
|
+
reason: rawReason.trim() || defaultReason(status)
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function stripAutopilotMarker(text) {
|
|
195
|
+
return text.replace(AUTOPILOT_MARKER_RE, "").trimEnd();
|
|
196
|
+
}
|
|
197
|
+
function inferAutopilotDirective(text) {
|
|
198
|
+
const marker = parseAutopilotMarker(text);
|
|
199
|
+
if (marker) {
|
|
200
|
+
return marker;
|
|
201
|
+
}
|
|
202
|
+
const source = text.trim();
|
|
203
|
+
if (!source) {
|
|
204
|
+
return {
|
|
205
|
+
status: "blocked",
|
|
206
|
+
reason: "Assistant returned no usable response."
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (BLOCKED_HINT_RE.test(source)) {
|
|
210
|
+
return {
|
|
211
|
+
status: "blocked",
|
|
212
|
+
reason: "Assistant requested input or reported it could not continue."
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
status: "continue",
|
|
217
|
+
reason: "No autopilot marker emitted; continuing with fallback policy."
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function defaultReason(status) {
|
|
221
|
+
if (status === "complete") {
|
|
222
|
+
return "Task complete.";
|
|
223
|
+
}
|
|
224
|
+
if (status === "blocked") {
|
|
225
|
+
return "Task is blocked.";
|
|
226
|
+
}
|
|
227
|
+
return "More work remains.";
|
|
228
|
+
}
|
|
229
|
+
// prompts/format.ts
|
|
230
|
+
function formatUsageMetadata(usage) {
|
|
231
|
+
if (!usage) {
|
|
232
|
+
return "";
|
|
233
|
+
}
|
|
234
|
+
const parts = [];
|
|
235
|
+
if (usage.tokens) {
|
|
236
|
+
const { total, input, output } = usage.tokens;
|
|
237
|
+
if (Number.isFinite(total)) {
|
|
238
|
+
parts.push(`${total} tokens`);
|
|
239
|
+
} else if (Number.isFinite(input) || Number.isFinite(output)) {
|
|
240
|
+
const tokenParts = [];
|
|
241
|
+
if (Number.isFinite(input)) {
|
|
242
|
+
tokenParts.push(`in ${input}`);
|
|
243
|
+
}
|
|
244
|
+
if (Number.isFinite(output)) {
|
|
245
|
+
tokenParts.push(`out ${output}`);
|
|
246
|
+
}
|
|
247
|
+
if (tokenParts.length > 0) {
|
|
248
|
+
parts.push(tokenParts.join(", "));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const totalCost = usage.cost?.total;
|
|
253
|
+
if (Number.isFinite(totalCost)) {
|
|
254
|
+
parts.push(`cost ${totalCost}`);
|
|
255
|
+
}
|
|
256
|
+
return parts.join("; ");
|
|
257
|
+
}
|
|
258
|
+
function summarizeAutopilotState(state) {
|
|
259
|
+
if (!state) {
|
|
260
|
+
return "Autopilot is idle.";
|
|
261
|
+
}
|
|
262
|
+
const status = [
|
|
263
|
+
`phase=${state.phase}`,
|
|
264
|
+
`mode=${state.mode}`,
|
|
265
|
+
`continues=${state.continuation_count}/${state.max_continues}`,
|
|
266
|
+
`agent=${state.worker_agent}`
|
|
267
|
+
];
|
|
268
|
+
if (state.stop_reason) {
|
|
269
|
+
status.push(`stop=${state.stop_reason}`);
|
|
270
|
+
}
|
|
271
|
+
const latestEvent = state.latest_observations.events.at(-1);
|
|
272
|
+
if (latestEvent) {
|
|
273
|
+
status.push(`last_event=${latestEvent.event_type}`);
|
|
274
|
+
}
|
|
275
|
+
return `Autopilot status: ${status.join(", ")}`;
|
|
276
|
+
}
|
|
277
|
+
// prompts/normalize.ts
|
|
278
|
+
var AUTOPILOT_DEFAULT_MAX_CONTINUES = 10;
|
|
279
|
+
var AUTOPILOT_MAX_CONTINUES_HARD_LIMIT = 50;
|
|
280
|
+
function normalizeMaxContinues(value) {
|
|
281
|
+
const numeric = Number(value);
|
|
282
|
+
if (!Number.isFinite(numeric) || numeric <= 0) {
|
|
283
|
+
return AUTOPILOT_DEFAULT_MAX_CONTINUES;
|
|
284
|
+
}
|
|
285
|
+
return Math.min(AUTOPILOT_MAX_CONTINUES_HARD_LIMIT, Math.max(1, Math.floor(numeric)));
|
|
286
|
+
}
|
|
287
|
+
// prompts/system-prompt.ts
|
|
288
|
+
function buildAutopilotSystemPrompt() {
|
|
289
|
+
return [
|
|
290
|
+
"Autopilot mode is active for this session.",
|
|
291
|
+
"Work autonomously toward the user's goal without asking follow-up questions unless you are truly blocked.",
|
|
292
|
+
"At the very end of every assistant response, append exactly one machine-readable status marker on its own line using this format:",
|
|
293
|
+
'<autopilot status="continue|complete|blocked">short reason</autopilot>',
|
|
294
|
+
"Use continue when more work remains and you can keep going without the user.",
|
|
295
|
+
"Use complete when the task is done or you have reached a stable handoff point.",
|
|
296
|
+
"Use blocked when missing information, denied permissions, or an external failure prevents meaningful progress.",
|
|
297
|
+
"Do not omit the marker."
|
|
298
|
+
].join(`
|
|
299
|
+
`);
|
|
300
|
+
}
|
|
301
|
+
// hooks/event-handler.ts
|
|
302
|
+
function createSessionTracking() {
|
|
303
|
+
return {
|
|
304
|
+
lastAssistantMessageID: undefined,
|
|
305
|
+
lastUsage: undefined,
|
|
306
|
+
awaitingWorkerReply: false,
|
|
307
|
+
blockedByPermission: false,
|
|
308
|
+
permissionBlockMessage: undefined
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function isString(value) {
|
|
312
|
+
return typeof value === "string";
|
|
313
|
+
}
|
|
314
|
+
function isObject(value) {
|
|
315
|
+
return typeof value === "object" && value !== null;
|
|
316
|
+
}
|
|
317
|
+
function normalizeError(error) {
|
|
318
|
+
if (!isObject(error))
|
|
319
|
+
return;
|
|
320
|
+
const result = {};
|
|
321
|
+
if (isString(error["name"])) {
|
|
322
|
+
result.name = error["name"];
|
|
323
|
+
}
|
|
324
|
+
if (isObject(error["data"]) && isString(error["data"]["message"])) {
|
|
325
|
+
result.data = { message: error["data"]["message"] };
|
|
326
|
+
}
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
function createEventHandler(deps) {
|
|
330
|
+
const {
|
|
331
|
+
getState,
|
|
332
|
+
deleteState,
|
|
333
|
+
sessionCache,
|
|
334
|
+
getTracking,
|
|
335
|
+
onSessionIdle,
|
|
336
|
+
onSessionError
|
|
337
|
+
} = deps;
|
|
338
|
+
return async function handleEvent(input) {
|
|
339
|
+
const { event } = input;
|
|
340
|
+
switch (event.type) {
|
|
341
|
+
case "message.updated": {
|
|
342
|
+
const info = event.properties["info"];
|
|
343
|
+
if (!isObject(info))
|
|
344
|
+
return;
|
|
345
|
+
const sessionID = info["sessionID"];
|
|
346
|
+
const messageID = info["id"];
|
|
347
|
+
const role = info["role"];
|
|
348
|
+
if (!isString(sessionID) || !isString(messageID) || !isString(role)) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (role === "user" || role === "assistant") {
|
|
352
|
+
sessionCache.setRole(sessionID, messageID, role);
|
|
353
|
+
}
|
|
354
|
+
if ("agent" in info && isString(info["agent"])) {
|
|
355
|
+
sessionCache.setAgent(sessionID, messageID, info["agent"]);
|
|
356
|
+
}
|
|
357
|
+
if (role === "assistant") {
|
|
358
|
+
const state = getState(sessionID);
|
|
359
|
+
const tracking = getTracking(sessionID);
|
|
360
|
+
if (state && tracking && tracking.awaitingWorkerReply) {
|
|
361
|
+
const agent = "agent" in info && isString(info["agent"]) ? info["agent"] : undefined;
|
|
362
|
+
if (agent === state.worker_agent) {
|
|
363
|
+
tracking.lastAssistantMessageID = messageID;
|
|
364
|
+
tracking.lastUsage = {
|
|
365
|
+
tokens: info["tokens"],
|
|
366
|
+
cost: info["cost"]
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
case "message.part.updated": {
|
|
374
|
+
const part = event.properties["part"];
|
|
375
|
+
if (!isObject(part))
|
|
376
|
+
return;
|
|
377
|
+
if (part["type"] !== "text" || !isString(part["sessionID"]) || !isString(part["messageID"]) || !isString(part["id"]) || !isString(part["text"])) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const state = getState(part["sessionID"]);
|
|
381
|
+
if (!state)
|
|
382
|
+
return;
|
|
383
|
+
const cachedRole = sessionCache.getRole(part["sessionID"], part["messageID"]);
|
|
384
|
+
const cachedAgent = sessionCache.getAgent(part["sessionID"], part["messageID"]);
|
|
385
|
+
if (cachedRole === "assistant" && cachedAgent === state.worker_agent) {
|
|
386
|
+
sessionCache.setTextPart(part["sessionID"], part["id"], part["messageID"], part["text"]);
|
|
387
|
+
}
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
case "session.idle": {
|
|
391
|
+
const sessionID = event.properties["sessionID"];
|
|
392
|
+
if (!isString(sessionID))
|
|
393
|
+
return;
|
|
394
|
+
await onSessionIdle(sessionID);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
case "session.error": {
|
|
398
|
+
const sessionID = event.properties["sessionID"];
|
|
399
|
+
if (!isString(sessionID))
|
|
400
|
+
return;
|
|
401
|
+
const state = getState(sessionID);
|
|
402
|
+
if (!state || state.mode !== "ENABLED")
|
|
403
|
+
return;
|
|
404
|
+
const error = normalizeError(event.properties["error"]);
|
|
405
|
+
await onSessionError(sessionID, error);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
case "session.deleted": {
|
|
409
|
+
const info = event.properties["info"];
|
|
410
|
+
if (!isObject(info) || !isString(info["id"]))
|
|
411
|
+
return;
|
|
412
|
+
sessionCache.cleanup(info["id"]);
|
|
413
|
+
deleteState(info["id"]);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
case "permission.updated": {
|
|
417
|
+
const sessionID = event.properties["sessionID"];
|
|
418
|
+
if (!isString(sessionID))
|
|
419
|
+
return;
|
|
420
|
+
const state = getState(sessionID);
|
|
421
|
+
const tracking = getTracking(sessionID);
|
|
422
|
+
if (!state || state.mode !== "ENABLED" || !tracking)
|
|
423
|
+
return;
|
|
424
|
+
tracking.blockedByPermission = true;
|
|
425
|
+
const permType = isString(event.properties["type"]) ? event.properties["type"] : "unknown";
|
|
426
|
+
const patterns = event.properties["pattern"];
|
|
427
|
+
const patternStr = Array.isArray(patterns) ? patterns.filter(isString).join(", ") : isString(patterns) ? patterns : "";
|
|
428
|
+
tracking.permissionBlockMessage = `Denied ${permType} ${patternStr}`.trim();
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// hooks/permission.ts
|
|
436
|
+
function createPermissionHook(deps) {
|
|
437
|
+
return async (input, output) => {
|
|
438
|
+
const state = deps.getState(input.sessionID);
|
|
439
|
+
if (!state || state.mode !== "ENABLED") {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const permissionMode = deps.getPermissionMode(input.sessionID);
|
|
443
|
+
if (permissionMode === "allow-all") {
|
|
444
|
+
output.status = "allow";
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (permissionMode === "limited") {
|
|
448
|
+
output.status = "deny";
|
|
449
|
+
deps.onPermissionDenied?.(input.sessionID, {
|
|
450
|
+
type: input.type,
|
|
451
|
+
pattern: input.pattern,
|
|
452
|
+
title: input.title
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// hooks/system-transform.ts
|
|
459
|
+
function createSystemTransformHook(deps) {
|
|
460
|
+
const {
|
|
461
|
+
getState,
|
|
462
|
+
getSuppressCount,
|
|
463
|
+
decrementSuppressCount,
|
|
464
|
+
buildSystemPrompt
|
|
465
|
+
} = deps;
|
|
466
|
+
return async (input, output) => {
|
|
467
|
+
const { sessionID } = input;
|
|
468
|
+
if (sessionID === undefined) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const state = getState(sessionID);
|
|
472
|
+
if (!state || state.mode !== "ENABLED") {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const suppressCount = getSuppressCount(sessionID);
|
|
476
|
+
if (suppressCount > 0) {
|
|
477
|
+
decrementSuppressCount(sessionID);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (!Array.isArray(output.system)) {
|
|
481
|
+
output.system = [];
|
|
482
|
+
}
|
|
483
|
+
output.system.push(buildSystemPrompt());
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// hooks/chat-message.ts
|
|
488
|
+
var CONTROL_AGENT = "autopilot";
|
|
489
|
+
function createChatMessageHook(deps) {
|
|
490
|
+
const { getState, incrementSuppressCount } = deps;
|
|
491
|
+
return async (input, _output) => {
|
|
492
|
+
const state = getState(input.sessionID);
|
|
493
|
+
if (!state || state.mode !== "ENABLED") {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (input.agent === CONTROL_AGENT) {
|
|
497
|
+
incrementSuppressCount(input.sessionID);
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// hooks/tool-after.ts
|
|
503
|
+
var AUTOPILOT_STATUS_TOOL = "autopilot_status";
|
|
504
|
+
function createToolAfterHook(deps) {
|
|
505
|
+
return async (input, output) => {
|
|
506
|
+
if (input.tool !== AUTOPILOT_STATUS_TOOL) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
output.output = deps.stripMarker(output.output);
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// tools/start.ts
|
|
514
|
+
import { tool } from "@opencode-ai/plugin";
|
|
515
|
+
var AUTOPILOT_FALLBACK_AGENT = "pi";
|
|
516
|
+
function createStartTool(deps) {
|
|
517
|
+
return tool({
|
|
518
|
+
description: "Arm autopilot mode for the current session",
|
|
519
|
+
args: {
|
|
520
|
+
task: tool.schema.string().min(1).describe("Task for the autonomous worker to execute"),
|
|
521
|
+
permissionMode: tool.schema.enum(["limited", "allow-all"]).optional().describe("How permissions should behave while autopilot is active"),
|
|
522
|
+
maxContinues: tool.schema.number().int().positive().optional().describe("Maximum number of autonomous continuation prompts after the initial task prompt"),
|
|
523
|
+
workerAgent: tool.schema.string().optional().describe("Agent used for autonomous follow-up turns (defaults to pi)")
|
|
524
|
+
},
|
|
525
|
+
async execute(args, context) {
|
|
526
|
+
if (args.task.trim().toLowerCase() === "help") {
|
|
527
|
+
return `
|
|
528
|
+
## Autopilot Usage
|
|
529
|
+
|
|
530
|
+
Use the global \`Autopilot\` agent to control the autopilot plugin.
|
|
531
|
+
|
|
532
|
+
**Start Autopilot:**
|
|
533
|
+
Switch to the \`Autopilot\` agent, then send the task you want delegated.
|
|
534
|
+
|
|
535
|
+
Examples:
|
|
536
|
+
- \`Fix the failing tests\`
|
|
537
|
+
- \`Use allow-all mode and build-high to refactor the reducer\`
|
|
538
|
+
|
|
539
|
+
**Check Status:**
|
|
540
|
+
- \`status\`
|
|
541
|
+
- \`is autopilot running?\`
|
|
542
|
+
|
|
543
|
+
**Stop Autopilot:**
|
|
544
|
+
- \`stop\`
|
|
545
|
+
- \`stop because I want to inspect manually\`
|
|
546
|
+
|
|
547
|
+
Defaults:
|
|
548
|
+
- permission mode: \`limited\`
|
|
549
|
+
- continuation limit: \`10\`
|
|
550
|
+
- worker agent: \`pi\`
|
|
551
|
+
`.trim();
|
|
552
|
+
}
|
|
553
|
+
const permissionMode = args.permissionMode ?? "limited";
|
|
554
|
+
const maxContinues = deps.normalizeMaxContinues(args.maxContinues);
|
|
555
|
+
const workerAgent = args.workerAgent?.trim() || deps.defaultWorkerAgent || AUTOPILOT_FALLBACK_AGENT;
|
|
556
|
+
const state = deps.createSessionState(context.sessionID, args.task.trim(), {
|
|
557
|
+
maxContinues,
|
|
558
|
+
workerAgent
|
|
559
|
+
});
|
|
560
|
+
Object.defineProperty(state, "permissionMode", {
|
|
561
|
+
value: permissionMode,
|
|
562
|
+
writable: true,
|
|
563
|
+
enumerable: true,
|
|
564
|
+
configurable: true
|
|
565
|
+
});
|
|
566
|
+
deps.setState(context.sessionID, state);
|
|
567
|
+
deps.initSession(context.sessionID);
|
|
568
|
+
context.metadata({
|
|
569
|
+
title: "Autopilot armed",
|
|
570
|
+
metadata: {
|
|
571
|
+
permissionMode,
|
|
572
|
+
maxContinues,
|
|
573
|
+
workerAgent
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
await deps.onArmed(context.sessionID, state);
|
|
577
|
+
return `Autopilot armed in ${permissionMode} mode with ${workerAgent}. It will start after this response and may continue up to ${maxContinues} times.`;
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// tools/status.ts
|
|
583
|
+
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
584
|
+
function createStatusTool(deps) {
|
|
585
|
+
return tool2({
|
|
586
|
+
description: "Show autopilot status for the current session",
|
|
587
|
+
args: {},
|
|
588
|
+
async execute(_args, context) {
|
|
589
|
+
const state = deps.getState(context.sessionID);
|
|
590
|
+
if (!state) {
|
|
591
|
+
return deps.summarizeState(state);
|
|
592
|
+
}
|
|
593
|
+
const history = deps.getHistory(context.sessionID);
|
|
594
|
+
const historyStr = history.length > 0 ? `
|
|
595
|
+
Recent events:
|
|
596
|
+
- ${history.join(`
|
|
597
|
+
- `)}` : "";
|
|
598
|
+
return `${deps.summarizeState(state)}${historyStr}`;
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// tools/stop.ts
|
|
604
|
+
import { tool as tool3 } from "@opencode-ai/plugin";
|
|
605
|
+
function createStopTool(deps) {
|
|
606
|
+
return tool3({
|
|
607
|
+
description: "Stop autopilot mode for the current session",
|
|
608
|
+
args: {
|
|
609
|
+
reason: tool3.schema.string().optional().describe("Optional reason to include in the stop message")
|
|
610
|
+
},
|
|
611
|
+
async execute(args, context) {
|
|
612
|
+
const state = deps.getState(context.sessionID);
|
|
613
|
+
if (!state || state.mode !== "ENABLED") {
|
|
614
|
+
return "Autopilot is not running in this session.";
|
|
615
|
+
}
|
|
616
|
+
deps.onStop(context.sessionID, args.reason);
|
|
617
|
+
return args.reason ? `Autopilot stopped: ${args.reason}` : "Autopilot stopped for this session.";
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// tools/help.ts
|
|
623
|
+
import { tool as tool4 } from "@opencode-ai/plugin";
|
|
624
|
+
function createHelpTool() {
|
|
625
|
+
return tool4({
|
|
626
|
+
description: "Show autopilot usage instructions",
|
|
627
|
+
args: {},
|
|
628
|
+
execute: async () => {
|
|
629
|
+
return `
|
|
630
|
+
## Autopilot Usage
|
|
631
|
+
|
|
632
|
+
Use the global \`Autopilot\` agent to control the autopilot plugin.
|
|
633
|
+
|
|
634
|
+
**Start Autopilot:**
|
|
635
|
+
Switch to the \`Autopilot\` agent, then send the task you want delegated.
|
|
636
|
+
|
|
637
|
+
Examples:
|
|
638
|
+
- \`Fix the failing tests\`
|
|
639
|
+
- \`Use allow-all mode and build-high to refactor the reducer\`
|
|
640
|
+
|
|
641
|
+
**Check Status:**
|
|
642
|
+
- \`status\`
|
|
643
|
+
- \`is autopilot running?\`
|
|
644
|
+
|
|
645
|
+
**Stop Autopilot:**
|
|
646
|
+
- \`stop\`
|
|
647
|
+
- \`stop because I want to inspect manually\`
|
|
648
|
+
|
|
649
|
+
Defaults:
|
|
650
|
+
- permission mode: \`limited\`
|
|
651
|
+
- continuation limit: \`10\`
|
|
652
|
+
- worker agent: \`pi\`
|
|
653
|
+
`.trim();
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// plugin.ts
|
|
659
|
+
var AUTOPILOT_FALLBACK_AGENT2 = "pi";
|
|
660
|
+
var MAX_HISTORY_ENTRIES = 10;
|
|
661
|
+
var AutopilotPlugin = async ({
|
|
662
|
+
client,
|
|
663
|
+
directory,
|
|
664
|
+
worktree
|
|
665
|
+
}) => {
|
|
666
|
+
const stateBySession = new Map;
|
|
667
|
+
const trackingBySession = new Map;
|
|
668
|
+
const suppressCountBySession = new Map;
|
|
669
|
+
const historyBySession = new Map;
|
|
670
|
+
const permissionModeBySession = new Map;
|
|
671
|
+
const sessionCache = new SessionCache;
|
|
672
|
+
const getState = (sessionID) => stateBySession.get(sessionID);
|
|
673
|
+
const setState = (sessionID, state) => {
|
|
674
|
+
stateBySession.set(sessionID, state);
|
|
675
|
+
};
|
|
676
|
+
const deleteState = (sessionID) => {
|
|
677
|
+
stateBySession.delete(sessionID);
|
|
678
|
+
trackingBySession.delete(sessionID);
|
|
679
|
+
suppressCountBySession.delete(sessionID);
|
|
680
|
+
historyBySession.delete(sessionID);
|
|
681
|
+
permissionModeBySession.delete(sessionID);
|
|
682
|
+
};
|
|
683
|
+
const getTracking = (sessionID) => trackingBySession.get(sessionID);
|
|
684
|
+
const initSession = (sessionID) => {
|
|
685
|
+
trackingBySession.set(sessionID, createSessionTracking());
|
|
686
|
+
suppressCountBySession.set(sessionID, 0);
|
|
687
|
+
historyBySession.set(sessionID, []);
|
|
688
|
+
};
|
|
689
|
+
const recordHistory = (sessionID, message) => {
|
|
690
|
+
let history = historyBySession.get(sessionID);
|
|
691
|
+
if (!history) {
|
|
692
|
+
history = [];
|
|
693
|
+
historyBySession.set(sessionID, history);
|
|
694
|
+
}
|
|
695
|
+
history.push(message);
|
|
696
|
+
if (history.length > MAX_HISTORY_ENTRIES) {
|
|
697
|
+
historyBySession.set(sessionID, history.slice(-MAX_HISTORY_ENTRIES));
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
const safeToast = async (opts) => {
|
|
701
|
+
try {
|
|
702
|
+
await client.tui.showToast({
|
|
703
|
+
directory,
|
|
704
|
+
workspace: worktree,
|
|
705
|
+
title: opts.title,
|
|
706
|
+
message: opts.message,
|
|
707
|
+
variant: opts.variant,
|
|
708
|
+
duration: 3000
|
|
709
|
+
});
|
|
710
|
+
} catch {}
|
|
711
|
+
};
|
|
712
|
+
const setStopped = async (sessionID, reason, detail, variant = "info") => {
|
|
713
|
+
const state = getState(sessionID);
|
|
714
|
+
if (!state)
|
|
715
|
+
return;
|
|
716
|
+
state.mode = "DISABLED";
|
|
717
|
+
state.phase = "STOPPED";
|
|
718
|
+
state.stop_reason = "USER_STOP";
|
|
719
|
+
recordHistory(sessionID, detail ? `${reason}: ${detail}` : reason);
|
|
720
|
+
await safeToast({
|
|
721
|
+
title: "Autopilot stopped",
|
|
722
|
+
message: detail ? `${reason}: ${detail}` : reason,
|
|
723
|
+
variant
|
|
724
|
+
});
|
|
725
|
+
};
|
|
726
|
+
const dispatchPrompt = async (sessionID, state, promptText) => {
|
|
727
|
+
const tracking = getTracking(sessionID);
|
|
728
|
+
if (!tracking)
|
|
729
|
+
return;
|
|
730
|
+
tracking.awaitingWorkerReply = true;
|
|
731
|
+
tracking.lastAssistantMessageID = undefined;
|
|
732
|
+
try {
|
|
733
|
+
await client.session.promptAsync({
|
|
734
|
+
directory,
|
|
735
|
+
workspace: worktree,
|
|
736
|
+
sessionID,
|
|
737
|
+
agent: state.worker_agent,
|
|
738
|
+
parts: [{ type: "text", text: promptText }]
|
|
739
|
+
});
|
|
740
|
+
} catch {}
|
|
741
|
+
};
|
|
742
|
+
const maybeContinue = async (sessionID) => {
|
|
743
|
+
const state = getState(sessionID);
|
|
744
|
+
const tracking = getTracking(sessionID);
|
|
745
|
+
if (!state || state.mode !== "ENABLED" || !tracking)
|
|
746
|
+
return;
|
|
747
|
+
if (state.phase === "OBSERVE" && state.continuation_count === 0 && !tracking.lastAssistantMessageID) {
|
|
748
|
+
recordHistory(sessionID, `Starting task with ${state.worker_agent}`);
|
|
749
|
+
await safeToast({
|
|
750
|
+
title: "Autopilot armed",
|
|
751
|
+
message: `Starting task with ${state.worker_agent}`,
|
|
752
|
+
variant: "info"
|
|
753
|
+
});
|
|
754
|
+
await dispatchPrompt(sessionID, state, state.goal);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
if (tracking.blockedByPermission) {
|
|
758
|
+
tracking.blockedByPermission = false;
|
|
759
|
+
await setStopped(sessionID, "Blocked by permissions", tracking.permissionBlockMessage ?? "A required action was denied in limited mode.", "warning");
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const messageID = tracking.lastAssistantMessageID;
|
|
763
|
+
if (!messageID)
|
|
764
|
+
return;
|
|
765
|
+
tracking.awaitingWorkerReply = false;
|
|
766
|
+
tracking.lastAssistantMessageID = undefined;
|
|
767
|
+
const assistantText = sessionCache.getMessageText(sessionID, messageID);
|
|
768
|
+
const directive = inferAutopilotDirective(assistantText);
|
|
769
|
+
if (directive.status === "complete") {
|
|
770
|
+
await setStopped(sessionID, "Task completed", directive.reason, "success");
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
if (directive.status === "blocked") {
|
|
774
|
+
await setStopped(sessionID, "Task blocked", directive.reason, "warning");
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
if (state.continuation_count >= state.max_continues) {
|
|
778
|
+
await setStopped(sessionID, "Continuation limit reached", `Stopped after ${state.continuation_count} autonomous continuations.`, "warning");
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
state.continuation_count += 1;
|
|
782
|
+
const usageBits = formatUsageMetadata(tracking.lastUsage);
|
|
783
|
+
const suffix = usageBits ? ` (${usageBits})` : "";
|
|
784
|
+
recordHistory(sessionID, `Continuation ${state.continuation_count}/${state.max_continues}${suffix}`);
|
|
785
|
+
await safeToast({
|
|
786
|
+
title: "Autopilot continuing",
|
|
787
|
+
message: `Continuation ${state.continuation_count}/${state.max_continues}${suffix}`,
|
|
788
|
+
variant: "info"
|
|
789
|
+
});
|
|
790
|
+
await dispatchPrompt(sessionID, state, buildContinuationPrompt({
|
|
791
|
+
continueCount: state.continuation_count,
|
|
792
|
+
maxContinues: state.max_continues,
|
|
793
|
+
task: state.goal
|
|
794
|
+
}));
|
|
795
|
+
};
|
|
796
|
+
const eventHandler = createEventHandler({
|
|
797
|
+
getState,
|
|
798
|
+
deleteState,
|
|
799
|
+
sessionCache,
|
|
800
|
+
getTracking,
|
|
801
|
+
onSessionIdle: maybeContinue,
|
|
802
|
+
onSessionError: async (sessionID, error) => {
|
|
803
|
+
const errorMessage = error?.data?.message ?? "Autopilot encountered an unknown error.";
|
|
804
|
+
const isAbort = error?.name === "MessageAbortedError";
|
|
805
|
+
const reason = isAbort ? "Interrupted" : "Error";
|
|
806
|
+
const variant = isAbort ? "warning" : "error";
|
|
807
|
+
await setStopped(sessionID, reason, errorMessage, variant);
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
const permissionHook = createPermissionHook({
|
|
811
|
+
getState,
|
|
812
|
+
getPermissionMode: (sessionID) => permissionModeBySession.get(sessionID),
|
|
813
|
+
onPermissionDenied: (sessionID, permission) => {
|
|
814
|
+
const tracking = getTracking(sessionID);
|
|
815
|
+
if (!tracking)
|
|
816
|
+
return;
|
|
817
|
+
tracking.blockedByPermission = true;
|
|
818
|
+
const patternStr = Array.isArray(permission.pattern) ? permission.pattern.join(", ") : permission.pattern ?? "";
|
|
819
|
+
tracking.permissionBlockMessage = `Denied ${permission.type} ${patternStr}`.trim();
|
|
820
|
+
recordHistory(sessionID, tracking.permissionBlockMessage);
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
const systemTransformHook = createSystemTransformHook({
|
|
824
|
+
getState,
|
|
825
|
+
getSuppressCount: (sessionID) => suppressCountBySession.get(sessionID) ?? 0,
|
|
826
|
+
decrementSuppressCount: (sessionID) => {
|
|
827
|
+
const current = suppressCountBySession.get(sessionID) ?? 0;
|
|
828
|
+
if (current > 0) {
|
|
829
|
+
suppressCountBySession.set(sessionID, current - 1);
|
|
830
|
+
}
|
|
831
|
+
},
|
|
832
|
+
buildSystemPrompt: buildAutopilotSystemPrompt
|
|
833
|
+
});
|
|
834
|
+
const chatMessageHook = createChatMessageHook({
|
|
835
|
+
getState,
|
|
836
|
+
incrementSuppressCount: (sessionID) => {
|
|
837
|
+
const current = suppressCountBySession.get(sessionID) ?? 0;
|
|
838
|
+
suppressCountBySession.set(sessionID, current + 1);
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
const toolAfterHook = createToolAfterHook({
|
|
842
|
+
stripMarker: stripAutopilotMarker
|
|
843
|
+
});
|
|
844
|
+
const startTool = createStartTool({
|
|
845
|
+
getState,
|
|
846
|
+
setState,
|
|
847
|
+
createSessionState,
|
|
848
|
+
normalizeMaxContinues,
|
|
849
|
+
initSession,
|
|
850
|
+
defaultWorkerAgent: AUTOPILOT_FALLBACK_AGENT2,
|
|
851
|
+
onArmed: async (sessionID, state) => {
|
|
852
|
+
const pm = state["permissionMode"];
|
|
853
|
+
if (pm === "allow-all" || pm === "limited") {
|
|
854
|
+
permissionModeBySession.set(sessionID, pm);
|
|
855
|
+
} else {
|
|
856
|
+
permissionModeBySession.set(sessionID, "limited");
|
|
857
|
+
}
|
|
858
|
+
recordHistory(sessionID, `Armed in ${permissionModeBySession.get(sessionID)} mode with ${state.worker_agent}.`);
|
|
859
|
+
recordHistory(sessionID, `Continuation limit: ${state.max_continues}.`);
|
|
860
|
+
}
|
|
861
|
+
});
|
|
862
|
+
const statusTool = createStatusTool({
|
|
863
|
+
getState,
|
|
864
|
+
summarizeState: summarizeAutopilotState,
|
|
865
|
+
getHistory: (sessionID) => historyBySession.get(sessionID) ?? []
|
|
866
|
+
});
|
|
867
|
+
const stopTool = createStopTool({
|
|
868
|
+
getState,
|
|
869
|
+
onStop: (sessionID, reason) => {
|
|
870
|
+
const state = getState(sessionID);
|
|
871
|
+
if (!state)
|
|
872
|
+
return;
|
|
873
|
+
state.mode = "DISABLED";
|
|
874
|
+
state.phase = "STOPPED";
|
|
875
|
+
state.stop_reason = "USER_STOP";
|
|
876
|
+
recordHistory(sessionID, reason ? `Cancelled by user: ${reason}` : "Cancelled by user");
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
const helpTool = createHelpTool();
|
|
880
|
+
return {
|
|
881
|
+
tool: {
|
|
882
|
+
autopilot_start: startTool,
|
|
883
|
+
autopilot_status: statusTool,
|
|
884
|
+
autopilot_stop: stopTool,
|
|
885
|
+
autopilot_help: helpTool
|
|
886
|
+
},
|
|
887
|
+
event: eventHandler,
|
|
888
|
+
"permission.ask": permissionHook,
|
|
889
|
+
"experimental.chat.system.transform": systemTransformHook,
|
|
890
|
+
"chat.message": chatMessageHook,
|
|
891
|
+
"tool.execute.after": toolAfterHook
|
|
892
|
+
};
|
|
893
|
+
};
|
|
894
|
+
export {
|
|
895
|
+
AutopilotPlugin
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
//# debugId=7838B318ED86DB9164756E2164756E21
|
|
899
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../state/session-cache.ts", "../state/factory.ts", "../prompts/continuation.ts", "../prompts/directives.ts", "../prompts/format.ts", "../prompts/normalize.ts", "../prompts/system-prompt.ts", "../hooks/event-handler.ts", "../hooks/permission.ts", "../hooks/system-transform.ts", "../hooks/chat-message.ts", "../hooks/tool-after.ts", "../tools/start.ts", "../tools/status.ts", "../tools/stop.ts", "../tools/help.ts", "../plugin.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"type MessageRole = \"user\" | \"assistant\" | \"system\" | \"tool\";\n\nexport interface CachedTextPart {\n id: string;\n messageID: string;\n text: string;\n}\n\nexport interface SessionSnapshot {\n roles: Record<string, MessageRole>;\n agents: Record<string, string>;\n textParts: CachedTextPart[];\n}\n\ninterface SessionStore {\n roles: Map<string, MessageRole>;\n agents: Map<string, string>;\n textParts: Map<string, CachedTextPart>;\n}\n\nfunction createSessionStore(): SessionStore {\n return {\n roles: new Map<string, MessageRole>(),\n agents: new Map<string, string>(),\n textParts: new Map<string, CachedTextPart>(),\n };\n}\n\nexport class SessionCache {\n private readonly sessions = new Map<string, SessionStore>();\n\n private ensureSession(sessionID: string): SessionStore {\n const existing = this.sessions.get(sessionID);\n\n if (existing) {\n return existing;\n }\n\n const created = createSessionStore();\n this.sessions.set(sessionID, created);\n return created;\n }\n\n setRole(sessionID: string, messageID: string, role: MessageRole): void {\n this.ensureSession(sessionID).roles.set(messageID, role);\n }\n\n getRole(sessionID: string, messageID: string): MessageRole | null {\n return this.sessions.get(sessionID)?.roles.get(messageID) ?? null;\n }\n\n setAgent(sessionID: string, messageID: string, agent: string): void {\n this.ensureSession(sessionID).agents.set(messageID, agent);\n }\n\n getAgent(sessionID: string, messageID: string): string | null {\n return this.sessions.get(sessionID)?.agents.get(messageID) ?? null;\n }\n\n setTextPart(\n sessionID: string,\n partID: string,\n messageID: string,\n text: string,\n ): void {\n this.ensureSession(sessionID).textParts.set(partID, {\n id: partID,\n messageID,\n text,\n });\n }\n\n getTextPart(sessionID: string, partID: string): CachedTextPart | null {\n return this.sessions.get(sessionID)?.textParts.get(partID) ?? null;\n }\n\n getMessageText(sessionID: string, messageID: string): string {\n const session = this.sessions.get(sessionID);\n\n if (!session) {\n return \"\";\n }\n\n return [...session.textParts.values()]\n .filter((part) => part.messageID === messageID)\n .map((part) => part.text)\n .join(\"\");\n }\n\n snapshot(sessionID: string): SessionSnapshot {\n const session = this.sessions.get(sessionID) ?? createSessionStore();\n\n return {\n roles: Object.fromEntries(session.roles),\n agents: Object.fromEntries(session.agents),\n textParts: [...session.textParts.values()],\n };\n }\n\n cleanup(sessionID: string): void {\n this.sessions.delete(sessionID);\n }\n}\n\nexport type { MessageRole };\n",
|
|
6
|
+
"import type {\n AgentMode,\n AgentPhase,\n ApprovalState,\n ContextState,\n ExtendedState,\n PlanState,\n RetryCounters,\n TrustState,\n} from \"../types/index.ts\";\n\nexport interface CreateInitialStateOptions {\n sessionID?: string;\n mode?: AgentMode;\n phase?: AgentPhase;\n allowedTools?: string[];\n allowedPaths?: string[];\n maxContinues?: number;\n workerAgent?: string;\n remainingBudget?: number | null;\n contextThreshold?: number;\n maxStepRetries?: number;\n maxGlobalRetries?: number;\n maxNoProgress?: number;\n trustedPaths?: string[];\n}\n\nconst DEFAULT_ALLOWED_TOOLS = [\"bash\", \"read\", \"glob\", \"grep\", \"apply_patch\"];\nconst DEFAULT_CONTEXT_THRESHOLD = 8_000;\nconst DEFAULT_MAX_CONTINUES = 25;\nconst DEFAULT_MAX_STEP_RETRIES = 2;\nconst DEFAULT_MAX_GLOBAL_RETRIES = 6;\nconst DEFAULT_MAX_NO_PROGRESS = 3;\nconst DEFAULT_WORKER_AGENT = \"pi\";\n\nfunction createPlanState(): PlanState {\n return {\n steps: [],\n open_items: [],\n completed_items: [],\n blocked_items: [],\n dependencies: {},\n stale: false,\n };\n}\n\nfunction createApprovalState(): ApprovalState {\n return {\n status: \"idle\",\n pending_action: null,\n pending_scope: null,\n approved_scopes: [],\n denied_scopes: [],\n last_feedback: null,\n };\n}\n\nfunction createTrustState(trustedPaths: string[]): TrustState {\n return {\n status: trustedPaths.length > 0 ? \"trusted\" : \"untrusted\",\n trusted_paths: [...trustedPaths],\n pending_path: null,\n denied_paths: [],\n last_feedback: null,\n };\n}\n\nfunction createContextState(\n remainingBudget: number | null,\n threshold: number,\n): ContextState {\n const resolvedBudget = remainingBudget ?? null;\n const compactionNeeded =\n resolvedBudget !== null ? resolvedBudget <= threshold : false;\n\n return {\n remaining_budget: resolvedBudget,\n threshold,\n compaction_needed: compactionNeeded,\n compacted_at: null,\n unsafe_to_continue: compactionNeeded,\n };\n}\n\nfunction createRetryCounters(options: CreateInitialStateOptions): RetryCounters {\n return {\n step_retry_count: 0,\n global_retry_count: 0,\n no_progress_count: 0,\n recovery_attempt_count: 0,\n max_step_retries: options.maxStepRetries ?? DEFAULT_MAX_STEP_RETRIES,\n max_global_retries: options.maxGlobalRetries ?? DEFAULT_MAX_GLOBAL_RETRIES,\n max_no_progress: options.maxNoProgress ?? DEFAULT_MAX_NO_PROGRESS,\n };\n}\n\nexport function createInitialState(\n goal: string,\n options: CreateInitialStateOptions = {},\n): ExtendedState {\n const sessionID = options.sessionID ?? \"\";\n const allowedPaths = options.allowedPaths ?? [];\n\n return {\n session_id: sessionID,\n mode: options.mode ?? \"DISABLED\",\n phase: options.phase ?? \"STOPPED\",\n goal,\n plan_state: createPlanState(),\n completion_evidence: [],\n allowed_tools: [...(options.allowedTools ?? DEFAULT_ALLOWED_TOOLS)],\n allowed_paths: [...allowedPaths],\n approval_state: createApprovalState(),\n trust_state: createTrustState(options.trustedPaths ?? allowedPaths),\n context_state: createContextState(\n options.remainingBudget ?? null,\n options.contextThreshold ?? DEFAULT_CONTEXT_THRESHOLD,\n ),\n foreground_action: null,\n background_tasks: [],\n retry_counters: createRetryCounters(options),\n stop_reason: null,\n latest_observations: {\n events: [],\n last_user_input: null,\n last_tool_result: null,\n last_tool_error: null,\n last_interrupt: null,\n },\n continuation_count: 0,\n max_continues: options.maxContinues ?? DEFAULT_MAX_CONTINUES,\n worker_agent: options.workerAgent ?? DEFAULT_WORKER_AGENT,\n last_updated_at: null,\n resumable: true,\n };\n}\n\nexport function createSessionState(\n sessionID: string,\n goal: string,\n options: Omit<CreateInitialStateOptions, \"sessionID\"> = {},\n): ExtendedState {\n return createInitialState(goal, {\n ...options,\n sessionID,\n mode: options.mode ?? \"ENABLED\",\n phase: options.phase ?? \"OBSERVE\",\n });\n}\n",
|
|
7
|
+
"export interface ContinuationPromptOptions {\n continueCount: number;\n maxContinues: number;\n task: string;\n}\n\nexport function buildContinuationPrompt(\n options: ContinuationPromptOptions,\n): string {\n return [\n `Autopilot continuation ${options.continueCount}/${options.maxContinues}.`,\n \"Continue working autonomously on the same task.\",\n `Original task: ${options.task}`,\n \"Review your latest progress, choose the next concrete step, and keep moving without waiting for the user.\",\n \"If you are done, provide the result and mark the response complete.\",\n \"If you are blocked by missing information or denied permissions, explain the blocker and mark the response blocked.\",\n ].join(\"\\n\");\n}\n",
|
|
8
|
+
"export type AutopilotDirectiveStatus = \"continue\" | \"complete\" | \"blocked\";\n\nexport interface AutopilotDirective {\n status: AutopilotDirectiveStatus;\n reason: string;\n}\n\nconst AUTOPILOT_MARKER_RE =\n /\\n?<autopilot\\s+status=\"(continue|complete|blocked)\">([\\s\\S]*?)<\\/autopilot>\\s*$/i;\nconst BLOCKED_HINT_RE =\n /(need (more|additional) information|cannot continue|can't continue|blocked|waiting for user|please provide|which option|what should i|what would you like)/i;\n\nexport function parseAutopilotMarker(text: string): AutopilotDirective | null {\n const match = text.match(AUTOPILOT_MARKER_RE);\n\n if (!match) {\n return null;\n }\n\n const [, rawStatus, rawReason] = match;\n\n if (!rawStatus || rawReason === undefined) {\n return null;\n }\n\n const status = rawStatus.toLowerCase() as AutopilotDirectiveStatus;\n return {\n status,\n reason: rawReason.trim() || defaultReason(status),\n };\n}\n\nexport function stripAutopilotMarker(text: string): string {\n return text.replace(AUTOPILOT_MARKER_RE, \"\").trimEnd();\n}\n\nexport function inferAutopilotDirective(text: string): AutopilotDirective {\n const marker = parseAutopilotMarker(text);\n\n if (marker) {\n return marker;\n }\n\n const source = text.trim();\n if (!source) {\n return {\n status: \"blocked\",\n reason: \"Assistant returned no usable response.\",\n };\n }\n\n if (BLOCKED_HINT_RE.test(source)) {\n return {\n status: \"blocked\",\n reason: \"Assistant requested input or reported it could not continue.\",\n };\n }\n\n return {\n status: \"continue\",\n reason: \"No autopilot marker emitted; continuing with fallback policy.\",\n };\n}\n\nfunction defaultReason(status: AutopilotDirectiveStatus): string {\n if (status === \"complete\") {\n return \"Task complete.\";\n }\n\n if (status === \"blocked\") {\n return \"Task is blocked.\";\n }\n\n return \"More work remains.\";\n}\n",
|
|
9
|
+
"import type { ExtendedState } from \"../types/index.ts\";\n\nexport interface UsageMetadata {\n tokens?: {\n total?: number;\n input?: number;\n output?: number;\n };\n cost?: {\n total?: number;\n };\n}\n\nexport function formatUsageMetadata(usage: UsageMetadata | null | undefined): string {\n if (!usage) {\n return \"\";\n }\n\n const parts: string[] = [];\n\n if (usage.tokens) {\n const { total, input, output } = usage.tokens;\n\n if (Number.isFinite(total)) {\n parts.push(`${total} tokens`);\n } else if (Number.isFinite(input) || Number.isFinite(output)) {\n const tokenParts: string[] = [];\n\n if (Number.isFinite(input)) {\n tokenParts.push(`in ${input}`);\n }\n\n if (Number.isFinite(output)) {\n tokenParts.push(`out ${output}`);\n }\n\n if (tokenParts.length > 0) {\n parts.push(tokenParts.join(\", \"));\n }\n }\n }\n\n const totalCost = usage.cost?.total;\n if (Number.isFinite(totalCost)) {\n parts.push(`cost ${totalCost}`);\n }\n\n return parts.join(\"; \");\n}\n\nexport function summarizeAutopilotState(\n state: ExtendedState | null | undefined,\n): string {\n if (!state) {\n return \"Autopilot is idle.\";\n }\n\n const status = [\n `phase=${state.phase}`,\n `mode=${state.mode}`,\n `continues=${state.continuation_count}/${state.max_continues}`,\n `agent=${state.worker_agent}`,\n ];\n\n if (state.stop_reason) {\n status.push(`stop=${state.stop_reason}`);\n }\n\n const latestEvent = state.latest_observations.events.at(-1);\n if (latestEvent) {\n status.push(`last_event=${latestEvent.event_type}`);\n }\n\n return `Autopilot status: ${status.join(\", \")}`;\n}\n",
|
|
10
|
+
"export const AUTOPILOT_DEFAULT_MAX_CONTINUES = 10;\nexport const AUTOPILOT_MAX_CONTINUES_HARD_LIMIT = 50;\n\nexport function normalizeMaxContinues(value: unknown): number {\n const numeric = Number(value);\n\n if (!Number.isFinite(numeric) || numeric <= 0) {\n return AUTOPILOT_DEFAULT_MAX_CONTINUES;\n }\n\n return Math.min(\n AUTOPILOT_MAX_CONTINUES_HARD_LIMIT,\n Math.max(1, Math.floor(numeric)),\n );\n}\n",
|
|
11
|
+
"export function buildAutopilotSystemPrompt(): string {\n return [\n \"Autopilot mode is active for this session.\",\n \"Work autonomously toward the user's goal without asking follow-up questions unless you are truly blocked.\",\n \"At the very end of every assistant response, append exactly one machine-readable status marker on its own line using this format:\",\n '<autopilot status=\"continue|complete|blocked\">short reason</autopilot>',\n \"Use continue when more work remains and you can keep going without the user.\",\n \"Use complete when the task is done or you have reached a stable handoff point.\",\n \"Use blocked when missing information, denied permissions, or an external failure prevents meaningful progress.\",\n \"Do not omit the marker.\",\n ].join(\"\\n\");\n}\n",
|
|
12
|
+
"import type { SessionCache } from \"../state/session-cache.ts\";\nimport type { ExtendedState } from \"../types/index.ts\";\n\n// ---------------------------------------------------------------------------\n// Auxiliary per-session tracking (fields not part of ExtendedState)\n// ---------------------------------------------------------------------------\n\nexport interface SessionTracking {\n lastAssistantMessageID: string | undefined;\n lastUsage: { tokens?: unknown; cost?: unknown } | undefined;\n awaitingWorkerReply: boolean;\n blockedByPermission: boolean;\n permissionBlockMessage: string | undefined;\n}\n\nexport function createSessionTracking(): SessionTracking {\n return {\n lastAssistantMessageID: undefined,\n lastUsage: undefined,\n awaitingWorkerReply: false,\n blockedByPermission: false,\n permissionBlockMessage: undefined,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Deps\n// ---------------------------------------------------------------------------\n\nexport interface EventHandlerDeps {\n getState: (sessionID: string) => ExtendedState | undefined;\n deleteState: (sessionID: string) => void;\n sessionCache: SessionCache;\n getTracking: (sessionID: string) => SessionTracking | undefined;\n onSessionIdle: (sessionID: string) => Promise<void>;\n onSessionError: (\n sessionID: string,\n error: { name?: string; data?: { message?: string } } | undefined,\n ) => Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction normalizeError(\n error: unknown,\n): { name?: string; data?: { message?: string } } | undefined {\n if (!isObject(error)) return undefined;\n\n const result: { name?: string; data?: { message?: string } } = {};\n\n if (isString(error[\"name\"])) {\n result.name = error[\"name\"];\n }\n\n if (isObject(error[\"data\"]) && isString(error[\"data\"][\"message\"])) {\n result.data = { message: error[\"data\"][\"message\"] };\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\ninterface SdkEvent {\n type: string;\n properties: Record<string, unknown>;\n}\n\nexport function createEventHandler(\n deps: EventHandlerDeps,\n): (input: { event: SdkEvent }) => Promise<void> {\n const {\n getState,\n deleteState,\n sessionCache,\n getTracking,\n onSessionIdle,\n onSessionError,\n } = deps;\n\n return async function handleEvent(input: {\n event: SdkEvent;\n }): Promise<void> {\n const { event } = input;\n\n switch (event.type) {\n case \"message.updated\": {\n const info = event.properties[\"info\"];\n if (!isObject(info)) return;\n\n const sessionID = info[\"sessionID\"];\n const messageID = info[\"id\"];\n const role = info[\"role\"];\n\n if (!isString(sessionID) || !isString(messageID) || !isString(role)) {\n return;\n }\n\n if (role === \"user\" || role === \"assistant\") {\n sessionCache.setRole(sessionID, messageID, role);\n }\n\n if (\"agent\" in info && isString(info[\"agent\"])) {\n sessionCache.setAgent(sessionID, messageID, info[\"agent\"]);\n }\n\n if (role === \"assistant\") {\n const state = getState(sessionID);\n const tracking = getTracking(sessionID);\n\n if (state && tracking && tracking.awaitingWorkerReply) {\n const agent =\n \"agent\" in info && isString(info[\"agent\"])\n ? info[\"agent\"]\n : undefined;\n\n if (agent === state.worker_agent) {\n tracking.lastAssistantMessageID = messageID;\n tracking.lastUsage = {\n tokens: info[\"tokens\"],\n cost: info[\"cost\"],\n };\n }\n }\n }\n return;\n }\n\n case \"message.part.updated\": {\n const part = event.properties[\"part\"];\n if (!isObject(part)) return;\n\n if (\n part[\"type\"] !== \"text\" ||\n !isString(part[\"sessionID\"]) ||\n !isString(part[\"messageID\"]) ||\n !isString(part[\"id\"]) ||\n !isString(part[\"text\"])\n ) {\n return;\n }\n\n const state = getState(part[\"sessionID\"]);\n if (!state) return;\n\n const cachedRole = sessionCache.getRole(\n part[\"sessionID\"],\n part[\"messageID\"],\n );\n const cachedAgent = sessionCache.getAgent(\n part[\"sessionID\"],\n part[\"messageID\"],\n );\n\n if (cachedRole === \"assistant\" && cachedAgent === state.worker_agent) {\n sessionCache.setTextPart(\n part[\"sessionID\"],\n part[\"id\"],\n part[\"messageID\"],\n part[\"text\"],\n );\n }\n return;\n }\n\n case \"session.idle\": {\n const sessionID = event.properties[\"sessionID\"];\n if (!isString(sessionID)) return;\n\n await onSessionIdle(sessionID);\n return;\n }\n\n case \"session.error\": {\n const sessionID = event.properties[\"sessionID\"];\n if (!isString(sessionID)) return;\n\n const state = getState(sessionID);\n if (!state || state.mode !== \"ENABLED\") return;\n\n const error = normalizeError(event.properties[\"error\"]);\n await onSessionError(sessionID, error);\n return;\n }\n\n case \"session.deleted\": {\n const info = event.properties[\"info\"];\n if (!isObject(info) || !isString(info[\"id\"])) return;\n\n sessionCache.cleanup(info[\"id\"]);\n deleteState(info[\"id\"]);\n return;\n }\n\n case \"permission.updated\": {\n const sessionID = event.properties[\"sessionID\"];\n if (!isString(sessionID)) return;\n\n const state = getState(sessionID);\n const tracking = getTracking(sessionID);\n\n if (!state || state.mode !== \"ENABLED\" || !tracking) return;\n\n // In limited mode, a permission being asked implies it was denied\n // (since our permission.ask hook denies it). Record the block.\n tracking.blockedByPermission = true;\n const permType = isString(event.properties[\"type\"])\n ? event.properties[\"type\"]\n : \"unknown\";\n const patterns = event.properties[\"pattern\"];\n const patternStr = Array.isArray(patterns)\n ? patterns.filter(isString).join(\", \")\n : isString(patterns)\n ? patterns\n : \"\";\n tracking.permissionBlockMessage =\n `Denied ${permType} ${patternStr}`.trim();\n return;\n }\n }\n };\n}\n",
|
|
13
|
+
"// ---------------------------------------------------------------------------\n// permission.ask hook — enforces allow-all / limited modes\n// ---------------------------------------------------------------------------\n\nexport interface PermissionHookDeps {\n getState: (\n sessionID: string,\n ) =>\n | { mode: \"DISABLED\" | \"ENABLED\" }\n | undefined;\n getPermissionMode: (\n sessionID: string,\n ) => \"allow-all\" | \"limited\" | undefined;\n onPermissionDenied?: (\n sessionID: string,\n permission: {\n type: string;\n pattern?: string | string[];\n title: string;\n },\n ) => void;\n}\n\ninterface PermissionInput {\n id: string;\n type: string;\n pattern?: string | string[];\n sessionID: string;\n messageID: string;\n callID?: string;\n title: string;\n metadata: Record<string, unknown>;\n time: { created: number };\n}\n\ninterface PermissionOutput {\n status: \"ask\" | \"deny\" | \"allow\";\n}\n\nexport function createPermissionHook(\n deps: PermissionHookDeps,\n): (input: PermissionInput, output: PermissionOutput) => Promise<void> {\n return async (\n input: PermissionInput,\n output: PermissionOutput,\n ): Promise<void> => {\n const state = deps.getState(input.sessionID);\n\n if (!state || state.mode !== \"ENABLED\") {\n return;\n }\n\n const permissionMode = deps.getPermissionMode(input.sessionID);\n\n if (permissionMode === \"allow-all\") {\n output.status = \"allow\";\n return;\n }\n\n if (permissionMode === \"limited\") {\n output.status = \"deny\";\n deps.onPermissionDenied?.(input.sessionID, {\n type: input.type,\n pattern: input.pattern,\n title: input.title,\n });\n }\n };\n}\n",
|
|
14
|
+
"// ---------------------------------------------------------------------------\n// experimental.chat.system.transform hook\n// Injects autopilot system prompt for worker turns, suppresses for control turns\n// ---------------------------------------------------------------------------\n\nexport interface SystemTransformHookDeps {\n getState: (\n sessionID: string,\n ) =>\n | { mode: \"DISABLED\" | \"ENABLED\" }\n | undefined;\n getSuppressCount: (sessionID: string) => number;\n decrementSuppressCount: (sessionID: string) => void;\n buildSystemPrompt: () => string;\n}\n\ninterface SystemTransformInput {\n sessionID?: string;\n model: Record<string, unknown>;\n}\n\ninterface SystemTransformOutput {\n system: string[];\n}\n\nexport function createSystemTransformHook(\n deps: SystemTransformHookDeps,\n): (\n input: SystemTransformInput,\n output: SystemTransformOutput,\n) => Promise<void> {\n const {\n getState,\n getSuppressCount,\n decrementSuppressCount,\n buildSystemPrompt,\n } = deps;\n\n return async (\n input: SystemTransformInput,\n output: SystemTransformOutput,\n ): Promise<void> => {\n const { sessionID } = input;\n\n if (sessionID === undefined) {\n return;\n }\n\n const state = getState(sessionID);\n\n if (!state || state.mode !== \"ENABLED\") {\n return;\n }\n\n const suppressCount = getSuppressCount(sessionID);\n\n if (suppressCount > 0) {\n decrementSuppressCount(sessionID);\n return;\n }\n\n if (!Array.isArray(output.system)) {\n output.system = [];\n }\n\n output.system.push(buildSystemPrompt());\n };\n}\n",
|
|
15
|
+
"// ---------------------------------------------------------------------------\n// chat.message hook — tracks control-agent turns for suppression\n// ---------------------------------------------------------------------------\n\nexport const CONTROL_AGENT = \"autopilot\";\n\nexport interface ChatMessageHookDeps {\n getState: (\n sessionID: string,\n ) =>\n | { mode: \"DISABLED\" | \"ENABLED\" }\n | undefined;\n incrementSuppressCount: (sessionID: string) => void;\n}\n\ninterface ChatMessageInput {\n sessionID: string;\n agent?: string;\n model?: { providerID: string; modelID: string };\n messageID?: string;\n variant?: string;\n}\n\ninterface ChatMessageOutput {\n message: Record<string, unknown>;\n parts: unknown[];\n}\n\nexport function createChatMessageHook(\n deps: ChatMessageHookDeps,\n): (input: ChatMessageInput, output: ChatMessageOutput) => Promise<void> {\n const { getState, incrementSuppressCount } = deps;\n\n return async (\n input: ChatMessageInput,\n _output: ChatMessageOutput,\n ): Promise<void> => {\n const state = getState(input.sessionID);\n\n if (!state || state.mode !== \"ENABLED\") {\n return;\n }\n\n if (input.agent === CONTROL_AGENT) {\n incrementSuppressCount(input.sessionID);\n }\n };\n}\n",
|
|
16
|
+
"// ---------------------------------------------------------------------------\n// tool.execute.after hook — strips autopilot markers from status output\n// ---------------------------------------------------------------------------\n\nconst AUTOPILOT_STATUS_TOOL = \"autopilot_status\";\n\nexport interface ToolAfterHookDeps {\n stripMarker: (text: string) => string;\n}\n\ninterface ToolAfterInput {\n tool: string;\n sessionID: string;\n callID: string;\n args: unknown;\n}\n\ninterface ToolAfterOutput {\n title: string;\n output: string;\n metadata: unknown;\n}\n\nexport function createToolAfterHook(\n deps: ToolAfterHookDeps,\n): (input: ToolAfterInput, output: ToolAfterOutput) => Promise<void> {\n return async (\n input: ToolAfterInput,\n output: ToolAfterOutput,\n ): Promise<void> => {\n if (input.tool !== AUTOPILOT_STATUS_TOOL) {\n return;\n }\n\n output.output = deps.stripMarker(output.output);\n };\n}\n",
|
|
17
|
+
"import { tool } from \"@opencode-ai/plugin\";\nimport type { ExtendedState } from \"../types/index.ts\";\n\n// ---------------------------------------------------------------------------\n// Deps\n// ---------------------------------------------------------------------------\n\nexport interface StartToolDeps {\n getState: (sessionID: string) => ExtendedState | undefined;\n setState: (sessionID: string, state: ExtendedState) => void;\n createSessionState: (\n sessionID: string,\n goal: string,\n options: {\n maxContinues?: number;\n workerAgent?: string;\n },\n ) => ExtendedState;\n normalizeMaxContinues: (value: unknown) => number;\n initSession: (sessionID: string) => void;\n onArmed: (sessionID: string, state: ExtendedState) => Promise<void>;\n defaultWorkerAgent: string;\n}\n\n// ---------------------------------------------------------------------------\n// Tool factory\n// ---------------------------------------------------------------------------\n\nconst AUTOPILOT_FALLBACK_AGENT = \"pi\";\n\nexport function createStartTool(deps: StartToolDeps) {\n return tool({\n description: \"Arm autopilot mode for the current session\",\n args: {\n task: tool.schema\n .string()\n .min(1)\n .describe(\"Task for the autonomous worker to execute\"),\n permissionMode: tool.schema\n .enum([\"limited\", \"allow-all\"])\n .optional()\n .describe(\n \"How permissions should behave while autopilot is active\",\n ),\n maxContinues: tool.schema\n .number()\n .int()\n .positive()\n .optional()\n .describe(\n \"Maximum number of autonomous continuation prompts after the initial task prompt\",\n ),\n workerAgent: tool.schema\n .string()\n .optional()\n .describe(\n \"Agent used for autonomous follow-up turns (defaults to pi)\",\n ),\n },\n async execute(args, context) {\n if (args.task.trim().toLowerCase() === \"help\") {\n return `\n## Autopilot Usage\n\nUse the global \\`Autopilot\\` agent to control the autopilot plugin.\n\n**Start Autopilot:**\nSwitch to the \\`Autopilot\\` agent, then send the task you want delegated.\n\nExamples:\n- \\`Fix the failing tests\\`\n- \\`Use allow-all mode and build-high to refactor the reducer\\`\n\n**Check Status:**\n- \\`status\\`\n- \\`is autopilot running?\\`\n\n**Stop Autopilot:**\n- \\`stop\\`\n- \\`stop because I want to inspect manually\\`\n\nDefaults:\n- permission mode: \\`limited\\`\n- continuation limit: \\`10\\`\n- worker agent: \\`pi\\`\n`.trim();\n }\n\n const permissionMode = args.permissionMode ?? \"limited\";\n const maxContinues = deps.normalizeMaxContinues(args.maxContinues);\n const workerAgent =\n args.workerAgent?.trim() || deps.defaultWorkerAgent || AUTOPILOT_FALLBACK_AGENT;\n\n const state = deps.createSessionState(context.sessionID, args.task.trim(), {\n maxContinues,\n workerAgent,\n });\n\n // Store permission mode alongside the state.\n // We set it via Object.defineProperty to keep strict types clean.\n Object.defineProperty(state, \"permissionMode\", {\n value: permissionMode,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n\n deps.setState(context.sessionID, state);\n deps.initSession(context.sessionID);\n\n context.metadata({\n title: \"Autopilot armed\",\n metadata: {\n permissionMode,\n maxContinues,\n workerAgent,\n },\n });\n\n await deps.onArmed(context.sessionID, state);\n\n return `Autopilot armed in ${permissionMode} mode with ${workerAgent}. It will start after this response and may continue up to ${maxContinues} times.`;\n },\n });\n}\n",
|
|
18
|
+
"import { tool } from \"@opencode-ai/plugin\";\nimport type { ExtendedState } from \"../types/index.ts\";\n\n// ---------------------------------------------------------------------------\n// Deps\n// ---------------------------------------------------------------------------\n\nexport interface StatusToolDeps {\n getState: (sessionID: string) => ExtendedState | undefined;\n summarizeState: (state: ExtendedState | null | undefined) => string;\n getHistory: (sessionID: string) => string[];\n}\n\n// ---------------------------------------------------------------------------\n// Tool factory\n// ---------------------------------------------------------------------------\n\nexport function createStatusTool(deps: StatusToolDeps) {\n return tool({\n description: \"Show autopilot status for the current session\",\n args: {},\n async execute(_args, context) {\n const state = deps.getState(context.sessionID);\n\n if (!state) {\n return deps.summarizeState(state);\n }\n\n const history = deps.getHistory(context.sessionID);\n const historyStr =\n history.length > 0\n ? `\\nRecent events:\\n- ${history.join(\"\\n- \")}`\n : \"\";\n\n return `${deps.summarizeState(state)}${historyStr}`;\n },\n });\n}\n",
|
|
19
|
+
"import { tool } from \"@opencode-ai/plugin\";\nimport type { ExtendedState } from \"../types/index.ts\";\n\n// ---------------------------------------------------------------------------\n// Deps\n// ---------------------------------------------------------------------------\n\nexport interface StopToolDeps {\n getState: (sessionID: string) => ExtendedState | undefined;\n onStop: (sessionID: string, reason: string | undefined) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Tool factory\n// ---------------------------------------------------------------------------\n\nexport function createStopTool(deps: StopToolDeps) {\n return tool({\n description: \"Stop autopilot mode for the current session\",\n args: {\n reason: tool.schema\n .string()\n .optional()\n .describe(\"Optional reason to include in the stop message\"),\n },\n async execute(args, context) {\n const state = deps.getState(context.sessionID);\n\n if (!state || state.mode !== \"ENABLED\") {\n return \"Autopilot is not running in this session.\";\n }\n\n deps.onStop(context.sessionID, args.reason);\n\n return args.reason\n ? `Autopilot stopped: ${args.reason}`\n : \"Autopilot stopped for this session.\";\n },\n });\n}\n",
|
|
20
|
+
"import { tool } from \"@opencode-ai/plugin\";\n\nexport function createHelpTool() {\n return tool({\n description: \"Show autopilot usage instructions\",\n args: {},\n execute: async () => {\n return `\n## Autopilot Usage\n\nUse the global \\`Autopilot\\` agent to control the autopilot plugin.\n\n**Start Autopilot:**\nSwitch to the \\`Autopilot\\` agent, then send the task you want delegated.\n\nExamples:\n- \\`Fix the failing tests\\`\n- \\`Use allow-all mode and build-high to refactor the reducer\\`\n\n**Check Status:**\n- \\`status\\`\n- \\`is autopilot running?\\`\n\n**Stop Autopilot:**\n- \\`stop\\`\n- \\`stop because I want to inspect manually\\`\n\nDefaults:\n- permission mode: \\`limited\\`\n- continuation limit: \\`10\\`\n- worker agent: \\`pi\\`\n`.trim();\n },\n });\n}\n",
|
|
21
|
+
"import type { Plugin } from \"@opencode-ai/plugin\";\nimport type { ExtendedState } from \"./types/index.ts\";\nimport { SessionCache } from \"./state/session-cache.ts\";\nimport { createSessionState } from \"./state/factory.ts\";\nimport {\n buildAutopilotSystemPrompt,\n buildContinuationPrompt,\n formatUsageMetadata,\n inferAutopilotDirective,\n normalizeMaxContinues,\n stripAutopilotMarker,\n summarizeAutopilotState,\n} from \"./prompts/index.ts\";\nimport {\n createEventHandler,\n createSessionTracking,\n} from \"./hooks/event-handler.ts\";\nimport type { SessionTracking } from \"./hooks/event-handler.ts\";\nimport { createPermissionHook } from \"./hooks/permission.ts\";\nimport { createSystemTransformHook } from \"./hooks/system-transform.ts\";\nimport { createChatMessageHook } from \"./hooks/chat-message.ts\";\nimport { createToolAfterHook } from \"./hooks/tool-after.ts\";\nimport { createStartTool } from \"./tools/start.ts\";\nimport { createStatusTool } from \"./tools/status.ts\";\nimport { createStopTool } from \"./tools/stop.ts\";\nimport { createHelpTool } from \"./tools/help.ts\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst AUTOPILOT_FALLBACK_AGENT = \"pi\";\nconst MAX_HISTORY_ENTRIES = 10;\n\n// ---------------------------------------------------------------------------\n// Plugin\n// ---------------------------------------------------------------------------\n\nexport const AutopilotPlugin: Plugin = async ({\n client,\n directory,\n worktree,\n}) => {\n // -- Shared state stores (per-session) --\n const stateBySession = new Map<string, ExtendedState>();\n const trackingBySession = new Map<string, SessionTracking>();\n const suppressCountBySession = new Map<string, number>();\n const historyBySession = new Map<string, string[]>();\n const permissionModeBySession = new Map<string, \"allow-all\" | \"limited\">();\n const sessionCache = new SessionCache();\n\n // -- State accessors --\n const getState = (sessionID: string): ExtendedState | undefined =>\n stateBySession.get(sessionID);\n\n const setState = (sessionID: string, state: ExtendedState): void => {\n stateBySession.set(sessionID, state);\n };\n\n const deleteState = (sessionID: string): void => {\n stateBySession.delete(sessionID);\n trackingBySession.delete(sessionID);\n suppressCountBySession.delete(sessionID);\n historyBySession.delete(sessionID);\n permissionModeBySession.delete(sessionID);\n };\n\n const getTracking = (sessionID: string): SessionTracking | undefined =>\n trackingBySession.get(sessionID);\n\n const initSession = (sessionID: string): void => {\n trackingBySession.set(sessionID, createSessionTracking());\n suppressCountBySession.set(sessionID, 0);\n historyBySession.set(sessionID, []);\n };\n\n const recordHistory = (sessionID: string, message: string): void => {\n let history = historyBySession.get(sessionID);\n if (!history) {\n history = [];\n historyBySession.set(sessionID, history);\n }\n history.push(message);\n if (history.length > MAX_HISTORY_ENTRIES) {\n historyBySession.set(sessionID, history.slice(-MAX_HISTORY_ENTRIES));\n }\n };\n\n // -- Toast helper --\n const safeToast = async (opts: {\n title: string;\n message: string;\n variant: \"info\" | \"success\" | \"warning\" | \"error\";\n }): Promise<void> => {\n try {\n await client.tui.showToast({\n directory,\n workspace: worktree,\n title: opts.title,\n message: opts.message,\n variant: opts.variant,\n duration: 3000,\n });\n } catch {\n // Ignore TUI toast failures in non-TUI sessions.\n }\n };\n\n // -- Stop helper --\n const setStopped = async (\n sessionID: string,\n reason: string,\n detail: string | undefined,\n variant: \"info\" | \"success\" | \"warning\" | \"error\" = \"info\",\n ): Promise<void> => {\n const state = getState(sessionID);\n if (!state) return;\n\n state.mode = \"DISABLED\";\n state.phase = \"STOPPED\";\n state.stop_reason = \"USER_STOP\";\n recordHistory(sessionID, detail ? `${reason}: ${detail}` : reason);\n\n await safeToast({\n title: \"Autopilot stopped\",\n message: detail ? `${reason}: ${detail}` : reason,\n variant,\n });\n };\n\n // -- Dispatch helper --\n const dispatchPrompt = async (\n sessionID: string,\n state: ExtendedState,\n promptText: string,\n ): Promise<void> => {\n const tracking = getTracking(sessionID);\n if (!tracking) return;\n\n tracking.awaitingWorkerReply = true;\n tracking.lastAssistantMessageID = undefined;\n\n try {\n // The plugin runtime provides a wrapped client that accepts these\n // named params directly (matching the legacy JS plugin pattern).\n await (client.session.promptAsync as (opts: Record<string, unknown>) => Promise<unknown>)({\n directory,\n workspace: worktree,\n sessionID,\n agent: state.worker_agent,\n parts: [{ type: \"text\" as const, text: promptText }],\n });\n } catch {\n // Prompt dispatch failure — will be caught by session.error\n }\n };\n\n // -- Continuation logic (called on session.idle) --\n const maybeContinue = async (sessionID: string): Promise<void> => {\n const state = getState(sessionID);\n const tracking = getTracking(sessionID);\n if (!state || state.mode !== \"ENABLED\" || !tracking) return;\n\n // Initial dispatch after arming\n if (state.phase === \"OBSERVE\" && state.continuation_count === 0 && !tracking.lastAssistantMessageID) {\n recordHistory(sessionID, `Starting task with ${state.worker_agent}`);\n await safeToast({\n title: \"Autopilot armed\",\n message: `Starting task with ${state.worker_agent}`,\n variant: \"info\",\n });\n await dispatchPrompt(sessionID, state, state.goal);\n return;\n }\n\n // Permission block check\n if (tracking.blockedByPermission) {\n tracking.blockedByPermission = false;\n await setStopped(\n sessionID,\n \"Blocked by permissions\",\n tracking.permissionBlockMessage ??\n \"A required action was denied in limited mode.\",\n \"warning\",\n );\n return;\n }\n\n // Check for worker reply\n const messageID = tracking.lastAssistantMessageID;\n if (!messageID) return;\n\n tracking.awaitingWorkerReply = false;\n tracking.lastAssistantMessageID = undefined;\n\n const assistantText = sessionCache.getMessageText(sessionID, messageID);\n const directive = inferAutopilotDirective(assistantText);\n\n if (directive.status === \"complete\") {\n await setStopped(\n sessionID,\n \"Task completed\",\n directive.reason,\n \"success\",\n );\n return;\n }\n\n if (directive.status === \"blocked\") {\n await setStopped(\n sessionID,\n \"Task blocked\",\n directive.reason,\n \"warning\",\n );\n return;\n }\n\n // Check continuation limit\n if (state.continuation_count >= state.max_continues) {\n await setStopped(\n sessionID,\n \"Continuation limit reached\",\n `Stopped after ${state.continuation_count} autonomous continuations.`,\n \"warning\",\n );\n return;\n }\n\n // Continue\n state.continuation_count += 1;\n const usageBits = formatUsageMetadata(\n tracking.lastUsage as Parameters<typeof formatUsageMetadata>[0],\n );\n const suffix = usageBits ? ` (${usageBits})` : \"\";\n recordHistory(\n sessionID,\n `Continuation ${state.continuation_count}/${state.max_continues}${suffix}`,\n );\n\n await safeToast({\n title: \"Autopilot continuing\",\n message: `Continuation ${state.continuation_count}/${state.max_continues}${suffix}`,\n variant: \"info\",\n });\n\n await dispatchPrompt(\n sessionID,\n state,\n buildContinuationPrompt({\n continueCount: state.continuation_count,\n maxContinues: state.max_continues,\n task: state.goal,\n }),\n );\n };\n\n // -- Build hooks --\n const eventHandler = createEventHandler({\n getState,\n deleteState,\n sessionCache,\n getTracking,\n onSessionIdle: maybeContinue,\n onSessionError: async (sessionID, error) => {\n const errorMessage =\n error?.data?.message ?? \"Autopilot encountered an unknown error.\";\n const isAbort = error?.name === \"MessageAbortedError\";\n const reason = isAbort ? \"Interrupted\" : \"Error\";\n const variant: \"warning\" | \"error\" = isAbort ? \"warning\" : \"error\";\n await setStopped(sessionID, reason, errorMessage, variant);\n },\n });\n\n const permissionHook = createPermissionHook({\n getState,\n getPermissionMode: (sessionID) => permissionModeBySession.get(sessionID),\n onPermissionDenied: (sessionID, permission) => {\n const tracking = getTracking(sessionID);\n if (!tracking) return;\n tracking.blockedByPermission = true;\n const patternStr = Array.isArray(permission.pattern)\n ? permission.pattern.join(\", \")\n : permission.pattern ?? \"\";\n tracking.permissionBlockMessage =\n `Denied ${permission.type} ${patternStr}`.trim();\n recordHistory(\n sessionID,\n tracking.permissionBlockMessage,\n );\n },\n });\n\n const systemTransformHook = createSystemTransformHook({\n getState,\n getSuppressCount: (sessionID) =>\n suppressCountBySession.get(sessionID) ?? 0,\n decrementSuppressCount: (sessionID) => {\n const current = suppressCountBySession.get(sessionID) ?? 0;\n if (current > 0) {\n suppressCountBySession.set(sessionID, current - 1);\n }\n },\n buildSystemPrompt: buildAutopilotSystemPrompt,\n });\n\n const chatMessageHook = createChatMessageHook({\n getState,\n incrementSuppressCount: (sessionID) => {\n const current = suppressCountBySession.get(sessionID) ?? 0;\n suppressCountBySession.set(sessionID, current + 1);\n },\n });\n\n const toolAfterHook = createToolAfterHook({\n stripMarker: stripAutopilotMarker,\n });\n\n // -- Build tools --\n const startTool = createStartTool({\n getState,\n setState,\n createSessionState,\n normalizeMaxContinues,\n initSession,\n defaultWorkerAgent: AUTOPILOT_FALLBACK_AGENT,\n onArmed: async (sessionID, state) => {\n // Extract and store permissionMode\n const pm = (state as unknown as Record<string, unknown>)[\"permissionMode\"];\n if (pm === \"allow-all\" || pm === \"limited\") {\n permissionModeBySession.set(sessionID, pm);\n } else {\n permissionModeBySession.set(sessionID, \"limited\");\n }\n recordHistory(\n sessionID,\n `Armed in ${permissionModeBySession.get(sessionID)} mode with ${state.worker_agent}.`,\n );\n recordHistory(\n sessionID,\n `Continuation limit: ${state.max_continues}.`,\n );\n },\n });\n\n const statusTool = createStatusTool({\n getState,\n summarizeState: summarizeAutopilotState,\n getHistory: (sessionID) => historyBySession.get(sessionID) ?? [],\n });\n\n const stopTool = createStopTool({\n getState,\n onStop: (sessionID, reason) => {\n const state = getState(sessionID);\n if (!state) return;\n state.mode = \"DISABLED\";\n state.phase = \"STOPPED\";\n state.stop_reason = \"USER_STOP\";\n recordHistory(\n sessionID,\n reason ? `Cancelled by user: ${reason}` : \"Cancelled by user\",\n );\n },\n });\n\n const helpTool = createHelpTool();\n\n // -- Return assembled hooks --\n return {\n tool: {\n autopilot_start: startTool,\n autopilot_status: statusTool,\n autopilot_stop: stopTool,\n autopilot_help: helpTool,\n },\n\n event: eventHandler,\n\n \"permission.ask\": permissionHook,\n\n \"experimental.chat.system.transform\": systemTransformHook,\n\n \"chat.message\": chatMessageHook,\n\n \"tool.execute.after\": toolAfterHook,\n };\n};\n"
|
|
22
|
+
],
|
|
23
|
+
"mappings": ";AAoBA,SAAS,kBAAkB,GAAiB;AAAA,EAC1C,OAAO;AAAA,IACL,OAAO,IAAI;AAAA,IACX,QAAQ,IAAI;AAAA,IACZ,WAAW,IAAI;AAAA,EACjB;AAAA;AAAA;AAGK,MAAM,aAAa;AAAA,EACP,WAAW,IAAI;AAAA,EAExB,aAAa,CAAC,WAAiC;AAAA,IACrD,MAAM,WAAW,KAAK,SAAS,IAAI,SAAS;AAAA,IAE5C,IAAI,UAAU;AAAA,MACZ,OAAO;AAAA,IACT;AAAA,IAEA,MAAM,UAAU,mBAAmB;AAAA,IACnC,KAAK,SAAS,IAAI,WAAW,OAAO;AAAA,IACpC,OAAO;AAAA;AAAA,EAGT,OAAO,CAAC,WAAmB,WAAmB,MAAyB;AAAA,IACrE,KAAK,cAAc,SAAS,EAAE,MAAM,IAAI,WAAW,IAAI;AAAA;AAAA,EAGzD,OAAO,CAAC,WAAmB,WAAuC;AAAA,IAChE,OAAO,KAAK,SAAS,IAAI,SAAS,GAAG,MAAM,IAAI,SAAS,KAAK;AAAA;AAAA,EAG/D,QAAQ,CAAC,WAAmB,WAAmB,OAAqB;AAAA,IAClE,KAAK,cAAc,SAAS,EAAE,OAAO,IAAI,WAAW,KAAK;AAAA;AAAA,EAG3D,QAAQ,CAAC,WAAmB,WAAkC;AAAA,IAC5D,OAAO,KAAK,SAAS,IAAI,SAAS,GAAG,OAAO,IAAI,SAAS,KAAK;AAAA;AAAA,EAGhE,WAAW,CACT,WACA,QACA,WACA,MACM;AAAA,IACN,KAAK,cAAc,SAAS,EAAE,UAAU,IAAI,QAAQ;AAAA,MAClD,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,IACF,CAAC;AAAA;AAAA,EAGH,WAAW,CAAC,WAAmB,QAAuC;AAAA,IACpE,OAAO,KAAK,SAAS,IAAI,SAAS,GAAG,UAAU,IAAI,MAAM,KAAK;AAAA;AAAA,EAGhE,cAAc,CAAC,WAAmB,WAA2B;AAAA,IAC3D,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS;AAAA,IAE3C,IAAI,CAAC,SAAS;AAAA,MACZ,OAAO;AAAA,IACT;AAAA,IAEA,OAAO,CAAC,GAAG,QAAQ,UAAU,OAAO,CAAC,EAClC,OAAO,CAAC,SAAS,KAAK,cAAc,SAAS,EAC7C,IAAI,CAAC,SAAS,KAAK,IAAI,EACvB,KAAK,EAAE;AAAA;AAAA,EAGZ,QAAQ,CAAC,WAAoC;AAAA,IAC3C,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS,KAAK,mBAAmB;AAAA,IAEnE,OAAO;AAAA,MACL,OAAO,OAAO,YAAY,QAAQ,KAAK;AAAA,MACvC,QAAQ,OAAO,YAAY,QAAQ,MAAM;AAAA,MACzC,WAAW,CAAC,GAAG,QAAQ,UAAU,OAAO,CAAC;AAAA,IAC3C;AAAA;AAAA,EAGF,OAAO,CAAC,WAAyB;AAAA,IAC/B,KAAK,SAAS,OAAO,SAAS;AAAA;AAElC;;;AC3EA,IAAM,wBAAwB,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,aAAa;AAC5E,IAAM,4BAA4B;AAClC,IAAM,wBAAwB;AAC9B,IAAM,2BAA2B;AACjC,IAAM,6BAA6B;AACnC,IAAM,0BAA0B;AAChC,IAAM,uBAAuB;AAE7B,SAAS,eAAe,GAAc;AAAA,EACpC,OAAO;AAAA,IACL,OAAO,CAAC;AAAA,IACR,YAAY,CAAC;AAAA,IACb,iBAAiB,CAAC;AAAA,IAClB,eAAe,CAAC;AAAA,IAChB,cAAc,CAAC;AAAA,IACf,OAAO;AAAA,EACT;AAAA;AAGF,SAAS,mBAAmB,GAAkB;AAAA,EAC5C,OAAO;AAAA,IACL,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,iBAAiB,CAAC;AAAA,IAClB,eAAe,CAAC;AAAA,IAChB,eAAe;AAAA,EACjB;AAAA;AAGF,SAAS,gBAAgB,CAAC,cAAoC;AAAA,EAC5D,OAAO;AAAA,IACL,QAAQ,aAAa,SAAS,IAAI,YAAY;AAAA,IAC9C,eAAe,CAAC,GAAG,YAAY;AAAA,IAC/B,cAAc;AAAA,IACd,cAAc,CAAC;AAAA,IACf,eAAe;AAAA,EACjB;AAAA;AAGF,SAAS,kBAAkB,CACzB,iBACA,WACc;AAAA,EACd,MAAM,iBAAiB,mBAAmB;AAAA,EAC1C,MAAM,mBACJ,mBAAmB,OAAO,kBAAkB,YAAY;AAAA,EAE1D,OAAO;AAAA,IACL,kBAAkB;AAAA,IAClB;AAAA,IACA,mBAAmB;AAAA,IACnB,cAAc;AAAA,IACd,oBAAoB;AAAA,EACtB;AAAA;AAGF,SAAS,mBAAmB,CAAC,SAAmD;AAAA,EAC9E,OAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,mBAAmB;AAAA,IACnB,wBAAwB;AAAA,IACxB,kBAAkB,QAAQ,kBAAkB;AAAA,IAC5C,oBAAoB,QAAQ,oBAAoB;AAAA,IAChD,iBAAiB,QAAQ,iBAAiB;AAAA,EAC5C;AAAA;AAGK,SAAS,kBAAkB,CAChC,MACA,UAAqC,CAAC,GACvB;AAAA,EACf,MAAM,YAAY,QAAQ,aAAa;AAAA,EACvC,MAAM,eAAe,QAAQ,gBAAgB,CAAC;AAAA,EAE9C,OAAO;AAAA,IACL,YAAY;AAAA,IACZ,MAAM,QAAQ,QAAQ;AAAA,IACtB,OAAO,QAAQ,SAAS;AAAA,IACxB;AAAA,IACA,YAAY,gBAAgB;AAAA,IAC5B,qBAAqB,CAAC;AAAA,IACtB,eAAe,CAAC,GAAI,QAAQ,gBAAgB,qBAAsB;AAAA,IAClE,eAAe,CAAC,GAAG,YAAY;AAAA,IAC/B,gBAAgB,oBAAoB;AAAA,IACpC,aAAa,iBAAiB,QAAQ,gBAAgB,YAAY;AAAA,IAClE,eAAe,mBACb,QAAQ,mBAAmB,MAC3B,QAAQ,oBAAoB,yBAC9B;AAAA,IACA,mBAAmB;AAAA,IACnB,kBAAkB,CAAC;AAAA,IACnB,gBAAgB,oBAAoB,OAAO;AAAA,IAC3C,aAAa;AAAA,IACb,qBAAqB;AAAA,MACnB,QAAQ,CAAC;AAAA,MACT,iBAAiB;AAAA,MACjB,kBAAkB;AAAA,MAClB,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,IAClB;AAAA,IACA,oBAAoB;AAAA,IACpB,eAAe,QAAQ,gBAAgB;AAAA,IACvC,cAAc,QAAQ,eAAe;AAAA,IACrC,iBAAiB;AAAA,IACjB,WAAW;AAAA,EACb;AAAA;AAGK,SAAS,kBAAkB,CAChC,WACA,MACA,UAAwD,CAAC,GAC1C;AAAA,EACf,OAAO,mBAAmB,MAAM;AAAA,OAC3B;AAAA,IACH;AAAA,IACA,MAAM,QAAQ,QAAQ;AAAA,IACtB,OAAO,QAAQ,SAAS;AAAA,EAC1B,CAAC;AAAA;;;AC7II,SAAS,uBAAuB,CACrC,SACQ;AAAA,EACR,OAAO;AAAA,IACL,0BAA0B,QAAQ,iBAAiB,QAAQ;AAAA,IAC3D;AAAA,IACA,kBAAkB,QAAQ;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK;AAAA,CAAI;AAAA;;ACTb,IAAM,sBACJ;AACF,IAAM,kBACJ;AAEK,SAAS,oBAAoB,CAAC,MAAyC;AAAA,EAC5E,MAAM,QAAQ,KAAK,MAAM,mBAAmB;AAAA,EAE5C,IAAI,CAAC,OAAO;AAAA,IACV,OAAO;AAAA,EACT;AAAA,EAEA,SAAS,WAAW,aAAa;AAAA,EAEjC,IAAI,CAAC,aAAa,cAAc,WAAW;AAAA,IACzC,OAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS,UAAU,YAAY;AAAA,EACrC,OAAO;AAAA,IACL;AAAA,IACA,QAAQ,UAAU,KAAK,KAAK,cAAc,MAAM;AAAA,EAClD;AAAA;AAGK,SAAS,oBAAoB,CAAC,MAAsB;AAAA,EACzD,OAAO,KAAK,QAAQ,qBAAqB,EAAE,EAAE,QAAQ;AAAA;AAGhD,SAAS,uBAAuB,CAAC,MAAkC;AAAA,EACxE,MAAM,SAAS,qBAAqB,IAAI;AAAA,EAExC,IAAI,QAAQ;AAAA,IACV,OAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS,KAAK,KAAK;AAAA,EACzB,IAAI,CAAC,QAAQ;AAAA,IACX,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAAA,EACF;AAAA,EAEA,IAAI,gBAAgB,KAAK,MAAM,GAAG;AAAA,IAChC,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAAA,EACF;AAAA,EAEA,OAAO;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA;AAGF,SAAS,aAAa,CAAC,QAA0C;AAAA,EAC/D,IAAI,WAAW,YAAY;AAAA,IACzB,OAAO;AAAA,EACT;AAAA,EAEA,IAAI,WAAW,WAAW;AAAA,IACxB,OAAO;AAAA,EACT;AAAA,EAEA,OAAO;AAAA;;AC5DF,SAAS,mBAAmB,CAAC,OAAiD;AAAA,EACnF,IAAI,CAAC,OAAO;AAAA,IACV,OAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAkB,CAAC;AAAA,EAEzB,IAAI,MAAM,QAAQ;AAAA,IAChB,QAAQ,OAAO,OAAO,WAAW,MAAM;AAAA,IAEvC,IAAI,OAAO,SAAS,KAAK,GAAG;AAAA,MAC1B,MAAM,KAAK,GAAG,cAAc;AAAA,IAC9B,EAAO,SAAI,OAAO,SAAS,KAAK,KAAK,OAAO,SAAS,MAAM,GAAG;AAAA,MAC5D,MAAM,aAAuB,CAAC;AAAA,MAE9B,IAAI,OAAO,SAAS,KAAK,GAAG;AAAA,QAC1B,WAAW,KAAK,MAAM,OAAO;AAAA,MAC/B;AAAA,MAEA,IAAI,OAAO,SAAS,MAAM,GAAG;AAAA,QAC3B,WAAW,KAAK,OAAO,QAAQ;AAAA,MACjC;AAAA,MAEA,IAAI,WAAW,SAAS,GAAG;AAAA,QACzB,MAAM,KAAK,WAAW,KAAK,IAAI,CAAC;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,MAAM,MAAM;AAAA,EAC9B,IAAI,OAAO,SAAS,SAAS,GAAG;AAAA,IAC9B,MAAM,KAAK,QAAQ,WAAW;AAAA,EAChC;AAAA,EAEA,OAAO,MAAM,KAAK,IAAI;AAAA;AAGjB,SAAS,uBAAuB,CACrC,OACQ;AAAA,EACR,IAAI,CAAC,OAAO;AAAA,IACV,OAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS;AAAA,IACb,SAAS,MAAM;AAAA,IACf,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM,sBAAsB,MAAM;AAAA,IAC/C,SAAS,MAAM;AAAA,EACjB;AAAA,EAEA,IAAI,MAAM,aAAa;AAAA,IACrB,OAAO,KAAK,QAAQ,MAAM,aAAa;AAAA,EACzC;AAAA,EAEA,MAAM,cAAc,MAAM,oBAAoB,OAAO,GAAG,EAAE;AAAA,EAC1D,IAAI,aAAa;AAAA,IACf,OAAO,KAAK,cAAc,YAAY,YAAY;AAAA,EACpD;AAAA,EAEA,OAAO,qBAAqB,OAAO,KAAK,IAAI;AAAA;;ACzEvC,IAAM,kCAAkC;AACxC,IAAM,qCAAqC;AAE3C,SAAS,qBAAqB,CAAC,OAAwB;AAAA,EAC5D,MAAM,UAAU,OAAO,KAAK;AAAA,EAE5B,IAAI,CAAC,OAAO,SAAS,OAAO,KAAK,WAAW,GAAG;AAAA,IAC7C,OAAO;AAAA,EACT;AAAA,EAEA,OAAO,KAAK,IACV,oCACA,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC,CACjC;AAAA;;ACbK,SAAS,0BAA0B,GAAW;AAAA,EACnD,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK;AAAA,CAAI;AAAA;;ACKN,SAAS,qBAAqB,GAAoB;AAAA,EACvD,OAAO;AAAA,IACL,wBAAwB;AAAA,IACxB,WAAW;AAAA,IACX,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,IACrB,wBAAwB;AAAA,EAC1B;AAAA;AAuBF,SAAS,QAAQ,CAAC,OAAiC;AAAA,EACjD,OAAO,OAAO,UAAU;AAAA;AAG1B,SAAS,QAAQ,CAAC,OAAkD;AAAA,EAClE,OAAO,OAAO,UAAU,YAAY,UAAU;AAAA;AAGhD,SAAS,cAAc,CACrB,OAC4D;AAAA,EAC5D,IAAI,CAAC,SAAS,KAAK;AAAA,IAAG;AAAA,EAEtB,MAAM,SAAyD,CAAC;AAAA,EAEhE,IAAI,SAAS,MAAM,OAAO,GAAG;AAAA,IAC3B,OAAO,OAAO,MAAM;AAAA,EACtB;AAAA,EAEA,IAAI,SAAS,MAAM,OAAO,KAAK,SAAS,MAAM,QAAQ,UAAU,GAAG;AAAA,IACjE,OAAO,OAAO,EAAE,SAAS,MAAM,QAAQ,WAAW;AAAA,EACpD;AAAA,EAEA,OAAO;AAAA;AAYF,SAAS,kBAAkB,CAChC,MAC+C;AAAA,EAC/C;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE;AAAA,EAEJ,OAAO,eAAe,WAAW,CAAC,OAEhB;AAAA,IAChB,QAAQ,UAAU;AAAA,IAElB,QAAQ,MAAM;AAAA,WACP,mBAAmB;AAAA,QACtB,MAAM,OAAO,MAAM,WAAW;AAAA,QAC9B,IAAI,CAAC,SAAS,IAAI;AAAA,UAAG;AAAA,QAErB,MAAM,YAAY,KAAK;AAAA,QACvB,MAAM,YAAY,KAAK;AAAA,QACvB,MAAM,OAAO,KAAK;AAAA,QAElB,IAAI,CAAC,SAAS,SAAS,KAAK,CAAC,SAAS,SAAS,KAAK,CAAC,SAAS,IAAI,GAAG;AAAA,UACnE;AAAA,QACF;AAAA,QAEA,IAAI,SAAS,UAAU,SAAS,aAAa;AAAA,UAC3C,aAAa,QAAQ,WAAW,WAAW,IAAI;AAAA,QACjD;AAAA,QAEA,IAAI,WAAW,QAAQ,SAAS,KAAK,QAAQ,GAAG;AAAA,UAC9C,aAAa,SAAS,WAAW,WAAW,KAAK,QAAQ;AAAA,QAC3D;AAAA,QAEA,IAAI,SAAS,aAAa;AAAA,UACxB,MAAM,QAAQ,SAAS,SAAS;AAAA,UAChC,MAAM,WAAW,YAAY,SAAS;AAAA,UAEtC,IAAI,SAAS,YAAY,SAAS,qBAAqB;AAAA,YACrD,MAAM,QACJ,WAAW,QAAQ,SAAS,KAAK,QAAQ,IACrC,KAAK,WACL;AAAA,YAEN,IAAI,UAAU,MAAM,cAAc;AAAA,cAChC,SAAS,yBAAyB;AAAA,cAClC,SAAS,YAAY;AAAA,gBACnB,QAAQ,KAAK;AAAA,gBACb,MAAM,KAAK;AAAA,cACb;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,QACA;AAAA,MACF;AAAA,WAEK,wBAAwB;AAAA,QAC3B,MAAM,OAAO,MAAM,WAAW;AAAA,QAC9B,IAAI,CAAC,SAAS,IAAI;AAAA,UAAG;AAAA,QAErB,IACE,KAAK,YAAY,UACjB,CAAC,SAAS,KAAK,YAAY,KAC3B,CAAC,SAAS,KAAK,YAAY,KAC3B,CAAC,SAAS,KAAK,KAAK,KACpB,CAAC,SAAS,KAAK,OAAO,GACtB;AAAA,UACA;AAAA,QACF;AAAA,QAEA,MAAM,QAAQ,SAAS,KAAK,YAAY;AAAA,QACxC,IAAI,CAAC;AAAA,UAAO;AAAA,QAEZ,MAAM,aAAa,aAAa,QAC9B,KAAK,cACL,KAAK,YACP;AAAA,QACA,MAAM,cAAc,aAAa,SAC/B,KAAK,cACL,KAAK,YACP;AAAA,QAEA,IAAI,eAAe,eAAe,gBAAgB,MAAM,cAAc;AAAA,UACpE,aAAa,YACX,KAAK,cACL,KAAK,OACL,KAAK,cACL,KAAK,OACP;AAAA,QACF;AAAA,QACA;AAAA,MACF;AAAA,WAEK,gBAAgB;AAAA,QACnB,MAAM,YAAY,MAAM,WAAW;AAAA,QACnC,IAAI,CAAC,SAAS,SAAS;AAAA,UAAG;AAAA,QAE1B,MAAM,cAAc,SAAS;AAAA,QAC7B;AAAA,MACF;AAAA,WAEK,iBAAiB;AAAA,QACpB,MAAM,YAAY,MAAM,WAAW;AAAA,QACnC,IAAI,CAAC,SAAS,SAAS;AAAA,UAAG;AAAA,QAE1B,MAAM,QAAQ,SAAS,SAAS;AAAA,QAChC,IAAI,CAAC,SAAS,MAAM,SAAS;AAAA,UAAW;AAAA,QAExC,MAAM,QAAQ,eAAe,MAAM,WAAW,QAAQ;AAAA,QACtD,MAAM,eAAe,WAAW,KAAK;AAAA,QACrC;AAAA,MACF;AAAA,WAEK,mBAAmB;AAAA,QACtB,MAAM,OAAO,MAAM,WAAW;AAAA,QAC9B,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK;AAAA,UAAG;AAAA,QAE9C,aAAa,QAAQ,KAAK,KAAK;AAAA,QAC/B,YAAY,KAAK,KAAK;AAAA,QACtB;AAAA,MACF;AAAA,WAEK,sBAAsB;AAAA,QACzB,MAAM,YAAY,MAAM,WAAW;AAAA,QACnC,IAAI,CAAC,SAAS,SAAS;AAAA,UAAG;AAAA,QAE1B,MAAM,QAAQ,SAAS,SAAS;AAAA,QAChC,MAAM,WAAW,YAAY,SAAS;AAAA,QAEtC,IAAI,CAAC,SAAS,MAAM,SAAS,aAAa,CAAC;AAAA,UAAU;AAAA,QAIrD,SAAS,sBAAsB;AAAA,QAC/B,MAAM,WAAW,SAAS,MAAM,WAAW,OAAO,IAC9C,MAAM,WAAW,UACjB;AAAA,QACJ,MAAM,WAAW,MAAM,WAAW;AAAA,QAClC,MAAM,aAAa,MAAM,QAAQ,QAAQ,IACrC,SAAS,OAAO,QAAQ,EAAE,KAAK,IAAI,IACnC,SAAS,QAAQ,IACf,WACA;AAAA,QACN,SAAS,yBACP,UAAU,YAAY,aAAa,KAAK;AAAA,QAC1C;AAAA,MACF;AAAA;AAAA;AAAA;;;AC/LC,SAAS,oBAAoB,CAClC,MACqE;AAAA,EACrE,OAAO,OACL,OACA,WACkB;AAAA,IAClB,MAAM,QAAQ,KAAK,SAAS,MAAM,SAAS;AAAA,IAE3C,IAAI,CAAC,SAAS,MAAM,SAAS,WAAW;AAAA,MACtC;AAAA,IACF;AAAA,IAEA,MAAM,iBAAiB,KAAK,kBAAkB,MAAM,SAAS;AAAA,IAE7D,IAAI,mBAAmB,aAAa;AAAA,MAClC,OAAO,SAAS;AAAA,MAChB;AAAA,IACF;AAAA,IAEA,IAAI,mBAAmB,WAAW;AAAA,MAChC,OAAO,SAAS;AAAA,MAChB,KAAK,qBAAqB,MAAM,WAAW;AAAA,QACzC,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,QACf,OAAO,MAAM;AAAA,MACf,CAAC;AAAA,IACH;AAAA;AAAA;;;ACzCG,SAAS,yBAAyB,CACvC,MAIiB;AAAA,EACjB;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE;AAAA,EAEJ,OAAO,OACL,OACA,WACkB;AAAA,IAClB,QAAQ,cAAc;AAAA,IAEtB,IAAI,cAAc,WAAW;AAAA,MAC3B;AAAA,IACF;AAAA,IAEA,MAAM,QAAQ,SAAS,SAAS;AAAA,IAEhC,IAAI,CAAC,SAAS,MAAM,SAAS,WAAW;AAAA,MACtC;AAAA,IACF;AAAA,IAEA,MAAM,gBAAgB,iBAAiB,SAAS;AAAA,IAEhD,IAAI,gBAAgB,GAAG;AAAA,MACrB,uBAAuB,SAAS;AAAA,MAChC;AAAA,IACF;AAAA,IAEA,IAAI,CAAC,MAAM,QAAQ,OAAO,MAAM,GAAG;AAAA,MACjC,OAAO,SAAS,CAAC;AAAA,IACnB;AAAA,IAEA,OAAO,OAAO,KAAK,kBAAkB,CAAC;AAAA;AAAA;;;AC7DnC,IAAM,gBAAgB;AAwBtB,SAAS,qBAAqB,CACnC,MACuE;AAAA,EACvE,QAAQ,UAAU,2BAA2B;AAAA,EAE7C,OAAO,OACL,OACA,YACkB;AAAA,IAClB,MAAM,QAAQ,SAAS,MAAM,SAAS;AAAA,IAEtC,IAAI,CAAC,SAAS,MAAM,SAAS,WAAW;AAAA,MACtC;AAAA,IACF;AAAA,IAEA,IAAI,MAAM,UAAU,eAAe;AAAA,MACjC,uBAAuB,MAAM,SAAS;AAAA,IACxC;AAAA;AAAA;;;ACzCJ,IAAM,wBAAwB;AAmBvB,SAAS,mBAAmB,CACjC,MACmE;AAAA,EACnE,OAAO,OACL,OACA,WACkB;AAAA,IAClB,IAAI,MAAM,SAAS,uBAAuB;AAAA,MACxC;AAAA,IACF;AAAA,IAEA,OAAO,SAAS,KAAK,YAAY,OAAO,MAAM;AAAA;AAAA;;;AClClD;AA4BA,IAAM,2BAA2B;AAE1B,SAAS,eAAe,CAAC,MAAqB;AAAA,EACnD,OAAO,KAAK;AAAA,IACV,aAAa;AAAA,IACb,MAAM;AAAA,MACJ,MAAM,KAAK,OACR,OAAO,EACP,IAAI,CAAC,EACL,SAAS,2CAA2C;AAAA,MACvD,gBAAgB,KAAK,OAClB,KAAK,CAAC,WAAW,WAAW,CAAC,EAC7B,SAAS,EACT,SACC,yDACF;AAAA,MACF,cAAc,KAAK,OAChB,OAAO,EACP,IAAI,EACJ,SAAS,EACT,SAAS,EACT,SACC,iFACF;AAAA,MACF,aAAa,KAAK,OACf,OAAO,EACP,SAAS,EACT,SACC,4DACF;AAAA,IACJ;AAAA,SACM,QAAO,CAAC,MAAM,SAAS;AAAA,MAC3B,IAAI,KAAK,KAAK,KAAK,EAAE,YAAY,MAAM,QAAQ;AAAA,QAC7C,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBb,KAAK;AAAA,MACD;AAAA,MAEA,MAAM,iBAAiB,KAAK,kBAAkB;AAAA,MAC9C,MAAM,eAAe,KAAK,sBAAsB,KAAK,YAAY;AAAA,MACjE,MAAM,cACJ,KAAK,aAAa,KAAK,KAAK,KAAK,sBAAsB;AAAA,MAEzD,MAAM,QAAQ,KAAK,mBAAmB,QAAQ,WAAW,KAAK,KAAK,KAAK,GAAG;AAAA,QACzE;AAAA,QACA;AAAA,MACF,CAAC;AAAA,MAID,OAAO,eAAe,OAAO,kBAAkB;AAAA,QAC7C,OAAO;AAAA,QACP,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,cAAc;AAAA,MAChB,CAAC;AAAA,MAED,KAAK,SAAS,QAAQ,WAAW,KAAK;AAAA,MACtC,KAAK,YAAY,QAAQ,SAAS;AAAA,MAElC,QAAQ,SAAS;AAAA,QACf,OAAO;AAAA,QACP,UAAU;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MAED,MAAM,KAAK,QAAQ,QAAQ,WAAW,KAAK;AAAA,MAE3C,OAAO,sBAAsB,4BAA4B,yEAAyE;AAAA;AAAA,EAEtI,CAAC;AAAA;;;AC3HH,iBAAS;AAiBF,SAAS,gBAAgB,CAAC,MAAsB;AAAA,EACrD,OAAO,MAAK;AAAA,IACV,aAAa;AAAA,IACb,MAAM,CAAC;AAAA,SACD,QAAO,CAAC,OAAO,SAAS;AAAA,MAC5B,MAAM,QAAQ,KAAK,SAAS,QAAQ,SAAS;AAAA,MAE7C,IAAI,CAAC,OAAO;AAAA,QACV,OAAO,KAAK,eAAe,KAAK;AAAA,MAClC;AAAA,MAEA,MAAM,UAAU,KAAK,WAAW,QAAQ,SAAS;AAAA,MACjD,MAAM,aACJ,QAAQ,SAAS,IACb;AAAA;AAAA,IAAuB,QAAQ,KAAK;AAAA,GAAM,MAC1C;AAAA,MAEN,OAAO,GAAG,KAAK,eAAe,KAAK,IAAI;AAAA;AAAA,EAE3C,CAAC;AAAA;;;ACpCH,iBAAS;AAgBF,SAAS,cAAc,CAAC,MAAoB;AAAA,EACjD,OAAO,MAAK;AAAA,IACV,aAAa;AAAA,IACb,MAAM;AAAA,MACJ,QAAQ,MAAK,OACV,OAAO,EACP,SAAS,EACT,SAAS,gDAAgD;AAAA,IAC9D;AAAA,SACM,QAAO,CAAC,MAAM,SAAS;AAAA,MAC3B,MAAM,QAAQ,KAAK,SAAS,QAAQ,SAAS;AAAA,MAE7C,IAAI,CAAC,SAAS,MAAM,SAAS,WAAW;AAAA,QACtC,OAAO;AAAA,MACT;AAAA,MAEA,KAAK,OAAO,QAAQ,WAAW,KAAK,MAAM;AAAA,MAE1C,OAAO,KAAK,SACR,sBAAsB,KAAK,WAC3B;AAAA;AAAA,EAER,CAAC;AAAA;;;ACtCH,iBAAS;AAEF,SAAS,cAAc,GAAG;AAAA,EAC/B,OAAO,MAAK;AAAA,IACV,aAAa;AAAA,IACb,MAAM,CAAC;AAAA,IACP,SAAS,YAAY;AAAA,MACnB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBX,KAAK;AAAA;AAAA,EAEL,CAAC;AAAA;;;ACFH,IAAM,4BAA2B;AACjC,IAAM,sBAAsB;AAMrB,IAAM,kBAA0B;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,MACI;AAAA,EAEJ,MAAM,iBAAiB,IAAI;AAAA,EAC3B,MAAM,oBAAoB,IAAI;AAAA,EAC9B,MAAM,yBAAyB,IAAI;AAAA,EACnC,MAAM,mBAAmB,IAAI;AAAA,EAC7B,MAAM,0BAA0B,IAAI;AAAA,EACpC,MAAM,eAAe,IAAI;AAAA,EAGzB,MAAM,WAAW,CAAC,cAChB,eAAe,IAAI,SAAS;AAAA,EAE9B,MAAM,WAAW,CAAC,WAAmB,UAA+B;AAAA,IAClE,eAAe,IAAI,WAAW,KAAK;AAAA;AAAA,EAGrC,MAAM,cAAc,CAAC,cAA4B;AAAA,IAC/C,eAAe,OAAO,SAAS;AAAA,IAC/B,kBAAkB,OAAO,SAAS;AAAA,IAClC,uBAAuB,OAAO,SAAS;AAAA,IACvC,iBAAiB,OAAO,SAAS;AAAA,IACjC,wBAAwB,OAAO,SAAS;AAAA;AAAA,EAG1C,MAAM,cAAc,CAAC,cACnB,kBAAkB,IAAI,SAAS;AAAA,EAEjC,MAAM,cAAc,CAAC,cAA4B;AAAA,IAC/C,kBAAkB,IAAI,WAAW,sBAAsB,CAAC;AAAA,IACxD,uBAAuB,IAAI,WAAW,CAAC;AAAA,IACvC,iBAAiB,IAAI,WAAW,CAAC,CAAC;AAAA;AAAA,EAGpC,MAAM,gBAAgB,CAAC,WAAmB,YAA0B;AAAA,IAClE,IAAI,UAAU,iBAAiB,IAAI,SAAS;AAAA,IAC5C,IAAI,CAAC,SAAS;AAAA,MACZ,UAAU,CAAC;AAAA,MACX,iBAAiB,IAAI,WAAW,OAAO;AAAA,IACzC;AAAA,IACA,QAAQ,KAAK,OAAO;AAAA,IACpB,IAAI,QAAQ,SAAS,qBAAqB;AAAA,MACxC,iBAAiB,IAAI,WAAW,QAAQ,MAAM,CAAC,mBAAmB,CAAC;AAAA,IACrE;AAAA;AAAA,EAIF,MAAM,YAAY,OAAO,SAIJ;AAAA,IACnB,IAAI;AAAA,MACF,MAAM,OAAO,IAAI,UAAU;AAAA,QACzB;AAAA,QACA,WAAW;AAAA,QACX,OAAO,KAAK;AAAA,QACZ,SAAS,KAAK;AAAA,QACd,SAAS,KAAK;AAAA,QACd,UAAU;AAAA,MACZ,CAAC;AAAA,MACD,MAAM;AAAA;AAAA,EAMV,MAAM,aAAa,OACjB,WACA,QACA,QACA,UAAoD,WAClC;AAAA,IAClB,MAAM,QAAQ,SAAS,SAAS;AAAA,IAChC,IAAI,CAAC;AAAA,MAAO;AAAA,IAEZ,MAAM,OAAO;AAAA,IACb,MAAM,QAAQ;AAAA,IACd,MAAM,cAAc;AAAA,IACpB,cAAc,WAAW,SAAS,GAAG,WAAW,WAAW,MAAM;AAAA,IAEjE,MAAM,UAAU;AAAA,MACd,OAAO;AAAA,MACP,SAAS,SAAS,GAAG,WAAW,WAAW;AAAA,MAC3C;AAAA,IACF,CAAC;AAAA;AAAA,EAIH,MAAM,iBAAiB,OACrB,WACA,OACA,eACkB;AAAA,IAClB,MAAM,WAAW,YAAY,SAAS;AAAA,IACtC,IAAI,CAAC;AAAA,MAAU;AAAA,IAEf,SAAS,sBAAsB;AAAA,IAC/B,SAAS,yBAAyB;AAAA,IAElC,IAAI;AAAA,MAGF,MAAO,OAAO,QAAQ,YAAoE;AAAA,QACxF;AAAA,QACA,WAAW;AAAA,QACX;AAAA,QACA,OAAO,MAAM;AAAA,QACb,OAAO,CAAC,EAAE,MAAM,QAAiB,MAAM,WAAW,CAAC;AAAA,MACrD,CAAC;AAAA,MACD,MAAM;AAAA;AAAA,EAMV,MAAM,gBAAgB,OAAO,cAAqC;AAAA,IAChE,MAAM,QAAQ,SAAS,SAAS;AAAA,IAChC,MAAM,WAAW,YAAY,SAAS;AAAA,IACtC,IAAI,CAAC,SAAS,MAAM,SAAS,aAAa,CAAC;AAAA,MAAU;AAAA,IAGrD,IAAI,MAAM,UAAU,aAAa,MAAM,uBAAuB,KAAK,CAAC,SAAS,wBAAwB;AAAA,MACnG,cAAc,WAAW,sBAAsB,MAAM,cAAc;AAAA,MACnE,MAAM,UAAU;AAAA,QACd,OAAO;AAAA,QACP,SAAS,sBAAsB,MAAM;AAAA,QACrC,SAAS;AAAA,MACX,CAAC;AAAA,MACD,MAAM,eAAe,WAAW,OAAO,MAAM,IAAI;AAAA,MACjD;AAAA,IACF;AAAA,IAGA,IAAI,SAAS,qBAAqB;AAAA,MAChC,SAAS,sBAAsB;AAAA,MAC/B,MAAM,WACJ,WACA,0BACA,SAAS,0BACP,iDACF,SACF;AAAA,MACA;AAAA,IACF;AAAA,IAGA,MAAM,YAAY,SAAS;AAAA,IAC3B,IAAI,CAAC;AAAA,MAAW;AAAA,IAEhB,SAAS,sBAAsB;AAAA,IAC/B,SAAS,yBAAyB;AAAA,IAElC,MAAM,gBAAgB,aAAa,eAAe,WAAW,SAAS;AAAA,IACtE,MAAM,YAAY,wBAAwB,aAAa;AAAA,IAEvD,IAAI,UAAU,WAAW,YAAY;AAAA,MACnC,MAAM,WACJ,WACA,kBACA,UAAU,QACV,SACF;AAAA,MACA;AAAA,IACF;AAAA,IAEA,IAAI,UAAU,WAAW,WAAW;AAAA,MAClC,MAAM,WACJ,WACA,gBACA,UAAU,QACV,SACF;AAAA,MACA;AAAA,IACF;AAAA,IAGA,IAAI,MAAM,sBAAsB,MAAM,eAAe;AAAA,MACnD,MAAM,WACJ,WACA,8BACA,iBAAiB,MAAM,gDACvB,SACF;AAAA,MACA;AAAA,IACF;AAAA,IAGA,MAAM,sBAAsB;AAAA,IAC5B,MAAM,YAAY,oBAChB,SAAS,SACX;AAAA,IACA,MAAM,SAAS,YAAY,KAAK,eAAe;AAAA,IAC/C,cACE,WACA,gBAAgB,MAAM,sBAAsB,MAAM,gBAAgB,QACpE;AAAA,IAEA,MAAM,UAAU;AAAA,MACd,OAAO;AAAA,MACP,SAAS,gBAAgB,MAAM,sBAAsB,MAAM,gBAAgB;AAAA,MAC3E,SAAS;AAAA,IACX,CAAC;AAAA,IAED,MAAM,eACJ,WACA,OACA,wBAAwB;AAAA,MACtB,eAAe,MAAM;AAAA,MACrB,cAAc,MAAM;AAAA,MACpB,MAAM,MAAM;AAAA,IACd,CAAC,CACH;AAAA;AAAA,EAIF,MAAM,eAAe,mBAAmB;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf,gBAAgB,OAAO,WAAW,UAAU;AAAA,MAC1C,MAAM,eACJ,OAAO,MAAM,WAAW;AAAA,MAC1B,MAAM,UAAU,OAAO,SAAS;AAAA,MAChC,MAAM,SAAS,UAAU,gBAAgB;AAAA,MACzC,MAAM,UAA+B,UAAU,YAAY;AAAA,MAC3D,MAAM,WAAW,WAAW,QAAQ,cAAc,OAAO;AAAA;AAAA,EAE7D,CAAC;AAAA,EAED,MAAM,iBAAiB,qBAAqB;AAAA,IAC1C;AAAA,IACA,mBAAmB,CAAC,cAAc,wBAAwB,IAAI,SAAS;AAAA,IACvE,oBAAoB,CAAC,WAAW,eAAe;AAAA,MAC7C,MAAM,WAAW,YAAY,SAAS;AAAA,MACtC,IAAI,CAAC;AAAA,QAAU;AAAA,MACf,SAAS,sBAAsB;AAAA,MAC/B,MAAM,aAAa,MAAM,QAAQ,WAAW,OAAO,IAC/C,WAAW,QAAQ,KAAK,IAAI,IAC5B,WAAW,WAAW;AAAA,MAC1B,SAAS,yBACP,UAAU,WAAW,QAAQ,aAAa,KAAK;AAAA,MACjD,cACE,WACA,SAAS,sBACX;AAAA;AAAA,EAEJ,CAAC;AAAA,EAED,MAAM,sBAAsB,0BAA0B;AAAA,IACpD;AAAA,IACA,kBAAkB,CAAC,cACjB,uBAAuB,IAAI,SAAS,KAAK;AAAA,IAC3C,wBAAwB,CAAC,cAAc;AAAA,MACrC,MAAM,UAAU,uBAAuB,IAAI,SAAS,KAAK;AAAA,MACzD,IAAI,UAAU,GAAG;AAAA,QACf,uBAAuB,IAAI,WAAW,UAAU,CAAC;AAAA,MACnD;AAAA;AAAA,IAEF,mBAAmB;AAAA,EACrB,CAAC;AAAA,EAED,MAAM,kBAAkB,sBAAsB;AAAA,IAC5C;AAAA,IACA,wBAAwB,CAAC,cAAc;AAAA,MACrC,MAAM,UAAU,uBAAuB,IAAI,SAAS,KAAK;AAAA,MACzD,uBAAuB,IAAI,WAAW,UAAU,CAAC;AAAA;AAAA,EAErD,CAAC;AAAA,EAED,MAAM,gBAAgB,oBAAoB;AAAA,IACxC,aAAa;AAAA,EACf,CAAC;AAAA,EAGD,MAAM,YAAY,gBAAgB;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB;AAAA,IACpB,SAAS,OAAO,WAAW,UAAU;AAAA,MAEnC,MAAM,KAAM,MAA6C;AAAA,MACzD,IAAI,OAAO,eAAe,OAAO,WAAW;AAAA,QAC1C,wBAAwB,IAAI,WAAW,EAAE;AAAA,MAC3C,EAAO;AAAA,QACL,wBAAwB,IAAI,WAAW,SAAS;AAAA;AAAA,MAElD,cACE,WACA,YAAY,wBAAwB,IAAI,SAAS,eAAe,MAAM,eACxE;AAAA,MACA,cACE,WACA,uBAAuB,MAAM,gBAC/B;AAAA;AAAA,EAEJ,CAAC;AAAA,EAED,MAAM,aAAa,iBAAiB;AAAA,IAClC;AAAA,IACA,gBAAgB;AAAA,IAChB,YAAY,CAAC,cAAc,iBAAiB,IAAI,SAAS,KAAK,CAAC;AAAA,EACjE,CAAC;AAAA,EAED,MAAM,WAAW,eAAe;AAAA,IAC9B;AAAA,IACA,QAAQ,CAAC,WAAW,WAAW;AAAA,MAC7B,MAAM,QAAQ,SAAS,SAAS;AAAA,MAChC,IAAI,CAAC;AAAA,QAAO;AAAA,MACZ,MAAM,OAAO;AAAA,MACb,MAAM,QAAQ;AAAA,MACd,MAAM,cAAc;AAAA,MACpB,cACE,WACA,SAAS,sBAAsB,WAAW,mBAC5C;AAAA;AAAA,EAEJ,CAAC;AAAA,EAED,MAAM,WAAW,eAAe;AAAA,EAGhC,OAAO;AAAA,IACL,MAAM;AAAA,MACJ,iBAAiB;AAAA,MACjB,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,IAClB;AAAA,IAEA,OAAO;AAAA,IAEP,kBAAkB;AAAA,IAElB,sCAAsC;AAAA,IAEtC,gBAAgB;AAAA,IAEhB,sBAAsB;AAAA,EACxB;AAAA;",
|
|
24
|
+
"debugId": "7838B318ED86DB9164756E2164756E21",
|
|
25
|
+
"names": []
|
|
26
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@withakay/opencode-autopilot",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Autopilot mode plugin for OpenCode - autonomous multi-step task execution with safety guarantees",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "bun build ./index.ts --outdir ./dist --target node --sourcemap --external @opencode-ai/plugin --external @opencode-ai/sdk",
|
|
21
|
+
"typecheck": "bun build --no-bundle ./index.ts",
|
|
22
|
+
"test": "bun test ./__tests__/",
|
|
23
|
+
"prepublishOnly": "bun run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"opencode",
|
|
27
|
+
"autopilot",
|
|
28
|
+
"agent",
|
|
29
|
+
"automation",
|
|
30
|
+
"ai",
|
|
31
|
+
"coding"
|
|
32
|
+
],
|
|
33
|
+
"author": "Jack Waring",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/withakay/opencode-autopilot.git"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/withakay/opencode-autopilot/issues"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/withakay/opencode-autopilot#readme",
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@opencode-ai/plugin": "^1.3.0",
|
|
45
|
+
"@opencode-ai/sdk": "^1.3.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@opencode-ai/plugin": "^1.3.0",
|
|
49
|
+
"@opencode-ai/sdk": "^1.3.0",
|
|
50
|
+
"bun-types": "^1.3.0",
|
|
51
|
+
"typescript": "^5.0.0"
|
|
52
|
+
}
|
|
53
|
+
}
|