pantheon-opencode 1.4.3 → 1.5.0-beta.2

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 (99) hide show
  1. package/.pantheon/code-mode/README.md +93 -0
  2. package/.pantheon/code-mode/checkpoint-session.py +7 -0
  3. package/.pantheon/code-mode/checkpoint-session.sh +11 -0
  4. package/.pantheon/code-mode/checkpoint_session.py +481 -0
  5. package/.pantheon/code-mode/compress-inline.py +450 -0
  6. package/.pantheon/code-mode/eval-llm-judge.py +255 -0
  7. package/.pantheon/code-mode/eval-monte-carlo.py +368 -0
  8. package/.pantheon/code-mode/eval-run.py +197 -0
  9. package/.pantheon/code-mode/eval-static.py +321 -0
  10. package/.pantheon/code-mode/example-sync.sh +5 -0
  11. package/.pantheon/code-mode/session-end-save.py +186 -0
  12. package/.pantheon/code-mode/session-end-save.sh +18 -0
  13. package/.pantheon/code-mode/test-allowed.sh +5 -0
  14. package/.pantheon/code-mode/test-auto-continue-checkpoint.sh +179 -0
  15. package/.pantheon/code-mode/test-env.sh +7 -0
  16. package/.pantheon/code-mode/test-prlimit.sh +9 -0
  17. package/.pantheon/code-mode/test-timeout.sh +6 -0
  18. package/README.md +161 -1101
  19. package/README.pt-BR.md +222 -0
  20. package/bin/pantheon-init.mjs +30 -11
  21. package/opencode.json +1 -0
  22. package/package.json +14 -3
  23. package/plugin.json +15 -0
  24. package/scripts/_pantheon_paths.py +48 -7
  25. package/scripts/ci-validate-yaml.py +7 -2
  26. package/scripts/code_mode_server.py +33 -2
  27. package/scripts/cost.mjs +30 -14
  28. package/scripts/doctor.mjs +135 -127
  29. package/scripts/generate-preset-docs.mjs +127 -66
  30. package/scripts/generate-prompts.sh +2 -2
  31. package/scripts/install/cli-ui.mjs +8 -1
  32. package/scripts/install/config-migration.mjs +401 -0
  33. package/scripts/install/health-check.mjs +3 -3
  34. package/scripts/install/model-picker.mjs +72 -23
  35. package/scripts/install/opencode-version.mjs +47 -0
  36. package/scripts/install/opencode.mjs +261 -88
  37. package/scripts/install/plugin.mjs +112 -42
  38. package/scripts/install/shared.mjs +13 -158
  39. package/scripts/install-mcp.mjs +44 -232
  40. package/scripts/mcp_persistence_server.py +382 -16
  41. package/scripts/mcp_resources_server.py +144 -40
  42. package/scripts/memory_mcp_server.py +2 -2
  43. package/scripts/probe-context-rehydrate.mjs +240 -0
  44. package/scripts/probe-pantheon-cost.mjs +304 -0
  45. package/scripts/recover-zenodo-deposition.mjs +146 -0
  46. package/scripts/sync-tui.mjs +32 -7
  47. package/scripts/test-opencode-v1-v2-sandbox.sh +944 -0
  48. package/scripts/uninstall.mjs +298 -954
  49. package/scripts/validate-zenodo-release.mjs +154 -0
  50. package/scripts/zenodo-artifact.mjs +21 -0
  51. package/scripts/zenodo-release-state.mjs +53 -0
  52. package/src/agents/zeus.md +25 -14
  53. package/src/mcp/_pantheon_paths.py +48 -7
  54. package/src/mcp/code_mode_server.py +33 -2
  55. package/src/mcp/eval_store.py +1 -1
  56. package/src/mcp/mcp_persistence_server.py +382 -16
  57. package/src/mcp/mcp_resources_server.py +144 -40
  58. package/src/mcp/pantheon_vision_server.py +1 -1
  59. package/src/mcp/toon_codec.py +491 -0
  60. package/src/pantheon/background-job-board.ts +67 -1
  61. package/src/pantheon/cost-command.ts +60 -39
  62. package/src/pantheon/delegate-manager.ts +669 -0
  63. package/src/pantheon/delegation-classifier.ts +307 -0
  64. package/src/pantheon/delegation-enforce.ts +13 -12
  65. package/src/pantheon/delegation-finalize.ts +374 -0
  66. package/src/pantheon/delegation-notify.ts +212 -0
  67. package/src/pantheon/delegation-toast.ts +73 -0
  68. package/src/pantheon/delegation.ts +1120 -0
  69. package/src/pantheon/goal-loop.ts +2 -2
  70. package/src/pantheon/goal-store.ts +1 -1
  71. package/src/pantheon/hashline/tool.ts +2 -2
  72. package/src/pantheon/model-command.ts +2 -1
  73. package/src/pantheon/permission-eval.ts +284 -0
  74. package/src/pantheon/step-cap.ts +110 -0
  75. package/src/pantheon/token-opt.ts +781 -0
  76. package/src/pantheon/toon-codec.ts +481 -0
  77. package/src/pantheon/tui-version-check.ts +1 -3
  78. package/src/pantheon/v2-bridge.ts +129 -0
  79. package/src/pantheon/v2-events.ts +131 -0
  80. package/src/pantheon/v2-hooks.ts +287 -0
  81. package/src/pantheon/v2-tool-definitions.ts +331 -0
  82. package/src/pantheon/zeus-delegate-with-retry.ts +406 -0
  83. package/src/plugin-v2/index.ts +21 -0
  84. package/src/plugin-v2.ts +824 -0
  85. package/src/plugin.ts +236 -42
  86. package/src/plugins/tui/dist/server.d.ts +1 -1
  87. package/src/plugins/tui/dist/server.d.ts.map +1 -1
  88. package/src/plugins/tui/dist/server.js +1 -1
  89. package/src/plugins/tui/dist/server.js.map +1 -1
  90. package/src/plugins/tui/dist/tui.d.ts +10 -7
  91. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  92. package/src/plugins/tui/dist/tui.js +11 -8
  93. package/src/plugins/tui/dist/tui.js.map +1 -1
  94. package/src/plugins/tui/dist/tui.tsx +22 -16
  95. package/src/plugins/tui/package-lock.json +51 -51
  96. package/src/plugins/tui/package.json +7 -7
  97. package/src/plugins/tui/src/index.tsx +28 -24
  98. package/src/skills/README.md +0 -2
  99. package/src/pantheon/tool-context.ts +0 -41
