@heretek-ai/epistemic-swarm 1.1.4 → 2.3.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/dist/tui.js +493 -247
- package/package.json +7 -4
- package/src/agents.ts +77 -6
- package/src/approve-tui.ts +124 -0
- package/src/hooks.ts +15 -3
- package/src/masked-input.tsx +123 -0
- package/src/panels.tsx +7 -2
- package/src/policy.ts +28 -4
- package/src/pr.ts +17 -13
- package/src/research.ts +9 -2
- package/src/rpc-def.ts +30 -9
- package/src/rpc.ts +40 -12
- package/src/runtime.ts +22 -5
- package/src/seats.ts +28 -1
- package/src/server.ts +384 -245
- package/src/tools.ts +5 -1
- package/src/tui.tsx +61 -8
package/src/tools.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
scoutTools,
|
|
15
15
|
} from "@heretek-ai/es-core"
|
|
16
16
|
import { Error as ToolError } from "@opencode/plugin/promise/tool"
|
|
17
|
+
import { modelProblemLines } from "./agents.ts"
|
|
17
18
|
import type { Runtime } from "./runtime.ts"
|
|
18
19
|
|
|
19
20
|
export function registerTools(editor: { add(tool: any): void }, runtime: Runtime) {
|
|
@@ -64,7 +65,10 @@ export function registerTools(editor: { add(tool: any): void }, runtime: Runtime
|
|
|
64
65
|
},
|
|
65
66
|
execute: async (input: unknown, context: { agent: string; sessionID: string; signal: AbortSignal }) => {
|
|
66
67
|
try {
|
|
67
|
-
|
|
68
|
+
const content = await def.execute(input ?? {}, context)
|
|
69
|
+
if (def.name !== "es_status" || runtime.modelProblems.size === 0 || typeof content !== "string")
|
|
70
|
+
return { content }
|
|
71
|
+
return { content: [content, ...modelProblemLines(runtime.modelProblems)].join("\n") }
|
|
68
72
|
} catch (error) {
|
|
69
73
|
if (error instanceof ToolError) throw error
|
|
70
74
|
throw new ToolError({ message: error instanceof Error ? error.message : String(error) })
|
package/src/tui.tsx
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
|
-
// Epistemic Swarm TUI plugin:
|
|
2
|
-
//
|
|
3
|
-
// passphrase-sealed human key (`
|
|
4
|
-
//
|
|
1
|
+
// Epistemic Swarm TUI plugin: human-only actions. Approvals complete here,
|
|
2
|
+
// through a masked passphrase dialog and in-process signing with the
|
|
3
|
+
// passphrase-sealed human key (`channel: "tui"`); trust and resume are
|
|
4
|
+
// previewed here, then recorded at a terminal with the same key
|
|
5
|
+
// (`es trust`, `es factory resume`), never through an agent tool or RPC.
|
|
6
|
+
// Only lspInstall still mutates via RPC.
|
|
5
7
|
import { userInfo } from "node:os"
|
|
6
8
|
import { Plugin } from "@opencode/plugin/tui"
|
|
9
|
+
import { approveInTui } from "./approve-tui.ts"
|
|
10
|
+
import { readMaskedPassphrase } from "./masked-input.tsx"
|
|
7
11
|
import { registerPanels } from "./panels.tsx"
|
|
8
12
|
import { EsRpc } from "./rpc-def.ts"
|
|
9
13
|
|
|
10
|
-
type Preview = {
|
|
14
|
+
type Preview = {
|
|
15
|
+
ok: boolean
|
|
16
|
+
title: string
|
|
17
|
+
lines: string[]
|
|
18
|
+
problems: string[]
|
|
19
|
+
token?: string
|
|
20
|
+
subjectHash?: string
|
|
21
|
+
issuedAt?: number
|
|
22
|
+
}
|
|
11
23
|
|
|
12
24
|
export default Plugin.define({
|
|
13
25
|
id: "epistemic-swarm.tui",
|
|
@@ -67,10 +79,51 @@ export default Plugin.define({
|
|
|
67
79
|
const preview: Preview = await call("previewApproval", { stage })
|
|
68
80
|
const token = await confirm(preview, "Approve")
|
|
69
81
|
if (!token) return
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
82
|
+
// Approvals complete inside the TUI (ADR 0002): masked passphrase
|
|
83
|
+
// dialog, in-process signing through the core service, channel "tui".
|
|
84
|
+
// No passphrase and no approval cross RPC. Trust and resume keep the
|
|
85
|
+
// terminal alert below.
|
|
86
|
+
const terminalAlert = () =>
|
|
87
|
+
context.ui.dialog.alert({
|
|
88
|
+
title: preview.title,
|
|
89
|
+
message: `${preview.lines.join("\n")}\n\nRun \`es approve ${stage}\` at a terminal with your passphrase (approvals need the sealed human key).`,
|
|
90
|
+
})
|
|
91
|
+
if (!preview.subjectHash) {
|
|
92
|
+
await terminalAlert()
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
const outcome = await approveInTui({
|
|
96
|
+
root: location().directory,
|
|
97
|
+
stage,
|
|
98
|
+
expectedSubjectHash: preview.subjectHash,
|
|
99
|
+
// ADR 0002 I3: the preview ticket is redeemed exactly once inside
|
|
100
|
+
// approveInTui (single-use + 120 s TTL). The redeem is validate-only:
|
|
101
|
+
// signing stays in-process below (#49).
|
|
102
|
+
...(token ? { previewToken: token } : {}),
|
|
103
|
+
...(preview.issuedAt !== undefined ? { previewIssuedAt: preview.issuedAt } : {}),
|
|
104
|
+
redeemPreview: (previewToken) =>
|
|
105
|
+
call("redeemApproval", { stage, token: previewToken, subjectHash: preview.subjectHash }),
|
|
106
|
+
approvedBy: user,
|
|
107
|
+
readPassphrase: (title) => readMaskedPassphrase(context.ui.dialog, title),
|
|
73
108
|
})
|
|
109
|
+
if (outcome.ok) {
|
|
110
|
+
toast(
|
|
111
|
+
`Approved ${stage} as ${outcome.record.approvedBy} (${outcome.record.subject.length} artifact(s)).`,
|
|
112
|
+
"success",
|
|
113
|
+
)
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
if ("fallback" in outcome) {
|
|
117
|
+
await terminalAlert()
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
if ("locked" in outcome) {
|
|
121
|
+
await context.ui.dialog.alert({
|
|
122
|
+
title: preview.title,
|
|
123
|
+
message: `Too many wrong passphrases. Try again in ${outcome.retryAfterSec}s (the lockout is audited).`,
|
|
124
|
+
})
|
|
125
|
+
return
|
|
126
|
+
}
|
|
74
127
|
})
|
|
75
128
|
|
|
76
129
|
const trust = guarded(async () => {
|