@mrc2204/opencode-bridge 0.1.1 → 0.1.3
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.en.md +73 -94
- package/README.md +73 -94
- package/dist/opencode-plugin/openclaw-bridge-callback.d.ts +5 -0
- package/dist/opencode-plugin/openclaw-bridge-callback.js +179 -0
- package/dist/src/chunk-OVQ5X54C.js +289 -0
- package/dist/src/chunk-TDVN5AFB.js +36 -0
- package/dist/{index.js → src/index.js} +605 -22
- package/dist/src/observability.d.ts +97 -0
- package/dist/{observability.js → src/observability.js} +3 -1
- package/dist/src/shared-contracts.d.ts +33 -0
- package/dist/src/shared-contracts.js +10 -0
- package/openclaw.plugin.json +2 -2
- package/opencode-plugin/README.md +25 -0
- package/opencode-plugin/openclaw-bridge-callback.ts +186 -0
- package/package.json +17 -9
- package/scripts/install-bridge.mjs +60 -0
- package/scripts/materialize-opencode-plugin.mjs +75 -0
- package/skills/opencode-orchestration/SKILL.md +108 -66
- package/src/shared-contracts.ts +58 -0
- package/dist/chunk-6NIQKNRA.js +0 -176
- package/dist/observability.d.ts +0 -52
- /package/dist/{index.d.ts → src/index.d.ts} +0 -0
|
@@ -280,71 +280,81 @@ Escalate to orchestrator (`scrum`/owner session) when:
|
|
|
280
280
|
|
|
281
281
|
---
|
|
282
282
|
|
|
283
|
-
##
|
|
283
|
+
## Execution strategy (current standard)
|
|
284
284
|
|
|
285
|
-
|
|
285
|
+
Use a **hybrid execution model**:
|
|
286
286
|
|
|
287
|
-
-
|
|
287
|
+
- **CLI-direct** for lightweight one-shot execution
|
|
288
|
+
- **serve/plugin mode** for canonical callback, event-driven lifecycle, observability, and multi-project-safe control plane
|
|
288
289
|
|
|
289
|
-
|
|
290
|
+
Rules:
|
|
290
291
|
|
|
291
|
-
-
|
|
292
|
-
-
|
|
293
|
-
-
|
|
294
|
-
- `invocation.command/rendered`
|
|
295
|
-
- `opencode.agent/model`
|
|
296
|
-
- `process.exit_code`
|
|
297
|
-
- `result.status/error`
|
|
298
|
-
- `evidence.git_commit`
|
|
292
|
+
- Do not assume `serve` is required for every coding task.
|
|
293
|
+
- Do not assume `CLI-direct` is sufficient when callback/lifecycle tracking matters.
|
|
294
|
+
- Choose the lane deliberately and report which lane was used.
|
|
299
295
|
|
|
300
|
-
|
|
296
|
+
### Prefer CLI-direct when
|
|
297
|
+
- the task is lightweight or one-shot
|
|
298
|
+
- no callback or long-lived lifecycle tracking is required
|
|
299
|
+
- no serve/session registry management is needed
|
|
301
300
|
|
|
302
|
-
|
|
301
|
+
### Prefer serve/plugin mode when
|
|
302
|
+
- callback correctness matters
|
|
303
|
+
- event-driven lifecycle handling is required
|
|
304
|
+
- multi-project safety matters
|
|
305
|
+
- observability/session/event introspection is required
|
|
306
|
+
- multiple tasks may reuse the same project-bound runtime
|
|
303
307
|
|
|
304
308
|
## OpenCode Bridge usage (current team standard)
|
|
305
309
|
|
|
306
310
|
Current team workflow after planning is:
|
|
307
311
|
|
|
308
312
|
1. `using-superpowers`
|
|
309
|
-
2. `brainstorming` (
|
|
313
|
+
2. `brainstorming` (when design/spec clarification is needed)
|
|
310
314
|
3. `writing-plans`
|
|
311
315
|
4. `execute`
|
|
312
316
|
5. `verification-before-completion`
|
|
313
317
|
|
|
314
|
-
|
|
318
|
+
During **execute**, if routing goes into the OpenCode lane, use the bridge-aware strategy and choose one of these execution lanes explicitly:
|
|
319
|
+
|
|
320
|
+
### Lane A — CLI-direct
|
|
321
|
+
Use for lightweight tasks where callback/lifecycle tracking is not the main requirement.
|
|
322
|
+
|
|
323
|
+
Rules:
|
|
324
|
+
- bind the repo explicitly with `--dir <absolute-repo-path>`
|
|
325
|
+
- prefer explicit `--model` if agent resolution is uncertain
|
|
326
|
+
- report clearly that the task used CLI-direct execution
|
|
327
|
+
|
|
328
|
+
### Lane B — serve/plugin mode (canonical callback lane)
|
|
329
|
+
Use when callback correctness, observability, event-driven lifecycle handling, or multi-project-safe control plane is required.
|
|
330
|
+
|
|
331
|
+
Rules:
|
|
332
|
+
- bind execution to one project-bound serve instance only
|
|
333
|
+
- keep routing envelope fields explicit
|
|
334
|
+
- treat `/hooks/agent` as the primary callback path
|
|
335
|
+
- use OpenCode-side plugin callback for terminal lifecycle signaling
|
|
315
336
|
|
|
316
337
|
### Current implementation status (important)
|
|
317
|
-
|
|
338
|
+
The bridge stack has already proven these capabilities:
|
|
318
339
|
- routing envelope build
|
|
319
340
|
- callback payload build
|
|
320
|
-
- callback execution
|
|
321
|
-
- status artifact persistence
|
|
322
|
-
-
|
|
323
|
-
-
|
|
324
|
-
|
|
325
|
-
### How to think about execution now
|
|
326
|
-
- **Do not** assume one shared `opencode serve` is safe for many projects.
|
|
327
|
-
- **Do** bind every coding task to:
|
|
328
|
-
- `project_id`
|
|
329
|
-
- `repo_root`
|
|
330
|
-
- `opencode_server_url`
|
|
331
|
-
- `task_id`
|
|
332
|
-
- `run_id`
|
|
333
|
-
- `agent_id`
|
|
334
|
-
- `session_key`
|
|
335
|
-
- **Do** treat `/hooks/agent` as callback primary.
|
|
336
|
-
- **Do not** use `cron` or `group:sessions` as the callback mechanism.
|
|
341
|
+
- callback execution to `/hooks/agent`
|
|
342
|
+
- run-status artifact persistence
|
|
343
|
+
- serve binding fail-closed by `repo_root`
|
|
344
|
+
- OpenCode-side plugin callback path with `session.idle` as canonical trigger
|
|
345
|
+
- materialized OpenCode-side plugin artifact and install flow
|
|
337
346
|
|
|
338
347
|
### Practical guidance for agents
|
|
339
348
|
When handing off into OpenCode execution:
|
|
340
349
|
- mention the intended repo explicitly
|
|
341
350
|
- ensure the execution packet is file-driven
|
|
342
|
-
- ensure
|
|
351
|
+
- ensure repo binding is explicit (`--dir <repo>` for CLI-direct, project-bound serve for serve/plugin mode)
|
|
343
352
|
- prefer bridge-aware execution over free-form `opencode run` whenever the flow needs:
|
|
344
353
|
- callback
|
|
345
354
|
- task/run tracking
|
|
346
355
|
- serve/session registry
|
|
347
356
|
- multi-agent lane routing
|
|
357
|
+
- event-driven terminal signaling
|
|
348
358
|
|
|
349
359
|
### Mandatory reporting after execution handoff
|
|
350
360
|
Outer agents should report:
|
|
@@ -500,6 +510,38 @@ Agents must describe these limits honestly and avoid over-claiming completion.
|
|
|
500
510
|
|
|
501
511
|
### Execution lane assumptions (MANDATORY)
|
|
502
512
|
|
|
513
|
+
### Current-session callback integrity (MANDATORY)
|
|
514
|
+
|
|
515
|
+
Bridge-aware execution must preserve caller identity and callback destination explicitly.
|
|
516
|
+
|
|
517
|
+
Required fields to preserve whenever available:
|
|
518
|
+
- `requested_agent_id`
|
|
519
|
+
- `resolved_agent_id`
|
|
520
|
+
- `origin_session_key`
|
|
521
|
+
- `origin_session_id`
|
|
522
|
+
- `callback_target_session_key`
|
|
523
|
+
- `callback_target_session_id`
|
|
524
|
+
|
|
525
|
+
Operational rules:
|
|
526
|
+
- Callback must return to the **current caller session**, not an arbitrary latest/execution session.
|
|
527
|
+
- If bridge/runtime cannot resolve the requested execution agent cleanly, it must **fail fast** instead of silently falling back to a default agent.
|
|
528
|
+
- If callback target session information is missing or inconsistent, treat that as a routing integrity error, not a soft warning.
|
|
529
|
+
- Reporting/trace should always expose:
|
|
530
|
+
- requested agent
|
|
531
|
+
- resolved execution agent
|
|
532
|
+
- callback target session key/id
|
|
533
|
+
- whether fallback was used
|
|
534
|
+
|
|
535
|
+
### Runtime hygiene after completion (MANDATORY)
|
|
536
|
+
|
|
537
|
+
When a task or epic is marked `done/closed`, any OpenCode serve/process that was started only for that execution must be shut down immediately unless there is an explicit reason to keep it alive.
|
|
538
|
+
|
|
539
|
+
Operational rules:
|
|
540
|
+
- Do not leave lingering OpenCode serves after work completes.
|
|
541
|
+
- Treat serve shutdown as part of completion hygiene, not optional cleanup.
|
|
542
|
+
- If a serve remains alive intentionally, the agent must report the reason explicitly.
|
|
543
|
+
|
|
544
|
+
|
|
503
545
|
1. **One project = one OpenCode serve instance**
|
|
504
546
|
- Do not assume one shared serve is safe for multiple repos.
|
|
505
547
|
- Always bind execution to a single project/repo root.
|
|
@@ -552,25 +594,22 @@ Use when you need to resolve which OpenCode serve should be used for a given:
|
|
|
552
594
|
#### `opencode_build_envelope`
|
|
553
595
|
Use when you are about to delegate a concrete task into OpenCode lane and need the canonical routing envelope.
|
|
554
596
|
|
|
555
|
-
#### `
|
|
556
|
-
Use
|
|
597
|
+
#### `opencode_execute_task`
|
|
598
|
+
Use as the standard serve/plugin-mode execution entrypoint:
|
|
599
|
+
- resolve/spawn project-bound serve
|
|
600
|
+
- create session
|
|
601
|
+
- send prompt async
|
|
602
|
+
- start watcher path
|
|
603
|
+
- persist run artifact
|
|
557
604
|
|
|
558
|
-
#### `
|
|
559
|
-
Use
|
|
560
|
-
|
|
561
|
-
#### `opencode_probe_sse`
|
|
562
|
-
Use when verifying that OpenCode serve is alive and emitting SSE events.
|
|
605
|
+
#### `opencode_run_status`
|
|
606
|
+
Use to inspect run state and event-derived lifecycle summary.
|
|
563
607
|
|
|
564
|
-
#### `
|
|
565
|
-
Use
|
|
566
|
-
- read an event
|
|
567
|
-
- normalize it
|
|
568
|
-
- callback
|
|
569
|
-
- write artifact
|
|
608
|
+
#### `opencode_run_events`
|
|
609
|
+
Use to inspect normalized SSE event output for a run/session.
|
|
570
610
|
|
|
571
|
-
#### `
|
|
572
|
-
Use
|
|
573
|
-
Treat it as baseline runtime manager logic, not a production-perfect daemon.
|
|
611
|
+
#### `opencode_session_tail`
|
|
612
|
+
Use to inspect session messages and diff/tail evidence when available.
|
|
574
613
|
|
|
575
614
|
#### `opencode_run_status`
|
|
576
615
|
Use to inspect the artifact state of a previously handled run.
|
|
@@ -610,21 +649,23 @@ Use to mark a serve as stopped and send shutdown for a project serve entry.
|
|
|
610
649
|
When a task must enter OpenCode execution lane, use this order:
|
|
611
650
|
|
|
612
651
|
1. Prepare/verify execution packet via the outer workflow (`using-superpowers` → `brainstorming` if needed → `writing-plans`)
|
|
613
|
-
2.
|
|
614
|
-
-
|
|
652
|
+
2. Classify execution shape:
|
|
653
|
+
- CLI-direct
|
|
654
|
+
- serve/plugin mode
|
|
655
|
+
3. If using **CLI-direct**:
|
|
656
|
+
- run with explicit `--dir <repo>`
|
|
657
|
+
- prefer explicit `--model` if needed
|
|
658
|
+
- report no callback expectation unless a separate tracking path is in place
|
|
659
|
+
4. If using **serve/plugin mode**:
|
|
660
|
+
- resolve project/server via `opencode_resolve_project`
|
|
615
661
|
- or spawn one via `opencode_serve_spawn`
|
|
616
|
-
|
|
617
|
-
- `
|
|
618
|
-
|
|
619
|
-
- `opencode_probe_sse`
|
|
620
|
-
- `opencode_listen_once`
|
|
621
|
-
5. Build and/or execute callbacks:
|
|
622
|
-
- `opencode_build_callback`
|
|
623
|
-
- `opencode_execute_callback`
|
|
624
|
-
- or `opencode_callback_from_event`
|
|
625
|
-
6. Check run artifact/status:
|
|
662
|
+
- build routing envelope via `opencode_build_envelope`
|
|
663
|
+
- keep callback expectation explicit through `/hooks/agent`
|
|
664
|
+
5. For serve/plugin mode, inspect artifacts as needed:
|
|
626
665
|
- `opencode_run_status`
|
|
627
|
-
|
|
666
|
+
- `opencode_run_events`
|
|
667
|
+
- `opencode_session_tail`
|
|
668
|
+
6. Only then proceed to outer verification:
|
|
628
669
|
- `verification-before-completion`
|
|
629
670
|
|
|
630
671
|
### Reporting requirements
|
|
@@ -640,14 +681,15 @@ When handing work into OpenCode lane, the outer agent should report:
|
|
|
640
681
|
#### Do
|
|
641
682
|
- Do keep OpenCode execution project-bound.
|
|
642
683
|
- Do keep callback/session routing explicit.
|
|
684
|
+
- Do choose execution lane explicitly: CLI-direct vs serve/plugin mode.
|
|
643
685
|
- Do use `opencode-orchestration` when coordination or bridge semantics matter.
|
|
644
686
|
- Do use `verification-before-completion` before claiming completion.
|
|
645
687
|
|
|
646
688
|
#### Don’t
|
|
647
|
-
- Don’t use ad-hoc `opencode run`
|
|
689
|
+
- Don’t use ad-hoc `opencode run` for callback/lifecycle-sensitive work without explicit lane selection.
|
|
648
690
|
- Don’t assume a single serve is multi-project-safe.
|
|
649
691
|
- Don’t assume bridge/runtime-manager features are production-perfect without verification.
|
|
650
|
-
- Don’t over-claim that the bridge is fully autonomous when only
|
|
692
|
+
- Don’t over-claim that the bridge is fully autonomous when only functionally verified/hardened behavior is available.
|
|
651
693
|
|
|
652
694
|
## Guardrails
|
|
653
695
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export type BridgeSessionTagFields = {
|
|
2
|
+
runId: string;
|
|
3
|
+
taskId: string;
|
|
4
|
+
requested: string;
|
|
5
|
+
resolved: string;
|
|
6
|
+
callbackSession: string;
|
|
7
|
+
callbackSessionId?: string;
|
|
8
|
+
projectId?: string;
|
|
9
|
+
repoRoot?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type OpenCodePluginCallbackAuditRecord = {
|
|
13
|
+
phase?: string;
|
|
14
|
+
event_type?: string;
|
|
15
|
+
session_id?: string;
|
|
16
|
+
title?: string;
|
|
17
|
+
tags?: Record<string, string> | null;
|
|
18
|
+
dedupeKey?: string;
|
|
19
|
+
ok?: boolean;
|
|
20
|
+
status?: number;
|
|
21
|
+
reason?: string;
|
|
22
|
+
body?: string;
|
|
23
|
+
payload?: any;
|
|
24
|
+
raw?: any;
|
|
25
|
+
created_at: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function buildTaggedSessionTitle(fields: BridgeSessionTagFields): string {
|
|
29
|
+
return [
|
|
30
|
+
`${fields.taskId}`,
|
|
31
|
+
`runId=${fields.runId}`,
|
|
32
|
+
`taskId=${fields.taskId}`,
|
|
33
|
+
`requested=${fields.requested}`,
|
|
34
|
+
`resolved=${fields.resolved}`,
|
|
35
|
+
`callbackSession=${fields.callbackSession}`,
|
|
36
|
+
...(fields.callbackSessionId ? [`callbackSessionId=${fields.callbackSessionId}`] : []),
|
|
37
|
+
...(fields.projectId ? [`projectId=${fields.projectId}`] : []),
|
|
38
|
+
...(fields.repoRoot ? [`repoRoot=${fields.repoRoot}`] : []),
|
|
39
|
+
].join(" ");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function parseTaggedSessionTitle(title?: string) {
|
|
43
|
+
if (!title || !title.trim()) return null;
|
|
44
|
+
const tags: Record<string, string> = {};
|
|
45
|
+
for (const token of title.split(/\s+/)) {
|
|
46
|
+
const idx = token.indexOf("=");
|
|
47
|
+
if (idx <= 0) continue;
|
|
48
|
+
const key = token.slice(0, idx).trim();
|
|
49
|
+
const raw = token.slice(idx + 1).trim();
|
|
50
|
+
if (!key || !raw) continue;
|
|
51
|
+
tags[key] = raw;
|
|
52
|
+
}
|
|
53
|
+
return Object.keys(tags).length > 0 ? tags : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function buildPluginCallbackDedupeKey(input: { sessionId?: string; runId?: string }) {
|
|
57
|
+
return `${input.sessionId || "no-session"}|${input.runId || "no-run"}`;
|
|
58
|
+
}
|
package/dist/chunk-6NIQKNRA.js
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
// src/observability.ts
|
|
2
|
-
function parseSseFramesFromBuffer(input) {
|
|
3
|
-
const normalized = input.replace(/\r\n/g, "\n");
|
|
4
|
-
const parts = normalized.split("\n\n");
|
|
5
|
-
const remainder = parts.pop() ?? "";
|
|
6
|
-
const frames = [];
|
|
7
|
-
for (const rawFrame of parts) {
|
|
8
|
-
const lines = rawFrame.split("\n");
|
|
9
|
-
let event;
|
|
10
|
-
let id;
|
|
11
|
-
let retry;
|
|
12
|
-
const dataLines = [];
|
|
13
|
-
for (const line of lines) {
|
|
14
|
-
if (!line || line.startsWith(":")) continue;
|
|
15
|
-
const idx = line.indexOf(":");
|
|
16
|
-
const field = idx >= 0 ? line.slice(0, idx).trim() : line.trim();
|
|
17
|
-
const value = idx >= 0 ? line.slice(idx + 1).replace(/^\s/, "") : "";
|
|
18
|
-
if (field === "event") event = value;
|
|
19
|
-
else if (field === "id") id = value;
|
|
20
|
-
else if (field === "retry") {
|
|
21
|
-
const n = Number(value);
|
|
22
|
-
if (Number.isFinite(n)) retry = n;
|
|
23
|
-
} else if (field === "data") dataLines.push(value);
|
|
24
|
-
}
|
|
25
|
-
if (dataLines.length <= 0) continue;
|
|
26
|
-
frames.push({
|
|
27
|
-
event,
|
|
28
|
-
id,
|
|
29
|
-
retry,
|
|
30
|
-
data: dataLines.join("\n"),
|
|
31
|
-
raw: rawFrame
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
return { frames, remainder };
|
|
35
|
-
}
|
|
36
|
-
function parseSseData(data) {
|
|
37
|
-
const trimmed = data.trim();
|
|
38
|
-
if (!trimmed) return null;
|
|
39
|
-
try {
|
|
40
|
-
return JSON.parse(trimmed);
|
|
41
|
-
} catch {
|
|
42
|
-
return { raw: trimmed };
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function unwrapGlobalPayload(raw) {
|
|
46
|
-
let current = raw;
|
|
47
|
-
const wrappers = [];
|
|
48
|
-
for (let i = 0; i < 3; i += 1) {
|
|
49
|
-
if (!current || typeof current !== "object") break;
|
|
50
|
-
if ("payload" in current && current.payload !== void 0) {
|
|
51
|
-
wrappers.push("payload");
|
|
52
|
-
current = current.payload;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if ("data" in current && current.data !== void 0 && Object.keys(current).length <= 4) {
|
|
56
|
-
wrappers.push("data");
|
|
57
|
-
current = current.data;
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
return { payload: current, wrappers };
|
|
63
|
-
}
|
|
64
|
-
function pickFirstString(obj, keys) {
|
|
65
|
-
if (!obj || typeof obj !== "object") return void 0;
|
|
66
|
-
for (const key of keys) {
|
|
67
|
-
const value = obj[key];
|
|
68
|
-
if (typeof value === "string" && value.trim()) return value.trim();
|
|
69
|
-
}
|
|
70
|
-
return void 0;
|
|
71
|
-
}
|
|
72
|
-
function normalizeOpenCodeEvent(raw) {
|
|
73
|
-
const text = JSON.stringify(raw || {}).toLowerCase();
|
|
74
|
-
if (text.includes("permission") || text.includes("question.asked")) {
|
|
75
|
-
return { kind: "permission.requested", summary: "OpenCode \u0111ang ch\u1EDD permission/user input", raw };
|
|
76
|
-
}
|
|
77
|
-
if (text.includes("error") || text.includes("failed")) {
|
|
78
|
-
return { kind: "task.failed", summary: "OpenCode task failed", raw };
|
|
79
|
-
}
|
|
80
|
-
if (text.includes("stalled")) {
|
|
81
|
-
return { kind: "task.stalled", summary: "OpenCode task stalled", raw };
|
|
82
|
-
}
|
|
83
|
-
if (text.includes("complete") || text.includes("completed") || text.includes("done")) {
|
|
84
|
-
return { kind: "task.completed", summary: "OpenCode task completed", raw };
|
|
85
|
-
}
|
|
86
|
-
if (text.includes("progress") || text.includes("delta") || text.includes("assistant")) {
|
|
87
|
-
return { kind: "task.progress", summary: "OpenCode task made progress", raw };
|
|
88
|
-
}
|
|
89
|
-
if (text.includes("start") || text.includes("created") || text.includes("connected")) {
|
|
90
|
-
return { kind: "task.started", summary: "OpenCode task/server started", raw };
|
|
91
|
-
}
|
|
92
|
-
return { kind: null, raw };
|
|
93
|
-
}
|
|
94
|
-
function normalizeTypedEventV1(frame, scope) {
|
|
95
|
-
const parsed = parseSseData(frame.data);
|
|
96
|
-
const unwrapped = unwrapGlobalPayload(parsed);
|
|
97
|
-
const normalized = normalizeOpenCodeEvent(unwrapped.payload);
|
|
98
|
-
return {
|
|
99
|
-
schema: "opencode.event.v1",
|
|
100
|
-
scope,
|
|
101
|
-
eventName: frame.event,
|
|
102
|
-
eventId: frame.id,
|
|
103
|
-
kind: normalized.kind,
|
|
104
|
-
summary: normalized.summary,
|
|
105
|
-
runId: pickFirstString(unwrapped.payload, ["run_id", "runId"]),
|
|
106
|
-
taskId: pickFirstString(unwrapped.payload, ["task_id", "taskId"]),
|
|
107
|
-
sessionId: pickFirstString(unwrapped.payload, ["session_id", "sessionId", "session"]),
|
|
108
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
109
|
-
wrappers: unwrapped.wrappers,
|
|
110
|
-
payload: unwrapped.payload
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
function asString(value) {
|
|
114
|
-
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
115
|
-
}
|
|
116
|
-
function scoreSessionCandidate(candidate, ctx) {
|
|
117
|
-
const id = asString(candidate?.id) || "";
|
|
118
|
-
const candidateText = JSON.stringify(candidate || {}).toLowerCase();
|
|
119
|
-
const runId = ctx.runId?.toLowerCase();
|
|
120
|
-
const taskId = ctx.taskId?.toLowerCase();
|
|
121
|
-
const sessionKey = ctx.sessionKey?.toLowerCase();
|
|
122
|
-
const artifactSessionId = ctx.artifactSessionId;
|
|
123
|
-
let score = 0;
|
|
124
|
-
if (artifactSessionId && id === artifactSessionId) score += 1e3;
|
|
125
|
-
if (runId && candidateText.includes(runId)) score += 120;
|
|
126
|
-
if (taskId && candidateText.includes(taskId)) score += 70;
|
|
127
|
-
if (sessionKey && candidateText.includes(sessionKey)) score += 60;
|
|
128
|
-
if (runId && id.toLowerCase().includes(runId)) score += 30;
|
|
129
|
-
if (taskId && id.toLowerCase().includes(taskId)) score += 20;
|
|
130
|
-
score += Math.max(0, 20 - ctx.recencyRank);
|
|
131
|
-
return score;
|
|
132
|
-
}
|
|
133
|
-
function resolveSessionId(input) {
|
|
134
|
-
if (input.explicitSessionId) {
|
|
135
|
-
return { sessionId: input.explicitSessionId, strategy: "explicit", score: 9999 };
|
|
136
|
-
}
|
|
137
|
-
const list = Array.isArray(input.sessionList) ? input.sessionList : [];
|
|
138
|
-
const withRecency = [...list].map((item, idx) => {
|
|
139
|
-
const updated = Number(item?.time?.updated || 0);
|
|
140
|
-
return { item, idx, updated };
|
|
141
|
-
}).sort((a, b) => b.updated - a.updated).map((x, rank) => ({ ...x, rank }));
|
|
142
|
-
if (input.artifactSessionId) {
|
|
143
|
-
const direct = withRecency.find((x) => asString(x.item?.id) === input.artifactSessionId);
|
|
144
|
-
if (direct) {
|
|
145
|
-
return { sessionId: input.artifactSessionId, strategy: "artifact", score: 1e3 };
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
let best = { score: -1 };
|
|
149
|
-
for (const candidate of withRecency) {
|
|
150
|
-
const id = asString(candidate.item?.id);
|
|
151
|
-
if (!id) continue;
|
|
152
|
-
const score = scoreSessionCandidate(candidate.item, {
|
|
153
|
-
runId: input.runId,
|
|
154
|
-
taskId: input.taskId,
|
|
155
|
-
sessionKey: input.sessionKey,
|
|
156
|
-
artifactSessionId: input.artifactSessionId,
|
|
157
|
-
recencyRank: candidate.rank
|
|
158
|
-
});
|
|
159
|
-
if (score > best.score) best = { id, score };
|
|
160
|
-
}
|
|
161
|
-
if (best.id && best.score > 0) {
|
|
162
|
-
return { sessionId: best.id, strategy: "scored_fallback", score: best.score };
|
|
163
|
-
}
|
|
164
|
-
const latest = withRecency.find((x) => asString(x.item?.id));
|
|
165
|
-
if (latest) return { sessionId: asString(latest.item?.id), strategy: "latest", score: 0 };
|
|
166
|
-
return { strategy: "none", score: -1 };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export {
|
|
170
|
-
parseSseFramesFromBuffer,
|
|
171
|
-
parseSseData,
|
|
172
|
-
unwrapGlobalPayload,
|
|
173
|
-
normalizeOpenCodeEvent,
|
|
174
|
-
normalizeTypedEventV1,
|
|
175
|
-
resolveSessionId
|
|
176
|
-
};
|
package/dist/observability.d.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
type OpenCodeEventKind = "task.started" | "task.progress" | "permission.requested" | "task.stalled" | "task.failed" | "task.completed";
|
|
2
|
-
type EventScope = "session" | "global";
|
|
3
|
-
type SseFrame = {
|
|
4
|
-
event?: string;
|
|
5
|
-
id?: string;
|
|
6
|
-
retry?: number;
|
|
7
|
-
data: string;
|
|
8
|
-
raw: string;
|
|
9
|
-
};
|
|
10
|
-
type TypedEventV1 = {
|
|
11
|
-
schema: "opencode.event.v1";
|
|
12
|
-
scope: EventScope;
|
|
13
|
-
eventName?: string;
|
|
14
|
-
eventId?: string;
|
|
15
|
-
kind: OpenCodeEventKind | null;
|
|
16
|
-
summary?: string;
|
|
17
|
-
runId?: string;
|
|
18
|
-
taskId?: string;
|
|
19
|
-
sessionId?: string;
|
|
20
|
-
timestamp: string;
|
|
21
|
-
wrappers: string[];
|
|
22
|
-
payload: any;
|
|
23
|
-
};
|
|
24
|
-
declare function parseSseFramesFromBuffer(input: string): {
|
|
25
|
-
frames: SseFrame[];
|
|
26
|
-
remainder: string;
|
|
27
|
-
};
|
|
28
|
-
declare function parseSseData(data: string): any;
|
|
29
|
-
declare function unwrapGlobalPayload(raw: any): {
|
|
30
|
-
payload: any;
|
|
31
|
-
wrappers: string[];
|
|
32
|
-
};
|
|
33
|
-
declare function normalizeOpenCodeEvent(raw: any): {
|
|
34
|
-
kind: OpenCodeEventKind | null;
|
|
35
|
-
summary?: string;
|
|
36
|
-
raw: any;
|
|
37
|
-
};
|
|
38
|
-
declare function normalizeTypedEventV1(frame: SseFrame, scope: EventScope): TypedEventV1;
|
|
39
|
-
declare function resolveSessionId(input: {
|
|
40
|
-
explicitSessionId?: string;
|
|
41
|
-
runId?: string;
|
|
42
|
-
taskId?: string;
|
|
43
|
-
sessionKey?: string;
|
|
44
|
-
artifactSessionId?: string;
|
|
45
|
-
sessionList?: any[];
|
|
46
|
-
}): {
|
|
47
|
-
sessionId?: string;
|
|
48
|
-
strategy: "explicit" | "artifact" | "scored_fallback" | "latest" | "none";
|
|
49
|
-
score?: number;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export { type EventScope, type OpenCodeEventKind, type SseFrame, type TypedEventV1, normalizeOpenCodeEvent, normalizeTypedEventV1, parseSseData, parseSseFramesFromBuffer, resolveSessionId, unwrapGlobalPayload };
|
|
File without changes
|