opencode-superagent 0.0.0-stage → 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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +333 -2
  3. package/agent/superagent-router.md +7 -0
  4. package/agent/superagent-worker.md +10 -0
  5. package/dist/server.d.ts +3 -0
  6. package/dist/server.js +115 -0
  7. package/dist/src/abort.d.ts +31 -0
  8. package/dist/src/abort.js +176 -0
  9. package/dist/src/agents.d.ts +51 -0
  10. package/dist/src/agents.js +83 -0
  11. package/dist/src/context.d.ts +20 -0
  12. package/dist/src/context.js +98 -0
  13. package/dist/src/dirs.d.ts +45 -0
  14. package/dist/src/dirs.js +129 -0
  15. package/dist/src/dispatch.d.ts +38 -0
  16. package/dist/src/dispatch.js +214 -0
  17. package/dist/src/format.d.ts +3 -0
  18. package/dist/src/format.js +14 -0
  19. package/dist/src/guard.d.ts +4 -0
  20. package/dist/src/guard.js +9 -0
  21. package/dist/src/prompts.d.ts +1 -0
  22. package/dist/src/prompts.js +36 -0
  23. package/dist/src/protocol.d.ts +210 -0
  24. package/dist/src/protocol.js +88 -0
  25. package/dist/src/recall.d.ts +58 -0
  26. package/dist/src/recall.js +309 -0
  27. package/dist/src/report.d.ts +72 -0
  28. package/dist/src/report.js +147 -0
  29. package/dist/src/router.d.ts +35 -0
  30. package/dist/src/router.js +209 -0
  31. package/dist/src/session-meta.d.ts +42 -0
  32. package/dist/src/session-meta.js +91 -0
  33. package/dist/src/state.d.ts +124 -0
  34. package/dist/src/state.js +431 -0
  35. package/dist/src/ticket-tools.d.ts +45 -0
  36. package/dist/src/ticket-tools.js +148 -0
  37. package/dist/src/ticket-view.d.ts +62 -0
  38. package/dist/src/ticket-view.js +135 -0
  39. package/dist/src/ticket.d.ts +246 -0
  40. package/dist/src/ticket.js +700 -0
  41. package/dist/src/tools.d.ts +85 -0
  42. package/dist/src/tools.js +376 -0
  43. package/dist/src/watchdog.d.ts +16 -0
  44. package/dist/src/watchdog.js +77 -0
  45. package/dist/src/watcher.d.ts +11 -0
  46. package/dist/src/watcher.js +181 -0
  47. package/dist/tui.d.ts +3 -0
  48. package/dist/tui.js +1027 -0
  49. package/package.json +79 -4