@@ -0,0 +1,406 @@
1
+ /**
2
+ * Zeus Delegate With Retry (Wave 4, empty-mode fix) — helper for Zeus waves.
3
+ *
4
+ * Opencode 1.18.x cannot intercept `task()` completion via hooks, so the
5
+ * `dispatch-guard` lib is NOT wired in the plugin. Zeus waves MUST use this
6
+ * helper manually: it encapsulates
7
+ *
8
+ * delegate → waitForTerminal → classify via createDispatchGuard
9
+ * → maybeRetry 1x on empty-mode1/mode2 → escalate
10
+ *
11
+ * Retry cap is HARD at 1 (never 2x). The guard is stateful per helper
12
+ * instance — one retry budget total. Fail-open on missing APIs is NOT
13
+ * applicable here: a failed delegate throws/log+escalate so the wave can
14
+ * decide (a) retry with different agent, (b) simplify, (c) manual.
15
+ *
16
+ * Two usage shapes:
17
+ * 1. High-level: `zeusDelegateWithRetry({ board, client, sessionID, agent, prompt })`
18
+ * — creates child session, registers board, promptAsync, waitForTerminal,
19
+ * reads report, classifies, retries once if empty, escalates if still empty.
20
+ * 2. Low-level: `createZeusRetryHelper()` — wraps a generic
21
+ * `() => Promise<DispatchResultLike>` pair via `executeWithRetry`.
22
+ *
23
+ * The high-level helper is the one Zeus waves should import:
24
+ *
25
+ * ```ts
26
+ * import { zeusDelegateWithRetry } from './pantheon/zeus-delegate-with-retry.ts'
27
+ * const { report, alias, taskID, retried, classification } =
28
+ * await zeusDelegateWithRetry({ board, client, sessionID, agent: 'apollo', prompt })
29
+ * ```
30
+ *
31
+ * If the result is empty after one retry, the helper THROWS with an
32
+ * `EscalationError` — the wave must catch and escalate to the user (see
33
+ * `src/agents/zeus.md` "Waves with Retry").
34
+ *
35
+ * Pure TypeScript — no direct SDK import. Board/client are structural.
36
+ *
37
+ * @module zeus-delegate-with-retry
38
+ */
39
+
40
+ import type { BackgroundJobBoard } from './background-job-board.ts'
41
+ import type { DelegationClient } from './delegation-finalize.ts'
42
+ import { DELEGATION_DEFAULTS, readDelegationReport } from './delegation-finalize.ts'
43
+ import {
44
+ createDispatchGuard,
45
+ type DispatchClassification,
46
+ type DispatchGuard,
47
+ type DispatchGuardOptions,
48
+ type DispatchResultLike,
49
+ } from './dispatch-guard.ts'
50
+ import { createPantheonLogger } from './logger.ts'
51
+ import {
52
+ type ProviderCooldownTracker,
53
+ type RetryPolicy,
54
+ RetryPolicyEngine,
55
+ } from './retry-policy.ts'
56
+ import { safeSessionPath } from './session-guard.ts'
57
+
58
+ const log = createPantheonLogger({ module: 'pantheon-zeus-retry' })
59
+
60
+ // ─── Types ───────────────────────────────────────────────────────────────
61
+
62
+ export interface ZeusDelegateWithRetryOptions {
63
+ /** Board singleton (same instance the plugin uses). */
64
+ board: BackgroundJobBoard
65
+ /** Delegation client (adapted SDK client, e.g. adaptDelegationClient). */
66
+ client: DelegationClient
67
+ /** Parent session that delegates (must be a valid ses_ id). */
68
+ sessionID: string
69
+ /** Target agent (e.g. "apollo", "hermes"). */
70
+ agent: string
71
+ /** Task prompt delivered to the background agent. */
72
+ prompt: string
73
+ /** Human-readable description shown on the job board. */
74
+ description?: string
75
+ /** Explicit model for the child session (provider/model). */
76
+ model?: { id: string; providerID: string }
77
+ /** Timeout for board.waitForTerminal (ms). Default: DELEGATION_DEFAULTS.readTimeoutMs */
78
+ readTimeoutMs?: number
79
+ /** Retry on empty results. Default: true */
80
+ retryOnEmpty?: boolean
81
+ /** R1: per-error-type retry policy (routing.yml `retry_policy`). */
82
+ retryPolicy?: RetryPolicy
83
+ /** R1: provider cooldown tracker (routing.yml `cooldown`). */
84
+ cooldown?: ProviderCooldownTracker
85
+ /** R1: provider name for cooldown tracking (default "default"). */
86
+ provider?: string
87
+ /** Injectable logger. Default: file log (PANTHEON_HOOKS_LOG gate). */
88
+ logger?: { warn: (message: string) => void; info?: (message: string) => void }
89
+ /** Output dir for delegation reports. Default: DELEGATION_DEFAULTS.outputDir */
90
+ outputDir?: string
91
+ }
92
+
93
+ export interface ZeusDelegateResult {
94
+ /** Job alias (e.g. "apo-1"). */
95
+ alias: string
96
+ /** Child session id (= board task id). */
97
+ taskID: string
98
+ /** Report markdown (with Agent Activity section when sampled). */
99
+ report: string
100
+ /** Classification of the final result. */
101
+ classification: DispatchClassification
102
+ /** True when a retry was fired (cap 1). */
103
+ retried: boolean
104
+ }
105
+
106
+ /**
107
+ * Thrown when a delegate returns empty even after one retry — the wave must
108
+ * escalate (try different agent, simplify scope, or manual intervention).
109
+ */
110
+ export class ZeusEscalationError extends Error {
111
+ constructor(
112
+ public readonly agent: string,
113
+ public readonly prompt: string,
114
+ public readonly classification: DispatchClassification,
115
+ ) {
116
+ super(
117
+ `Zeus delegate escalate: agent "${agent}" returned ${classification} even after retry (prompt: "${prompt.slice(0, 80)}") — escalate to user: (a) try different agent, (b) simplify scope, (c) manual`,
118
+ )
119
+ this.name = 'ZeusEscalationError'
120
+ }
121
+ }
122
+
123
+ // ─── Low-level helper — wraps any async delegate+read pair ───────────────
124
+
125
+ /**
126
+ * Low-level retry helper. Zeus waves that already have a `delegate+read`
127
+ * pair can use this directly without the board/client machinery:
128
+ *
129
+ * ```ts
130
+ * const guard = createZeusRetryHelper({ logger })
131
+ * const out = await guard.executeWithRetry(
132
+ * () => firstRead(),
133
+ * () => secondRead(),
134
+ * )
135
+ * if (out.escalate) throw new ZeusEscalationError(...)
136
+ * ```
137
+ */
138
+ export interface ZeusRetryHelper {
139
+ guard: DispatchGuard
140
+ /**
141
+ * Classify `first` and, if empty and budget allows, run `redispatch` once.
142
+ * Returns the final result plus escalation flag (true when still empty after
143
+ * the one allowed retry).
144
+ */
145
+ executeWithRetry(
146
+ first: DispatchResultLike,
147
+ redispatch: () => Promise<DispatchResultLike>,
148
+ ): Promise<{
149
+ result: DispatchResultLike
150
+ classification: DispatchClassification
151
+ retried: boolean
152
+ escalate: boolean
153
+ }>
154
+ }
155
+
156
+ export interface ZeusRetryHelperOptions extends DispatchGuardOptions {
157
+ /**
158
+ * R1: per-error-type retry policy. When provided, error-type retries with
159
+ * exponential backoff apply (auth → 0, rate_limit → N, ...); when absent
160
+ * the legacy hard-cap-1 empty-result retry applies.
161
+ */
162
+ retryPolicy?: RetryPolicy
163
+ /** R1: provider cooldown tracker (shared across helpers for global cooldown). */
164
+ cooldown?: ProviderCooldownTracker
165
+ /** R1: provider name for cooldown tracking (default "default"). */
166
+ provider?: string
167
+ /** R1: injectable sleep for tests (default: setTimeout promise). */
168
+ sleep?: (ms: number) => Promise<void>
169
+ }
170
+
171
+ export function createZeusRetryHelper(options?: ZeusRetryHelperOptions): ZeusRetryHelper {
172
+ const guard = createDispatchGuard(options)
173
+ const engine =
174
+ options?.retryPolicy !== undefined
175
+ ? new RetryPolicyEngine(options.retryPolicy, options.cooldown)
176
+ : undefined
177
+ const provider = options?.provider ?? 'default'
178
+ const sleep =
179
+ options?.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)))
180
+ return {
181
+ guard,
182
+ async executeWithRetry(first, redispatch) {
183
+ if (engine === undefined) {
184
+ // Legacy path (unchanged): hard cap 1 on empty results.
185
+ const out = await guard.maybeRetry(first, redispatch)
186
+ const escalate = out.classification !== 'content'
187
+ return {
188
+ result: out.result,
189
+ classification: out.classification,
190
+ retried: out.retried,
191
+ escalate,
192
+ }
193
+ }
194
+ // R1 path: classify the failure, apply the per-error-type policy with
195
+ // exponential backoff, and track provider cooldown. A content result
196
+ // records success (resets the provider's failure counter); an
197
+ // exhausted budget records failure (feeds the cooldown tracker).
198
+ let result = first
199
+ let attemptsUsed = 0
200
+ let retried = false
201
+ let classification = guard.classifyResult(result)
202
+ let lastError: unknown =
203
+ result.error ??
204
+ (classification === 'content' ? undefined : new Error('empty dispatch result'))
205
+ for (;;) {
206
+ if (classification === 'content') {
207
+ engine.recordSuccess(provider)
208
+ return { result, classification, retried, escalate: false }
209
+ }
210
+ lastError = result.error ?? lastError
211
+ const decision = engine.decide(provider, lastError, attemptsUsed)
212
+ if (!decision.shouldRetry) {
213
+ engine.recordFailure(provider, lastError)
214
+ return { result, classification, retried, escalate: true }
215
+ }
216
+ retried = true
217
+ attemptsUsed += 1
218
+ await sleep(decision.delayMs)
219
+ try {
220
+ result = await redispatch()
221
+ lastError = undefined
222
+ } catch (err: unknown) {
223
+ lastError = err
224
+ }
225
+ classification = guard.classifyResult(result)
226
+ }
227
+ },
228
+ }
229
+ }
230
+
231
+ // ─── High-level helper — full delegate → wait → read → retry → escalate ───
232
+
233
+ /**
234
+ * High-level helper Zeus waves MUST use (see `src/agents/zeus.md`).
235
+ * Delegates a background agent as a child session, waits for terminal,
236
+ * classifies via `createDispatchGuard`, retries ONCE on empty-mode1/mode2,
237
+ * and escalates (throws `ZeusEscalationError`) if still empty.
238
+ *
239
+ * Not wired automatically in the plugin — opencode 1.18.x cannot intercept
240
+ * task completion via hooks, so wiring is manual-orchestration only.
241
+ */
242
+ export async function zeusDelegateWithRetry(
243
+ opts: ZeusDelegateWithRetryOptions,
244
+ ): Promise<ZeusDelegateResult> {
245
+ const logger = opts.logger ?? log
246
+ const warn = logger.warn.bind(logger)
247
+ const board = opts.board
248
+ const client = opts.client
249
+ const sessionID = opts.sessionID
250
+
251
+ const sessionPath = safeSessionPath(sessionID)
252
+ if (!sessionPath) {
253
+ warn(`zeusDelegateWithRetry: invalid sessionID "${sessionID}" — aborting`)
254
+ throw new Error(`invalid sessionID: ${sessionID}`)
255
+ }
256
+
257
+ const readTimeoutMs = opts.readTimeoutMs ?? DELEGATION_DEFAULTS.readTimeoutMs
258
+ const outputDir = opts.outputDir ?? DELEGATION_DEFAULTS.outputDir
259
+ const retryHelper = createZeusRetryHelper({
260
+ retryOnEmpty: opts.retryOnEmpty ?? true,
261
+ logger,
262
+ ...(opts.retryPolicy !== undefined ? { retryPolicy: opts.retryPolicy } : {}),
263
+ ...(opts.cooldown !== undefined ? { cooldown: opts.cooldown } : {}),
264
+ ...(opts.provider !== undefined ? { provider: opts.provider } : {}),
265
+ })
266
+
267
+ // One delegate+read attempt. Factored so maybeRetry can invoke a second
268
+ // attempt with a fresh child session + board job.
269
+ async function runOnce(): Promise<{
270
+ alias: string
271
+ taskID: string
272
+ report: string
273
+ dispatchResult: DispatchResultLike
274
+ }> {
275
+ // 1. Create child session (parentID = caller). When a model is resolved,
276
+ // the same model is forwarded to session.create and promptAsync; otherwise
277
+ // both requests omit it so the host keeps native model inheritance.
278
+ let childID: string
279
+ try {
280
+ const created = await client.session.create({
281
+ body: {
282
+ parentID: sessionID,
283
+ title: opts.description ?? opts.prompt.slice(0, 80),
284
+ ...(opts.model !== undefined ? { model: opts.model } : {}),
285
+ },
286
+ })
287
+ childID = created.id
288
+ } catch (err: unknown) {
289
+ const reason = err instanceof Error ? err.message : String(err)
290
+ warn(`zeusDelegateWithRetry: session.create failed: ${reason}`)
291
+ throw new Error(`delegate session.create failed: ${reason}`)
292
+ }
293
+
294
+ const childPath = safeSessionPath(childID)
295
+ if (!childPath) {
296
+ warn(`zeusDelegateWithRetry: child sessionID invalid "${childID}"`)
297
+ throw new Error(`invalid child sessionID: ${childID}`)
298
+ }
299
+
300
+ // 2. Register on board (taskID == child session id)
301
+ const job = await board.registerLaunch({
302
+ taskID: childID,
303
+ parentSessionID: sessionID,
304
+ agent: opts.agent,
305
+ description: opts.description ?? opts.prompt,
306
+ objective: opts.prompt,
307
+ })
308
+
309
+ // 3. Fire-and-forget promptAsync on the child. Completion is observed via
310
+ // board.waitForTerminal, not via noReply — the spike refuted noReply.
311
+ // A promptAsync failure is logged but does not abort the wait — the board
312
+ // timeout will finalize the job.
313
+ const promptBody = {
314
+ agent: opts.agent,
315
+ ...(opts.model !== undefined ? { model: opts.model } : {}),
316
+ parts: [{ type: 'text' as const, text: opts.prompt }],
317
+ }
318
+ void client.session
319
+ .promptAsync({
320
+ path: childPath.path,
321
+ body: promptBody,
322
+ })
323
+ .catch((err: unknown) => {
324
+ const reason = err instanceof Error ? err.message : String(err)
325
+ warn(`zeusDelegateWithRetry: promptAsync failed for ${childID}: ${reason}`)
326
+ })
327
+
328
+ // 4. Wait for terminal state
329
+ let terminal: Awaited<ReturnType<BackgroundJobBoard['waitForTerminal']>>
330
+ try {
331
+ terminal = await board.waitForTerminal(childID, readTimeoutMs)
332
+ } catch {
333
+ // Timed out waiting — treat as empty so the guard retries once
334
+ return {
335
+ alias: job.alias,
336
+ taskID: childID,
337
+ report: '',
338
+ dispatchResult: { content: '' },
339
+ }
340
+ }
341
+
342
+ // 5. Read report markdown
343
+ const report = (await readDelegationReport(outputDir, terminal)) ?? ''
344
+
345
+ // DispatchResultLike for classification: content = report markdown.
346
+ // Tokens are unavailable from the board/report path → mode1 when empty.
347
+ const dispatchResult: DispatchResultLike = { content: report }
348
+
349
+ return { alias: job.alias, taskID: childID, report, dispatchResult }
350
+ }
351
+
352
+ // First attempt
353
+ const first = await runOnce()
354
+
355
+ // Classify + maybe retry once — capture second run's metadata via closure
356
+ let secondRun: typeof first | undefined
357
+ const out = await retryHelper.executeWithRetry(first.dispatchResult, async () => {
358
+ const second = await runOnce()
359
+ secondRun = second
360
+ return second.dispatchResult
361
+ })
362
+
363
+ // When retried, return second run's report or escalate
364
+ if (out.retried && secondRun !== undefined) {
365
+ if (out.classification === 'content') {
366
+ return {
367
+ alias: secondRun.alias,
368
+ taskID: secondRun.taskID,
369
+ report: secondRun.report,
370
+ classification: out.classification,
371
+ retried: true,
372
+ }
373
+ }
374
+ // Still empty after retry → escalate
375
+ throw new ZeusEscalationError(opts.agent, opts.prompt, out.classification)
376
+ }
377
+
378
+ // No retry, or first was content
379
+ if (out.classification !== 'content') {
380
+ // Empty and retry budget exhausted (or retryOnEmpty:false) → escalate
381
+ // But if we already retried and failed we threw above; this path is for
382
+ // the case where maybeRetry did NOT retry (budget exhausted or disabled)
383
+ // and the first result was empty. Per spec, one retry is allowed — if
384
+ // maybeRetry didn't retry, we should NOT auto-escalate here unless the
385
+ // guard's budget was used. Instead, treat as final empty that still
386
+ // requires escalation when retried==false but classification empty and
387
+ // retryOnEmpty was true but guard had no budget left? The guard already
388
+ // enforces cap 1, so after one retry escalate is required. For the
389
+ // first-attempt-empty with no retry (retryOnEmpty false), we still escalate
390
+ // so the wave knows to handle it.
391
+ if (out.retried) {
392
+ throw new ZeusEscalationError(opts.agent, opts.prompt, out.classification)
393
+ }
394
+ // No retry happened — still empty. Escalate so the wave doesn't silently
395
+ // use an empty report.
396
+ throw new ZeusEscalationError(opts.agent, opts.prompt, out.classification)
397
+ }
398
+
399
+ return {
400
+ alias: first.alias,
401
+ taskID: first.taskID,
402
+ report: first.report,
403
+ classification: out.classification,
404
+ retried: out.retried,
405
+ }
406
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Pantheon V2 plugin — directory entry for the OpenCode V2 beta loader.
3
+ *
4
+ * Loader contract (proven by sandbox probes in ~/pantheon-sandbox/v2):
5
+ * the beta only accepts `plugins` entries that are DIRECTORIES containing a
6
+ * real index.js/index.ts — a file path warns "must be a directory" and a
7
+ * symlink is ignored. The installer therefore registers this directory
8
+ * (`<pkg>/src/plugin-v2`), never the `src/plugin-v2.ts` file directly.
9
+ *
10
+ * This index is a thin re-export shim over the real entry at
11
+ * `src/plugin-v2.ts` (kept as the single source of truth so the
12
+ * `./plugin-v2` package export, unit tests, and V1 tooling keep working
13
+ * unchanged). The shim shape mirrors the verified probe:
14
+ * `export { default } from '<installed>/src/plugin-v2.ts'` loads with zero
15
+ * errors and exposes the full orchestration plugin (tools, events, hooks).
16
+ *
17
+ * @module plugin-v2/index
18
+ */
19
+
20
+ export * from '../plugin-v2.ts'
21
+ export { default } from '../plugin-v2.ts'