package/dist/tui.js ADDED
@@ -0,0 +1,1027 @@
1
+ // @bun
2
+ // tui.tsx
3
+ import { effect as _$effect } from "@opentui/solid";
4
+ import { insert as _$insert } from "@opentui/solid";
5
+ import { createTextNode as _$createTextNode } from "@opentui/solid";
6
+ import { insertNode as _$insertNode } from "@opentui/solid";
7
+ import { setProp as _$setProp } from "@opentui/solid";
8
+ import { createElement as _$createElement } from "@opentui/solid";
9
+ import { memo as _$memo } from "@opentui/solid";
10
+ import { createComponent as _$createComponent } from "@opentui/solid";
11
+ import { Plugin } from "@opencode/plugin/tui";
12
+ import { createMemo, createSignal, For, onCleanup, onMount, Show } from "solid-js";
13
+
14
+ // src/protocol.ts
15
+ import { Rpc } from "@opencode/plugin/effect";
16
+ import { Schema } from "effect";
17
+ var Definition = Rpc.define({
18
+ id: "opencode.superagent",
19
+ methods: {
20
+ router: {
21
+ input: Schema.toStandardSchemaV1(Schema.Struct({})),
22
+ output: Schema.toStandardSchemaV1(Schema.Struct({ sessionID: Schema.String }))
23
+ },
24
+ status: {
25
+ input: Schema.toStandardSchemaV1(Schema.Struct({})),
26
+ output: Schema.toStandardSchemaV1(Schema.Struct({
27
+ router: Schema.optionalKey(Schema.String),
28
+ pending: Schema.Number,
29
+ workers: Schema.Array(Schema.Struct({
30
+ sessionID: Schema.String,
31
+ title: Schema.optionalKey(Schema.String),
32
+ threads: Schema.Array(Schema.String),
33
+ state: Schema.Literals(["running", "idle", "failed", "done", "aborted"])
34
+ }))
35
+ }))
36
+ },
37
+ tickets: {
38
+ input: Schema.toStandardSchemaV1(Schema.Struct({
39
+ threadID: Schema.optionalKey(Schema.String),
40
+ status: Schema.optionalKey(Schema.String),
41
+ limit: Schema.optionalKey(Schema.Number)
42
+ })),
43
+ output: Schema.toStandardSchemaV1(Schema.Struct({ text: Schema.String }))
44
+ },
45
+ ticket: {
46
+ input: Schema.toStandardSchemaV1(Schema.Struct({ ticketID: Schema.String })),
47
+ output: Schema.toStandardSchemaV1(Schema.Struct({ text: Schema.String }))
48
+ },
49
+ dispatch: {
50
+ input: Schema.toStandardSchemaV1(Schema.Struct({
51
+ task: Schema.String,
52
+ title: Schema.optionalKey(Schema.String),
53
+ threadID: Schema.optionalKey(Schema.String),
54
+ agent: Schema.optionalKey(Schema.String),
55
+ directory: Schema.optionalKey(Schema.String),
56
+ ticketID: Schema.String
57
+ })),
58
+ output: Schema.toStandardSchemaV1(Schema.Struct({ sessionID: Schema.String })),
59
+ errors: { "dispatch.rejected": Schema.toStandardSchemaV1(Schema.Struct({})) }
60
+ },
61
+ trigger: {
62
+ input: Schema.toStandardSchemaV1(Schema.Struct({
63
+ id: Schema.optionalKey(Schema.String),
64
+ source: Schema.String,
65
+ threadID: Schema.String,
66
+ author: Schema.optionalKey(Schema.String),
67
+ text: Schema.String,
68
+ title: Schema.optionalKey(Schema.String),
69
+ createdAt: Schema.optionalKey(Schema.Number)
70
+ })),
71
+ output: Schema.toStandardSchemaV1(Schema.Struct({
72
+ route: Schema.Literals(["router", "worker", "dropped"]),
73
+ sessionID: Schema.optionalKey(Schema.String)
74
+ }))
75
+ },
76
+ stop: {
77
+ input: Schema.toStandardSchemaV1(Schema.Struct({ sessionID: Schema.String })),
78
+ output: Schema.toStandardSchemaV1(Schema.Struct({ interrupted: Schema.Boolean }))
79
+ },
80
+ abort: {
81
+ input: Schema.toStandardSchemaV1(Schema.Struct({
82
+ sessionID: Schema.optionalKey(Schema.String),
83
+ reason: Schema.optionalKey(Schema.String),
84
+ all: Schema.optionalKey(Schema.Boolean),
85
+ stale: Schema.optionalKey(Schema.Boolean)
86
+ })),
87
+ output: Schema.toStandardSchemaV1(Schema.Struct({ text: Schema.String })),
88
+ errors: { "abort.rejected": Schema.toStandardSchemaV1(Schema.Struct({})) }
89
+ }
90
+ },
91
+ events: {}
92
+ });
93
+
94
+ // src/tools.ts
95
+ var GROUP_LABELS = {
96
+ dispatch: "\u8C03\u5EA6\u7C7B",
97
+ query: "\u67E5\u8BE2\u7C7B",
98
+ control: "\u63A7\u5236\u7C7B"
99
+ };
100
+ var RISK_LABELS = {
101
+ read: "\u53EA\u8BFB",
102
+ write: "\u53EF\u5199",
103
+ terminate: "\u7EC8\u6B62"
104
+ };
105
+ var ROUTER_TOOL_SPECS = [
106
+ {
107
+ name: "dispatch",
108
+ group: "dispatch",
109
+ description: "Dispatch one independent task to a background worker session in the given directory (absolute path). ticketID is required and must be an open/in_progress ticket for this thread with no running worker: create it with ticket_create first. Only the router may call it.",
110
+ summary: "\u628A\u72EC\u7ACB\u4EFB\u52A1\u6D3E\u7ED9\u540E\u53F0 Worker \u4F1A\u8BDD\uFF0C\u4E0D\u963B\u585E Router\u3002",
111
+ params: [
112
+ { name: "task", required: true, type: "string", note: "\u4EFB\u52A1\u5185\u5BB9" },
113
+ { name: "title", required: false, type: "string", note: "\u4F1A\u8BDD\u6807\u9898" },
114
+ { name: "threadID", required: false, type: "string", note: "thread \u7ED1\u5B9A" },
115
+ { name: "agent", required: false, type: "string", note: "\u8986\u76D6\u9ED8\u8BA4 agent" },
116
+ { name: "directory", required: false, type: "string", note: "\u7EDD\u5BF9\u8DEF\u5F84" },
117
+ { name: "ticketID", required: true, type: "string", note: "\u5FC5\u586B\uFF0C\u5148 ticket_create" }
118
+ ],
119
+ risk: "write",
120
+ confirmation: "\u81EA\u52A8\u6267\u884C\uFF0C\u4E0D\u9010\u6B21\u786E\u8BA4\uFF1A\u5148\u6821\u9A8C\u5DE5\u5355\uFF0C\u518D\u521B\u5EFA Worker \u4F1A\u8BDD\u5E76\u4EE5\u961F\u5217 prompt \u6295\u9012\u4EFB\u52A1\u3002",
121
+ usage: [
122
+ "\u5F3A\u5236\uFF1A\u5FC5\u987B\u5148 ticket_create \u5EFA\u5355\uFF0C\u518D dispatch({ ticketID })\uFF1B\u7F3A ticketID \u76F4\u63A5\u62A5\u9519\u3002",
123
+ "\u5DE5\u5355\u987B\u4E3A open/in_progress\u3001\u5C5E\u4E8E\u540C\u4E00 thread\u3001\u4E14\u6CA1\u6709\u8FD0\u884C\u4E2D\u7684 Worker\uFF1B\u88AB\u62D2\u65F6\u6309\u62A5\u9519\u8865\u6551\uFF08\u8865\u5EFA\u5355 / \u7B49\u539F Worker \u7ED3\u675F\u6216 abort_session \u540E\u5EFA\u65B0\u5355\uFF09\u518D\u6D3E\u3002",
124
+ "\u6BCF\u6B21\u6D3E\u53D1\u90FD\u8981\u5E26 directory\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09\uFF1BRouter \u4F1A\u590D\u7528 thread \u7ED1\u5B9A\u4E0E\u76EE\u5F55\u522B\u540D\u3002",
125
+ "\u540C\u4E00 thread \u7684\u540E\u7EED\u6D88\u606F\u76F4\u6295\u8BE5 Worker\uFF0C\u4E0D\u8981\u91CD\u590D dispatch\u3002",
126
+ "\u6D3E\u53D1\u6210\u529F\u5373\u7ED1\u5B9A\u5DE5\u5355\uFF1AworkerSessionID \u5199\u5165\u3001attempts+1\u3001state \u2192 in_progress\uFF1BWorker \u7ED3\u675F\u81EA\u52A8\u7ED3\u5355\u3002"
127
+ ]
128
+ },
129
+ {
130
+ name: "ticket_create",
131
+ group: "dispatch",
132
+ description: "Create a work ticket before dispatching: title, objective, acceptance criteria (1-10) and an absolute directory are required. Returns the ticket id and reuses an open ticket with the same dedupe key in the same thread. Only the router may call it.",
133
+ summary: "\u5EFA\u5DE5\u5355\uFF1A\u5199\u6E05\u76EE\u6807\u3001\u9A8C\u6536\u6807\u51C6\u4E0E\u76EE\u5F55\uFF0C\u91CD\u590D\u4EFB\u52A1\u81EA\u52A8\u590D\u7528\u3002",
134
+ params: [
135
+ { name: "threadID", required: true, type: "string", note: "thread \u6807\u8BC6" },
136
+ { name: "title", required: true, type: "string", note: `\u226480 \u5B57` },
137
+ { name: "objective", required: true, type: "string", note: "\u8981\u505A\u4EC0\u4E48" },
138
+ { name: "acceptance", required: true, type: "string[]", note: "1-10 \u6761\u9A8C\u6536\u6807\u51C6" },
139
+ { name: "directory", required: true, type: "string", note: "\u7EDD\u5BF9\u8DEF\u5F84" },
140
+ { name: "priority", required: false, type: "string", values: ["p0", "p1", "p2"], note: "\u9ED8\u8BA4 p1" },
141
+ { name: "parentTicketID", required: false, type: "string", note: "\u62C6\u5355\u65F6\u6307\u5411\u7236\u5355" },
142
+ { name: "dedupeKey", required: false, type: "string", note: "\u9ED8\u8BA4\u6309\u6765\u6E90+thread+\u76EE\u6807\u751F\u6210" }
143
+ ],
144
+ risk: "write",
145
+ permission: "status",
146
+ confirmation: "\u81EA\u52A8\u6267\u884C\uFF1A\u4EC5\u5EFA\u5355\uFF0C\u4E0D\u6D3E\u6D3B\uFF1Bauthorization \u81EA\u52A8\u53D6\u5F53\u524D\u89E6\u53D1\u5668\u7684 id \u4E0E\u539F\u8BDD\u6458\u8981\u3002",
147
+ usage: [
148
+ "acceptance \u4E3A\u7A7A\u76F4\u63A5\u62D2\u7EDD\u5EFA\u5355\uFF1A\u60F3\u4E0D\u51FA\u9A8C\u6536\u6807\u51C6\u5C31\u5148\u95EE\u7528\u6237\u3002",
149
+ "\u540C thread \u5DF2\u6709 open/in_progress \u540C dedupeKey \u7684\u5DE5\u5355\u4F1A\u76F4\u63A5\u590D\u7528\uFF08reused=true\uFF09\u3002"
150
+ ]
151
+ },
152
+ {
153
+ name: "ticket_list",
154
+ group: "query",
155
+ description: "List tickets, optionally filtered by thread, state, update time and limit. Returns id/state/title/priority/worker per ticket. Only the router may call it.",
156
+ summary: "\u6309 thread/\u72B6\u6001/\u65F6\u95F4\u5217\u51FA\u5DE5\u5355\uFF08\u9ED8\u8BA4 20 \u6761\uFF09\u3002",
157
+ params: [
158
+ { name: "threadID", required: false, type: "string", note: "\u53EA\u770B\u8BE5 thread" },
159
+ {
160
+ name: "status",
161
+ required: false,
162
+ type: "string",
163
+ values: ["open", "in_progress", "blocked", "done", "failed", "cancelled", "superseded"],
164
+ note: "\u72B6\u6001\u8FC7\u6EE4"
165
+ },
166
+ { name: "since", required: false, type: "number", note: "updatedAt \u8D77\u59CB\u6BEB\u79D2" },
167
+ { name: "limit", required: false, type: "number", note: "\u9ED8\u8BA4 20\uFF0C\u4E0A\u9650 100" }
168
+ ],
169
+ risk: "read",
170
+ permission: "status",
171
+ confirmation: "\u53EA\u8BFB\uFF0C\u81EA\u52A8\u6267\u884C\uFF08permission: status\uFF09\u3002",
172
+ usage: ["\u6309 updatedAt \u5012\u5E8F\uFF1B\u9700\u8981\u67D0\u5F20\u5355\u7684\u5B8C\u6574\u9A8C\u6536\u6807\u51C6\u7528 /ticket <id>\u3002"]
173
+ },
174
+ {
175
+ name: "ticket_update",
176
+ group: "control",
177
+ description: "Move a ticket along its state machine (open \u2192 in_progress \u2192 blocked/done/failed/cancelled/superseded) and optionally append a note. Illegal transitions are rejected. Only the router may call it.",
178
+ summary: "\u63A8\u8FDB\u5DE5\u5355\u72B6\u6001\u6216\u8FFD\u52A0\u5907\u6CE8\uFF0C\u975E\u6CD5\u8FC1\u79FB\u76F4\u63A5\u62A5\u9519\u3002",
179
+ params: [
180
+ { name: "ticketID", required: true, type: "string", note: "\u5DE5\u5355 ID" },
181
+ {
182
+ name: "status",
183
+ required: false,
184
+ type: "string",
185
+ values: ["open", "in_progress", "blocked", "done", "failed", "cancelled", "superseded"],
186
+ note: "\u76EE\u6807\u72B6\u6001"
187
+ },
188
+ { name: "note", required: false, type: "string", note: "\u5907\u6CE8\uFF0C\u5199\u5165 history" }
189
+ ],
190
+ risk: "write",
191
+ permission: "status",
192
+ confirmation: "\u81EA\u52A8\u6267\u884C\uFF1A\u72B6\u6001\u8FC1\u79FB\u6309\u72B6\u6001\u673A\u6821\u9A8C\u3002",
193
+ usage: ["\u7ED3\u675F\u4EFB\u52A1\u4F18\u5148\u7528 ticket_close\uFF08\u5E26 outcome \u4E0E summary\uFF09\uFF0C\u4E0D\u8981\u624B\u52A8\u8DF3\u5230 done\u3002"]
194
+ },
195
+ {
196
+ name: "ticket_close",
197
+ group: "control",
198
+ description: "Close a ticket with an outcome (done, failed or cancelled) and a summary; records closedAt, closeReason and a history entry. Only the router may call it.",
199
+ summary: "\u7ED3\u5355\uFF1A\u5199\u5165\u7ED3\u8BBA\u3001\u5173\u95ED\u65F6\u95F4\u4E0E\u5386\u53F2\u3002",
200
+ params: [
201
+ { name: "ticketID", required: true, type: "string", note: "\u5DE5\u5355 ID" },
202
+ { name: "outcome", required: true, type: "string", values: ["done", "failed", "cancelled"], note: "\u7ED3\u679C" },
203
+ { name: "summary", required: true, type: "string", note: "\u7ED3\u8BBA\uFF0C\u2264500 \u5B57" }
204
+ ],
205
+ risk: "write",
206
+ permission: "status",
207
+ confirmation: "\u81EA\u52A8\u6267\u884C\uFF1A\u5DF2\u7ED3\u5355\u7684\u5DE5\u5355\u4FDD\u6301\u539F\u72B6\u6001\uFF0C\u6539\u7ED3\u8BBA\u9700\u65B0\u5EFA\u5DE5\u5355\u3002",
208
+ usage: ["Worker \u7ED3\u675F\u4F1A\u81EA\u52A8\u7ED3\u5355\uFF1B\u53EA\u6709\u5728 Router \u4E3B\u52A8\u653E\u5F03/\u6539\u5224\u65F6\u624D\u624B\u52A8\u8C03\u7528\u3002"]
209
+ },
210
+ {
211
+ name: "status",
212
+ group: "query",
213
+ description: "Show a compact summary of the router, pending triggers and worker sessions.",
214
+ summary: "\u67E5\u770B Router\u3001pending \u89E6\u53D1\u5668\u6570\u91CF\u4E0E\u5168\u90E8 Worker \u7684 thread/\u72B6\u6001/\u6700\u8FD1\u62A5\u544A\u3002",
215
+ params: [],
216
+ risk: "read",
217
+ confirmation: "\u53EA\u8BFB\uFF0C\u81EA\u52A8\u6267\u884C\uFF08permission: status\uFF09\u3002",
218
+ usage: ["system \u72B6\u6001\u884C\u53EA\u7ED9\u8BA1\u6570\uFF0C\u7EC6\u8282\u7528 status / thread_detail\u3002"]
219
+ },
220
+ {
221
+ name: "thread_detail",
222
+ group: "query",
223
+ description: "Show a thread's trigger history plus its subscribed workers. Only the router may call it.",
224
+ summary: "\u67E5\u770B\u67D0\u4E2A thread \u7684\u89E6\u53D1\u5668\u5386\u53F2\uFF08\u8C01\u5728\u4F55\u65F6\u8BF4\u4E86\u4EC0\u4E48\uFF09\u4E0E\u8BA2\u9605\u5B83\u7684 Worker\u3002",
225
+ params: [
226
+ { name: "threadID", required: true, type: "string", note: "thread \u6807\u8BC6" },
227
+ { name: "limit", required: false, type: "number", note: "\u9ED8\u8BA4 20 \u6761\uFF0C\u4E0A\u9650 200" },
228
+ { name: "offset", required: false, type: "number", note: "\u4ECE\u6700\u65B0\u5F80\u524D\u8DF3\u8FC7 N \u6761\uFF0C\u9ED8\u8BA4 0" }
229
+ ],
230
+ risk: "read",
231
+ permission: "status",
232
+ confirmation: "\u53EA\u8BFB\uFF0C\u81EA\u52A8\u6267\u884C\uFF08permission: status\uFF09\u3002",
233
+ usage: [
234
+ "\u8BE6\u7EC6\u4FE1\u606F\u4E0D\u5728 system \u91CC\uFF1A\u9700\u8981\u539F\u6587\u65F6\u7528\u5B83\uFF0C\u4E0D\u8981\u731C\u3002",
235
+ "\u5185\u5B58\u53EA\u7559\u6700\u8FD1 200 \u6761\uFF1Boffset/limit \u8D85\u51FA\u65F6\u6309\u9700\u4ECE KV \u56DE\u6E90\uFF0C\u6BCF\u6B21\u6700\u591A\u8865 20 \u6761\u3002"
236
+ ]
237
+ },
238
+ {
239
+ name: "worker_detail",
240
+ group: "query",
241
+ description: "Show a worker's state plus its recent assistant messages. Only the router may call it.",
242
+ summary: "\u67E5\u770B\u67D0\u4E2A Worker \u7684\u72B6\u6001\u4E0E\u6700\u8FD1\u51E0\u6761 assistant \u6587\u672C\u3002",
243
+ params: [
244
+ { name: "sessionID", required: true, type: "string", note: "Worker \u4F1A\u8BDD ID" },
245
+ { name: "messages", required: false, type: "number", note: "\u9ED8\u8BA4 5\uFF0C\u4E0A\u9650 10" }
246
+ ],
247
+ risk: "read",
248
+ permission: "status",
249
+ confirmation: "\u53EA\u8BFB\uFF0C\u81EA\u52A8\u6267\u884C\uFF08permission: status\uFF09\u3002",
250
+ usage: ["\u7528\u4E8E\u9A8C\u6536 Worker \u7684\u4E2D\u95F4\u7ED3\u8BBA\uFF0C\u4E0D\u5FC5\u7B49\u5B83\u7ED3\u675F\u3002", "\u5355\u6761\u6D88\u606F\u9ED8\u8BA4\u622A\u65AD 1500 \u5B57\uFF1B\u8981\u770B\u5B8C\u6574\u62A5\u544A\u7528 report_read\u3002"]
251
+ },
252
+ {
253
+ name: "report_read",
254
+ group: "query",
255
+ description: "Read the full report text a worker produced when it finished, by character range (offset, limit). Returns the slice plus the total length, truncation state and the next offset. Only the router may call it.",
256
+ summary: "\u5206\u9875\u8BFB\u53D6 Worker \u7ED3\u675F\u62A5\u544A\u7684\u5B8C\u6574\u5168\u6587\uFF08\u9ED8\u8BA4 4000 \u5B57/\u9875\uFF09\u3002",
257
+ params: [
258
+ { name: "sessionID", required: true, type: "string", note: "Worker \u4F1A\u8BDD ID" },
259
+ { name: "offset", required: false, type: "number", note: "\u8D77\u59CB\u5B57\u7B26\u504F\u79FB\uFF0C\u9ED8\u8BA4 0" },
260
+ { name: "limit", required: false, type: "number", note: "\u9ED8\u8BA4 4000\uFF0C\u4E0A\u9650 20000" }
261
+ ],
262
+ risk: "read",
263
+ permission: "status",
264
+ confirmation: "\u53EA\u8BFB\uFF0C\u81EA\u52A8\u6267\u884C\uFF08permission: status\uFF09\u3002",
265
+ usage: [
266
+ "\u53EA\u5B58 Worker \u7ED3\u675F\u65F6\u6355\u83B7\u7684\u5168\u6587\uFF1B\u6CA1\u6709\u5168\u6587\u8BB0\u5F55\u4F1A\u76F4\u63A5\u62A5\u9519\uFF0C\u6B64\u65F6\u7528 worker_detail\u3002",
267
+ "\u6309\u8FD4\u56DE\u7684 nextOffset \u7FFB\u9875\uFF0C\u5230\u672B\u5C3E\u65F6\u63D0\u793A\u300C\u5DF2\u5230\u672B\u5C3E\u300D\u3002"
268
+ ]
269
+ },
270
+ {
271
+ name: "abort_session",
272
+ group: "control",
273
+ description: "Terminate a tracked worker session: interrupt it, wait for it to settle, mark it aborted and detach it from its thread so later messages fall back to the router. Pass sessionID, or all/stale for batch cleanup. Only the router may call it.",
274
+ summary: "\u7EC8\u6B62/\u6E05\u7406 Worker\uFF1A\u4E2D\u65AD\u6267\u884C\u3001\u7B49\u5F85\u6536\u655B\u3001\u6807\u8BB0 aborted \u5E76\u4ECE thread \u6458\u9664\u3002",
275
+ params: [
276
+ { name: "sessionID", required: false, type: "string", note: "\u5355\u4E2A Worker" },
277
+ { name: "reason", required: false, type: "string", note: "\u7EC8\u6B62\u539F\u56E0" },
278
+ { name: "all", required: false, type: "boolean", values: ["true"], note: "\u7EC8\u6B62\u5168\u90E8" },
279
+ { name: "stale", required: false, type: "boolean", values: ["true"], note: "\u53EA\u6E05\u7406\u5DF2\u7ED3\u675F" }
280
+ ],
281
+ risk: "terminate",
282
+ permission: "status",
283
+ confirmation: "\u81EA\u52A8\u6267\u884C\uFF08permission: status\uFF09\uFF1A\u53EA\u63A5\u53D7\u63D2\u4EF6\u8DDF\u8E2A\u7684 Worker\uFF0CRouter \u81EA\u8EAB\u4E0E\u7528\u6237\u4F1A\u8BDD\u4E00\u5F8B\u62D2\u7EDD\u3002",
284
+ usage: [
285
+ "sessionID \u4E0E all/stale \u4E8C\u9009\u4E00\uFF1Bstale \u4E0D\u4F1A\u4E2D\u65AD\u8FD0\u884C\u4E2D\u7684 Worker\u3002",
286
+ "\u7EC8\u6B62\u540E\u8BE5 thread \u7684\u4E0B\u4E00\u6761\u6D88\u606F\u56DE\u843D\u5230 Router \u91CD\u65B0 dispatch\u3002"
287
+ ]
288
+ }
289
+ ];
290
+ var specsByName = new Map(ROUTER_TOOL_SPECS.map((spec) => [spec.name, spec]));
291
+ var ROUTER_TOOLS = new Set(specsByName.keys());
292
+ function specFor(name) {
293
+ const spec = specsByName.get(name);
294
+ if (spec === undefined)
295
+ throw new Error(`superagent tool ${name} has no entry in src/tools.ts`);
296
+ return spec;
297
+ }
298
+ var toolPermission = (spec) => spec.permission ?? spec.name;
299
+ var ROUTER_PERMISSION_ACTIONS = [
300
+ ...new Set(ROUTER_TOOL_SPECS.map(toolPermission))
301
+ ].sort();
302
+ var ROUTER_PERMISSIONS = [
303
+ { action: "*", resource: "*", effect: "deny" },
304
+ ...ROUTER_PERMISSION_ACTIONS.map((action) => ({ action, resource: "*", effect: "allow" }))
305
+ ];
306
+ var ROUTER_LIMITS = [
307
+ "\u4E0D\u80FD\u8BFB\u5199\u6587\u4EF6\u6216\u6267\u884C\u547D\u4EE4\uFF1A\u6CA1\u6709 Read/Write/Edit/Bash\uFF0C\u53EA\u80FD\u6D3E Worker \u53BB\u505A\u3002",
308
+ "\u4E0D\u80FD\u64CD\u4F5C\u975E superagent \u4F1A\u8BDD\uFF1Aabort_session \u53EA\u63A5\u53D7\u63D2\u4EF6\u81EA\u5DF1\u521B\u5EFA\u7684 Worker\uFF0C\u7528\u6237\u4F1A\u8BDD\u3001Router \u81EA\u8EAB\u548C\u672A\u77E5 sessionID \u4E00\u5F8B\u62D2\u7EDD\u3002",
309
+ "\u4E0D\u80FD\u4FEE\u6539\u81EA\u8EAB\uFF1A\u6539\u4E0D\u4E86\u81EA\u5DF1\u7684 prompt\u3001\u5DE5\u5177\u96C6\u6216\u914D\u7F6E\uFF0C\u4E5F\u4E0D\u80FD\u7EC8\u6B62\u81EA\u5DF1\u6240\u5728\u7684 Router \u4F1A\u8BDD\u3002",
310
+ "\u4E0D\u80FD\u76F4\u63A5\u548C Worker \u7684\u6A21\u578B\u5BF9\u8BDD\uFF1A\u53EA\u80FD dispatch \u4EFB\u52A1\uFF0C\u6216\u8BFB\u5B83\u7684\u72B6\u6001\u3001\u6700\u8FD1 assistant \u6D88\u606F\u4E0E\u62A5\u544A\u5168\u6587\uFF08report_read\uFF09\u3002",
311
+ "\u4E0D\u80FD\u8DE8\u7F51\u5173\u6216\u8DE8\u8FDB\u7A0B\uFF1A\u72B6\u6001\u5B58\u5728\u7F51\u5173\u8FDB\u7A0B\u5185\u5B58\u4E0E KV\uFF0C\u91CD\u542F\u540E\u6309 KV \u6062\u590D\uFF0C\u4E0D\u8DE8\u673A\u5668\u540C\u6B65\u3002"
312
+ ];
313
+ var DETAIL_WIDTH = 52;
314
+ function displayWidth(text) {
315
+ let width = 0;
316
+ for (const char of text)
317
+ width += fullWidth(char.codePointAt(0) ?? 0) ? 2 : 1;
318
+ return width;
319
+ }
320
+ function fullWidth(code) {
321
+ return code >= 4352 && code <= 4447 || code >= 11904 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65135 || code >= 65280 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 131072 && code <= 262141;
322
+ }
323
+ var NO_BREAK_BEFORE = "\uFF0C\u3002\u3001\uFF1B\uFF1A\uFF01\uFF1F\uFF09\u300B\u300D\u300F\u3011\u2026";
324
+ function tokens(text) {
325
+ const result = [];
326
+ let word = "";
327
+ for (const char of text) {
328
+ if (char === " " || fullWidth(char.codePointAt(0) ?? 0)) {
329
+ if (word !== "")
330
+ result.push(word);
331
+ word = "";
332
+ const previous = result.at(-1);
333
+ if (NO_BREAK_BEFORE.includes(char) && previous !== undefined) {
334
+ result[result.length - 1] = `${previous}${char}`;
335
+ continue;
336
+ }
337
+ result.push(char);
338
+ continue;
339
+ }
340
+ word += char;
341
+ }
342
+ if (word !== "")
343
+ result.push(word);
344
+ return result;
345
+ }
346
+ function wrapText(text, width) {
347
+ const lines = [];
348
+ let line = "";
349
+ let cells = 0;
350
+ const push = (unit) => {
351
+ const unitWidth = displayWidth(unit);
352
+ if (cells + unitWidth > width && line !== "") {
353
+ lines.push(line.trimEnd());
354
+ line = "";
355
+ cells = 0;
356
+ }
357
+ line += unit;
358
+ cells += unitWidth;
359
+ };
360
+ for (const token of tokens(text)) {
361
+ if (token === " ") {
362
+ if (line !== "")
363
+ push(token);
364
+ continue;
365
+ }
366
+ if (displayWidth(token) > width)
367
+ for (const char of token)
368
+ push(char);
369
+ else
370
+ push(token);
371
+ }
372
+ if (line !== "")
373
+ lines.push(line.trimEnd());
374
+ return lines;
375
+ }
376
+ function bullet(text, width) {
377
+ return wrapText(`\xB7 ${text}`, width).map((line, index) => index === 0 ? line : ` ${line}`);
378
+ }
379
+ function labelled(label, text, width) {
380
+ const indent = " ".repeat(displayWidth(label));
381
+ return wrapText(`${label}${text}`, width).map((line, index) => index === 0 ? line : `${indent}${line}`);
382
+ }
383
+ function pad(text, width) {
384
+ return `${text}${" ".repeat(Math.max(0, width - displayWidth(text)))}`;
385
+ }
386
+ function paramLine(param, nameWidth) {
387
+ const required = param.required ? "\u5FC5\u586B" : "\u53EF\u9009";
388
+ const type = param.values === undefined ? param.type : `${param.type} ${param.values.join("|")}`;
389
+ const note = param.note === undefined ? "" : ` ${param.note}`;
390
+ return `${pad(param.name, nameWidth)}${required} ${type}${note}`;
391
+ }
392
+ function describeTool(spec, width = DETAIL_WIDTH) {
393
+ const lines = [`${spec.name} \xB7 ${GROUP_LABELS[spec.group]} \xB7 ${RISK_LABELS[spec.risk]}`];
394
+ lines.push(...wrapText(spec.summary, width));
395
+ if (spec.params.length === 0) {
396
+ lines.push("", "\u53C2\u6570\uFF1A\u65E0");
397
+ } else {
398
+ const nameWidth = Math.max(...spec.params.map((param) => displayWidth(param.name))) + 2;
399
+ const indent = " ".repeat(nameWidth + 7);
400
+ lines.push("", "\u53C2\u6570");
401
+ for (const param of spec.params) {
402
+ const [head, ...rest] = wrapText(paramLine(param, nameWidth), width - 2);
403
+ if (head !== undefined)
404
+ lines.push(` ${head}`);
405
+ for (const line of rest.length === 0 ? [] : wrapText(rest.join(" "), width - indent.length)) {
406
+ lines.push(`${indent}${line}`);
407
+ }
408
+ }
409
+ }
410
+ lines.push("", ...labelled("\u786E\u8BA4\uFF1A", spec.confirmation, width));
411
+ for (const note of spec.usage ?? [])
412
+ lines.push(...bullet(note, width));
413
+ return lines.join(`
414
+ `);
415
+ }
416
+ function describeLimits(width = DETAIL_WIDTH) {
417
+ const lines = ["Router \u53EA\u80FD\u8C03\u5EA6\uFF0C\u4E0D\u80FD\u81EA\u5DF1\u52A8\u624B\u3002\u4EE5\u4E0B\u662F\u660E\u786E\u7684\u8FB9\u754C\uFF1A", ""];
418
+ for (const limit of ROUTER_LIMITS)
419
+ lines.push(...bullet(limit, width));
420
+ return lines.join(`
421
+ `);
422
+ }
423
+
424
+ // src/ticket-view.ts
425
+ var TICKET_PANEL_WIDTH = 36;
426
+ var TICKET_TITLE_MAX = 26;
427
+ var TICKET_TERMINAL_LIMIT = 3;
428
+ var TICKET_PANEL_LIMIT = 20;
429
+ var TICKET_PANEL_REFRESH_MS = 4000;
430
+ var SHORT_ID_LENGTH = 4;
431
+ var TICKET_STATE_SYMBOL = {
432
+ open: "\u25CB",
433
+ in_progress: "\u25CF",
434
+ blocked: "\u25C6",
435
+ done: "\u2713",
436
+ failed: "\u2717",
437
+ cancelled: "\u2298",
438
+ superseded: "\xB7"
439
+ };
440
+ var STATE_RANK = {
441
+ in_progress: 0,
442
+ blocked: 1,
443
+ open: 2,
444
+ done: 3,
445
+ failed: 4,
446
+ cancelled: 5,
447
+ superseded: 6
448
+ };
449
+ var TERMINAL = new Set(["done", "failed", "cancelled", "superseded"]);
450
+ var GROUP_LINE = /^(open|in_progress|blocked|done|failed|cancelled|superseded) \(\d+\)$/;
451
+ var ITEM_LINE = /^\u00B7 (tk_\S+) (p[012])(?: (.*))?$/;
452
+ var TRAILER_LINE = /^\s{2}\d{2}-\d{2} \d{2}:\d{2}(?: w=\S+)?$/;
453
+ function parseTicketList(text) {
454
+ const rows = [];
455
+ let state;
456
+ for (const line of text.split(`
457
+ `)) {
458
+ const group = GROUP_LINE.exec(line);
459
+ if (group?.[1] !== undefined) {
460
+ state = group[1];
461
+ continue;
462
+ }
463
+ const item = ITEM_LINE.exec(line);
464
+ if (item !== null && state !== undefined) {
465
+ rows.push({
466
+ id: item[1],
467
+ priority: item[2],
468
+ state,
469
+ title: (item[3] ?? "").trim()
470
+ });
471
+ continue;
472
+ }
473
+ if (state !== undefined && rows.length > 0 && line.startsWith(" ") && !TRAILER_LINE.test(line)) {
474
+ const last = rows[rows.length - 1];
475
+ const continuation = line.trim();
476
+ if (continuation !== "")
477
+ last.title = last.title === "" ? continuation : `${last.title} ${continuation}`;
478
+ }
479
+ }
480
+ return rows;
481
+ }
482
+ function truncateCells(text, max) {
483
+ if (max <= 0)
484
+ return "";
485
+ if (displayWidth(text) <= max)
486
+ return text;
487
+ let result = "";
488
+ let cells = 0;
489
+ for (const char of text) {
490
+ const width = displayWidth(char);
491
+ if (cells + width > max - 1)
492
+ break;
493
+ result += char;
494
+ cells += width;
495
+ }
496
+ return `${result}\u2026`;
497
+ }
498
+ function shortTicketID(ticketID) {
499
+ return ticketID.length <= SHORT_ID_LENGTH ? ticketID : ticketID.slice(-SHORT_ID_LENGTH);
500
+ }
501
+ function ticketPanel(rows, width = TICKET_PANEL_WIDTH, terminalLimit = TICKET_TERMINAL_LIMIT) {
502
+ const titleMax = Math.min(TICKET_TITLE_MAX, width - (1 + 1 + 1 + SHORT_ID_LENGTH));
503
+ const ordered = [...rows].sort((left, right) => STATE_RANK[left.state] - STATE_RANK[right.state]);
504
+ const active = ordered.filter((row) => !TERMINAL.has(row.state));
505
+ const terminal = ordered.filter((row) => TERMINAL.has(row.state));
506
+ return {
507
+ total: rows.length,
508
+ active: active.length,
509
+ hidden: Math.max(0, terminal.length - terminalLimit),
510
+ items: [...active, ...terminal.slice(0, terminalLimit)].map((row) => ({
511
+ id: row.id,
512
+ shortID: shortTicketID(row.id),
513
+ title: truncateCells(row.title, titleMax),
514
+ state: row.state
515
+ }))
516
+ };
517
+ }
518
+
519
+ // tui.tsx
520
+ var events = [{
521
+ thread: "T-1",
522
+ text: "Review src/index.ts and report one improvement"
523
+ }, {
524
+ thread: "T-1",
525
+ text: "Check the README for one missing setup step"
526
+ }, {
527
+ thread: "T-1",
528
+ text: "Summarize the test failures in src/index.ts"
529
+ }, {
530
+ thread: "T-2",
531
+ text: "Find one unused export in src/index.ts"
532
+ }, {
533
+ thread: "T-2",
534
+ text: "Summarize the latest commit in one sentence"
535
+ }, {
536
+ thread: "T-3",
537
+ text: "List the files changed in the last 24 hours"
538
+ }];
539
+ var pollInterval = 500;
540
+ var pollAttempts = 40;
541
+ var tui_default = Plugin.define({
542
+ id: "opencode.superagent",
543
+ setup(context) {
544
+ context.keymap.layer(() => ({
545
+ mode: "global",
546
+ commands: [{
547
+ id: "superagent.open",
548
+ title: "Superagent",
549
+ palette: true,
550
+ slash: {
551
+ name: "superagent"
552
+ },
553
+ run: () => open(context)
554
+ }, {
555
+ id: "superagent.tasks",
556
+ title: "Superagent tasks",
557
+ palette: true,
558
+ slash: {
559
+ name: "task"
560
+ },
561
+ run: () => tasks(context)
562
+ }, {
563
+ id: "superagent.abort",
564
+ title: "Superagent abort worker",
565
+ palette: true,
566
+ slash: {
567
+ name: "abort"
568
+ },
569
+ run: () => abort(context)
570
+ }, {
571
+ id: "superagent.tools",
572
+ title: "Superagent tools",
573
+ palette: true,
574
+ slash: {
575
+ name: "tools",
576
+ aliases: ["superagent-tools"]
577
+ },
578
+ run: () => tools(context)
579
+ }, {
580
+ id: "superagent.tickets",
581
+ title: "Superagent tickets",
582
+ palette: true,
583
+ slash: {
584
+ name: "tickets"
585
+ },
586
+ run: () => tickets(context)
587
+ }, {
588
+ id: "superagent.ticket",
589
+ title: "Superagent ticket detail",
590
+ palette: true,
591
+ slash: {
592
+ name: "ticket",
593
+ arguments: true
594
+ },
595
+ run: (input) => ticket(context, input)
596
+ }, {
597
+ id: "superagent.demo",
598
+ title: "Superagent demo triggers",
599
+ palette: true,
600
+ slash: {
601
+ name: "superagent-demo"
602
+ },
603
+ run: () => demo(context)
604
+ }]
605
+ }));
606
+ context.ui.slot({
607
+ append: "sidebar.content",
608
+ render: () => _$createComponent(SidebarTickets, {
609
+ context
610
+ })
611
+ });
612
+ }
613
+ });
614
+ function ticketStateColor(theme, state) {
615
+ switch (state) {
616
+ case "in_progress":
617
+ return theme.text.feedback.info.base;
618
+ case "blocked":
619
+ return theme.text.feedback.warning.base;
620
+ case "done":
621
+ return theme.text.feedback.success.base;
622
+ case "failed":
623
+ return theme.text.feedback.error.base;
624
+ case "cancelled":
625
+ case "superseded":
626
+ return theme.text.muted;
627
+ case "open":
628
+ return theme.text.base;
629
+ }
630
+ }
631
+ function SidebarTickets(props) {
632
+ const theme = props.context.theme;
633
+ const [rows, setRows] = createSignal([]);
634
+ const [failed, setFailed] = createSignal(false);
635
+ let disposed = false;
636
+ let running = false;
637
+ const refresh = async () => {
638
+ if (running)
639
+ return;
640
+ running = true;
641
+ try {
642
+ const rpc = props.context.client.rpc(Definition);
643
+ const {
644
+ text
645
+ } = await rpc.tickets({
646
+ limit: TICKET_PANEL_LIMIT
647
+ }, options(props.context));
648
+ if (disposed)
649
+ return;
650
+ setRows(parseTicketList(text));
651
+ setFailed(false);
652
+ } catch {
653
+ if (!disposed)
654
+ setFailed(true);
655
+ } finally {
656
+ running = false;
657
+ }
658
+ };
659
+ onMount(() => {
660
+ refresh();
661
+ const timer = setInterval(() => void refresh(), TICKET_PANEL_REFRESH_MS);
662
+ onCleanup(() => {
663
+ disposed = true;
664
+ clearInterval(timer);
665
+ });
666
+ });
667
+ const panel = createMemo(() => ticketPanel(rows()));
668
+ return _$createComponent(Show, {
669
+ get when() {
670
+ return panel().items.length > 0 || failed();
671
+ },
672
+ get children() {
673
+ var _el$ = _$createElement("box");
674
+ _$insert(_el$, _$createComponent(Show, {
675
+ get when() {
676
+ return panel().items.length > 0;
677
+ },
678
+ get fallback() {
679
+ return (() => {
680
+ var _el$11 = _$createElement("text");
681
+ _$insertNode(_el$11, _$createTextNode(`\u5DE5\u5355 \u8BFB\u53D6\u5931\u8D25`));
682
+ _$effect((_$p) => _$setProp(_el$11, "fg", theme.text.feedback.warning.base, _$p));
683
+ return _el$11;
684
+ })();
685
+ },
686
+ get children() {
687
+ return [(() => {
688
+ var _el$2 = _$createElement("text"), _el$3 = _$createElement("b"), _el$4 = _$createTextNode(`\u5DE5\u5355 `);
689
+ _$insertNode(_el$2, _el$3);
690
+ _$setProp(_el$2, "wrapMode", "none");
691
+ _$setProp(_el$2, "truncate", true);
692
+ _$insertNode(_el$3, _el$4);
693
+ _$insert(_el$3, () => panel().total, null);
694
+ _$insert(_el$2, _$createComponent(Show, {
695
+ get when() {
696
+ return panel().active > 0;
697
+ },
698
+ get children() {
699
+ var _el$5 = _$createElement("span"), _el$6 = _$createTextNode(` \xB7 `), _el$7 = _$createTextNode(` \u672A\u7ED3`);
700
+ _$insertNode(_el$5, _el$6);
701
+ _$insertNode(_el$5, _el$7);
702
+ _$insert(_el$5, () => panel().active, _el$7);
703
+ _$effect((_$p) => _$setProp(_el$5, "style", {
704
+ fg: theme.text.muted
705
+ }, _$p));
706
+ return _el$5;
707
+ }
708
+ }), null);
709
+ _$insert(_el$2, _$createComponent(Show, {
710
+ get when() {
711
+ return failed();
712
+ },
713
+ get children() {
714
+ var _el$8 = _$createElement("span");
715
+ _$insertNode(_el$8, _$createTextNode(` \xB7 \u8BFB\u53D6\u5931\u8D25`));
716
+ _$effect((_$p) => _$setProp(_el$8, "style", {
717
+ fg: theme.text.feedback.warning.base
718
+ }, _$p));
719
+ return _el$8;
720
+ }
721
+ }), null);
722
+ _$effect((_$p) => _$setProp(_el$2, "fg", theme.text.base, _$p));
723
+ return _el$2;
724
+ })(), _$createComponent(For, {
725
+ get each() {
726
+ return panel().items;
727
+ },
728
+ children: (item) => (() => {
729
+ var _el$13 = _$createElement("box"), _el$14 = _$createElement("text"), _el$15 = _$createElement("span"), _el$16 = _$createElement("span"), _el$17 = _$createTextNode(` `), _el$18 = _$createElement("span"), _el$19 = _$createTextNode(` `);
730
+ _$insertNode(_el$13, _el$14);
731
+ _$setProp(_el$13, "flexDirection", "row");
732
+ _$setProp(_el$13, "minWidth", 0);
733
+ _$setProp(_el$13, "onMouseUp", () => void ticket(props.context, item.id));
734
+ _$insertNode(_el$14, _el$15);
735
+ _$insertNode(_el$14, _el$16);
736
+ _$insertNode(_el$14, _el$18);
737
+ _$setProp(_el$14, "wrapMode", "none");
738
+ _$setProp(_el$14, "truncate", true);
739
+ _$insert(_el$15, () => TICKET_STATE_SYMBOL[item.state]);
740
+ _$insertNode(_el$16, _el$17);
741
+ _$insert(_el$16, () => item.title, null);
742
+ _$insertNode(_el$18, _el$19);
743
+ _$insert(_el$18, () => item.shortID, null);
744
+ _$effect((_p$) => {
745
+ var _v$ = {
746
+ fg: ticketStateColor(theme, item.state)
747
+ }, _v$2 = {
748
+ fg: theme.text.base
749
+ }, _v$3 = {
750
+ fg: theme.text.muted
751
+ };
752
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$15, "style", _v$, _p$.e));
753
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$16, "style", _v$2, _p$.t));
754
+ _v$3 !== _p$.a && (_p$.a = _$setProp(_el$18, "style", _v$3, _p$.a));
755
+ return _p$;
756
+ }, {
757
+ e: undefined,
758
+ t: undefined,
759
+ a: undefined
760
+ });
761
+ return _el$13;
762
+ })()
763
+ }), _$createComponent(Show, {
764
+ get when() {
765
+ return panel().hidden > 0;
766
+ },
767
+ get children() {
768
+ var _el$0 = _$createElement("text"), _el$1 = _$createTextNode(`\u2026 \u53E6\u6709 `), _el$10 = _$createTextNode(` \u6761\u5DF2\u7ED3`);
769
+ _$insertNode(_el$0, _el$1);
770
+ _$insertNode(_el$0, _el$10);
771
+ _$insert(_el$0, () => panel().hidden, _el$10);
772
+ _$effect((_$p) => _$setProp(_el$0, "fg", theme.text.muted, _$p));
773
+ return _el$0;
774
+ }
775
+ })];
776
+ }
777
+ }));
778
+ return _el$;
779
+ }
780
+ });
781
+ }
782
+ async function open(context) {
783
+ try {
784
+ const rpc = context.client.rpc(Definition);
785
+ const {
786
+ sessionID
787
+ } = await rpc.router({}, options(context));
788
+ gotoSession(context, sessionID);
789
+ } catch (error) {
790
+ context.ui.toast.show({
791
+ title: "Superagent",
792
+ message: message(error),
793
+ variant: "error"
794
+ });
795
+ }
796
+ }
797
+ async function tasks(context) {
798
+ try {
799
+ const rpc = context.client.rpc(Definition);
800
+ const status = await rpc.status({}, options(context));
801
+ if (status.workers.length === 0) {
802
+ context.ui.toast.show({
803
+ title: "Superagent tasks",
804
+ message: "\u6682\u65E0 worker",
805
+ variant: "info"
806
+ });
807
+ return;
808
+ }
809
+ const sessionID = await context.ui.dialog.select({
810
+ title: "Superagent workers",
811
+ options: status.workers.map((worker) => ({
812
+ title: worker.title ?? worker.sessionID.slice(0, 8),
813
+ description: `${worker.state} \xB7 ${worker.threads.join(",")} \xB7 ${worker.sessionID.slice(0, 8)}`,
814
+ value: worker.sessionID
815
+ }))
816
+ });
817
+ if (sessionID === undefined)
818
+ return;
819
+ gotoSession(context, sessionID);
820
+ } catch (error) {
821
+ context.ui.toast.show({
822
+ title: "Superagent tasks",
823
+ message: message(error),
824
+ variant: "error"
825
+ });
826
+ }
827
+ }
828
+ var STALE_CHOICE = "__stale__";
829
+ async function abort(context) {
830
+ try {
831
+ const rpc = context.client.rpc(Definition);
832
+ const call = options(context);
833
+ const status = await rpc.status({}, call);
834
+ const selection = await context.ui.dialog.select({
835
+ title: "Superagent abort",
836
+ options: [{
837
+ title: "\u6E05\u7406\u5168\u90E8\u5DF2\u7ED3\u675F worker",
838
+ description: "stale\uFF1A\u53EA\u6E05\u7406 done/failed/idle/aborted\uFF0C\u4E0D\u4E2D\u65AD\u8FD0\u884C\u4E2D\u7684 worker",
839
+ value: STALE_CHOICE
840
+ }, ...status.workers.map((worker) => ({
841
+ title: worker.title ?? worker.sessionID.slice(0, 8),
842
+ description: `${worker.state} \xB7 ${worker.threads.join(",") || "-"} \xB7 ${worker.sessionID.slice(0, 8)}`,
843
+ value: worker.sessionID
844
+ }))]
845
+ });
846
+ if (selection === undefined)
847
+ return;
848
+ const result = selection === STALE_CHOICE ? await rpc.abort({
849
+ stale: true
850
+ }, call) : await rpc.abort({
851
+ sessionID: selection
852
+ }, call);
853
+ context.ui.toast.show({
854
+ title: "Superagent abort",
855
+ message: result.text,
856
+ variant: "info"
857
+ });
858
+ } catch (error) {
859
+ context.ui.toast.show({
860
+ title: "Superagent abort",
861
+ message: message(error),
862
+ variant: "error"
863
+ });
864
+ }
865
+ }
866
+ var LIMITS_CHOICE = "__limits__";
867
+ async function tools(context) {
868
+ try {
869
+ for (;; ) {
870
+ const selection = await context.ui.dialog.select({
871
+ title: `Superagent \u5DE5\u5177 \xB7 ${ROUTER_TOOL_SPECS.length}`,
872
+ placeholder: "\u641C\u7D22\u5DE5\u5177\uFF1Adispatch / abort / status \u2026",
873
+ options: [...ROUTER_TOOL_SPECS.map((spec) => {
874
+ const required = spec.params.flatMap((param) => param.required ? [param.name] : []);
875
+ return {
876
+ title: spec.name,
877
+ description: `${RISK_LABELS[spec.risk]} \xB7 ${spec.summary}`,
878
+ footer: required.length === 0 ? spec.params.length === 0 ? "\u65E0\u53C2\u6570" : "\u65E0\u5FC5\u586B" : required.join(","),
879
+ category: GROUP_LABELS[spec.group],
880
+ value: spec.name
881
+ };
882
+ }), {
883
+ title: "\u80FD\u529B\u8FB9\u754C",
884
+ description: `Router \u4E0D\u80FD\u505A\u4EC0\u4E48\uFF08${ROUTER_LIMITS.length} \u6761\uFF09`,
885
+ footer: "\u4E0D\u542B\u5DE5\u5177",
886
+ category: "\u8BF4\u660E",
887
+ value: LIMITS_CHOICE
888
+ }]
889
+ });
890
+ if (selection === undefined)
891
+ return;
892
+ if (selection === LIMITS_CHOICE) {
893
+ await context.ui.dialog.alert({
894
+ title: "Superagent \u80FD\u529B\u8FB9\u754C",
895
+ message: describeLimits()
896
+ });
897
+ continue;
898
+ }
899
+ await context.ui.dialog.alert({
900
+ title: `Superagent ${selection}`,
901
+ message: describeTool(specFor(selection))
902
+ });
903
+ }
904
+ } catch (error) {
905
+ context.ui.toast.show({
906
+ title: "Superagent tools",
907
+ message: message(error),
908
+ variant: "error"
909
+ });
910
+ }
911
+ }
912
+ async function tickets(context) {
913
+ try {
914
+ const rpc = context.client.rpc(Definition);
915
+ const {
916
+ text
917
+ } = await rpc.tickets({}, options(context));
918
+ await context.ui.dialog.alert({
919
+ title: "Superagent \u5DE5\u5355",
920
+ message: text
921
+ });
922
+ } catch (error) {
923
+ context.ui.toast.show({
924
+ title: "Superagent tickets",
925
+ message: message(error),
926
+ variant: "error"
927
+ });
928
+ }
929
+ }
930
+ async function ticket(context, input) {
931
+ try {
932
+ const ticketID = input?.trim() || (await context.ui.dialog.prompt({
933
+ title: "/ticket",
934
+ placeholder: "tk_\u2026"
935
+ }))?.trim();
936
+ if (ticketID === undefined || ticketID === "")
937
+ return;
938
+ const rpc = context.client.rpc(Definition);
939
+ const {
940
+ text
941
+ } = await rpc.ticket({
942
+ ticketID
943
+ }, options(context));
944
+ await context.ui.dialog.alert({
945
+ title: `Superagent ${ticketID}`,
946
+ message: text
947
+ });
948
+ } catch (error) {
949
+ context.ui.toast.show({
950
+ title: "Superagent ticket",
951
+ message: message(error),
952
+ variant: "error"
953
+ });
954
+ }
955
+ }
956
+ function gotoSession(context, sessionID) {
957
+ context.ui.router.navigate({
958
+ type: "session",
959
+ sessionID
960
+ });
961
+ if (context.ui.tabs.enabled())
962
+ context.ui.tabs.focus(sessionID);
963
+ }
964
+ async function demo(context) {
965
+ try {
966
+ const rpc = context.client.rpc(Definition);
967
+ const call = options(context);
968
+ await rpc.router({}, call);
969
+ const routes = [await trigger(rpc, events[0], call)];
970
+ const ready = await waitForWorker(rpc, "T-1", call);
971
+ for (const event of events.slice(1)) {
972
+ routes.push(await trigger(rpc, event, call));
973
+ await sleep(150);
974
+ }
975
+ const summary = events.map((event, index) => `${event.thread}=${routes[index]?.route ?? "unknown"}`).join(" ");
976
+ context.ui.toast.show({
977
+ title: "Superagent demo",
978
+ message: ready ? summary : `${summary} (T-1 wait timed out)`,
979
+ variant: ready ? "info" : "warning"
980
+ });
981
+ } catch (error) {
982
+ context.ui.toast.show({
983
+ title: "Superagent demo",
984
+ message: message(error),
985
+ variant: "error"
986
+ });
987
+ }
988
+ }
989
+ async function trigger(rpc, event, options) {
990
+ return rpc.trigger({
991
+ source: "demo",
992
+ threadID: event.thread,
993
+ author: "demo-user",
994
+ text: event.text
995
+ }, options);
996
+ }
997
+ async function waitForWorker(rpc, thread, options) {
998
+ for (let attempt = 0;attempt < pollAttempts; attempt++) {
999
+ await sleep(pollInterval);
1000
+ const status = await rpc.status({}, options);
1001
+ if (status.workers.some((worker) => worker.threads.includes(thread)))
1002
+ return true;
1003
+ }
1004
+ return false;
1005
+ }
1006
+ function sleep(ms) {
1007
+ return new Promise((resolve) => setTimeout(resolve, ms));
1008
+ }
1009
+ function options(context) {
1010
+ return context.location ? {
1011
+ location: {
1012
+ directory: context.location.directory
1013
+ }
1014
+ } : {};
1015
+ }
1016
+ function message(error) {
1017
+ if (error instanceof Error)
1018
+ return error.message;
1019
+ if (typeof error === "string")
1020
+ return error;
1021
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string")
1022
+ return error.message;
1023
+ return String(error);
1024
+ }
1025
+ export {
1026
+ tui_default as default
1027
+ };