@agentdeck/shared 0.2.3 → 1.0.1
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/command-builders.d.ts +2 -1
- package/dist/command-builders.d.ts.map +1 -1
- package/dist/command-builders.js +3 -0
- package/dist/command-builders.js.map +1 -1
- package/dist/creature-layout.d.ts +18 -0
- package/dist/creature-layout.d.ts.map +1 -0
- package/dist/creature-layout.js +131 -0
- package/dist/creature-layout.js.map +1 -0
- package/dist/d200h-layout.d.ts +15 -0
- package/dist/d200h-layout.d.ts.map +1 -1
- package/dist/d200h-layout.js +222 -41
- package/dist/d200h-layout.js.map +1 -1
- package/dist/design-tokens.d.ts +2 -0
- package/dist/design-tokens.d.ts.map +1 -1
- package/dist/design-tokens.js +1 -0
- package/dist/design-tokens.js.map +1 -1
- package/dist/eval-schema.d.ts +7 -1
- package/dist/eval-schema.d.ts.map +1 -1
- package/dist/eval-schema.js.map +1 -1
- package/dist/format-utils.d.ts +13 -0
- package/dist/format-utils.d.ts.map +1 -1
- package/dist/format-utils.js +25 -0
- package/dist/format-utils.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +68 -5
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js.map +1 -1
- package/dist/svg-renderers/agent-logos.d.ts +4 -8
- package/dist/svg-renderers/agent-logos.d.ts.map +1 -1
- package/dist/svg-renderers/agent-logos.js +8 -38
- package/dist/svg-renderers/agent-logos.js.map +1 -1
- package/dist/svg-renderers/session-slot-renderer.d.ts +12 -0
- package/dist/svg-renderers/session-slot-renderer.d.ts.map +1 -1
- package/dist/svg-renderers/session-slot-renderer.js +32 -1
- package/dist/svg-renderers/session-slot-renderer.js.map +1 -1
- package/dist/terrarium-rules.d.ts +46 -0
- package/dist/terrarium-rules.d.ts.map +1 -0
- package/dist/terrarium-rules.js +63 -0
- package/dist/terrarium-rules.js.map +1 -0
- package/dist/timeline-icons.d.ts +15 -5
- package/dist/timeline-icons.d.ts.map +1 -1
- package/dist/timeline-icons.js +19 -6
- package/dist/timeline-icons.js.map +1 -1
- package/dist/timeline-task-display.d.ts +66 -0
- package/dist/timeline-task-display.d.ts.map +1 -0
- package/dist/timeline-task-display.js +94 -0
- package/dist/timeline-task-display.js.map +1 -0
- package/dist/timeline.d.ts +29 -0
- package/dist/timeline.d.ts.map +1 -1
- package/dist/timeline.js +66 -5
- package/dist/timeline.js.map +1 -1
- package/dist/trmnl-layout.d.ts.map +1 -0
- package/dist/trmnl-layout.js.map +1 -0
- package/package.json +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ResponseCommand, SelectOptionCommand, NavigateOptionCommand, SendPromptCommand, SwitchModeCommand, InterruptCommand, EscapeCommand, VoiceCommand, QueryUsageCommand, QuerySessionTimelineCommand, DiagCommand, UtilityCommand, SwitchAgentCommand, FocusSessionCommand, ClearSessionFocusCommand, SessionCommand, ClientRegisterCommand, ApmeVibeFeedbackCommand, ApmeRecommendCommand, PermissionDecisionCommand, Esp32OtaAckCommand, Esp32OtaErrorCommand } from "./protocol.js";
|
|
1
|
+
import type { ResponseCommand, SelectOptionCommand, NavigateOptionCommand, SendPromptCommand, SwitchModeCommand, InterruptCommand, EscapeCommand, VoiceCommand, QueryUsageCommand, QuerySessionTimelineCommand, DiagCommand, UtilityCommand, SwitchAgentCommand, FocusSessionCommand, ClearSessionFocusCommand, SessionCommand, ClientRegisterCommand, ApmeVibeFeedbackCommand, ApmeRecommendCommand, PermissionDecisionCommand, ReviewRunCommand, Esp32OtaAckCommand, Esp32OtaErrorCommand } from "./protocol.js";
|
|
2
2
|
export declare const agentCommand: {
|
|
3
3
|
readonly respond: (value: string) => ResponseCommand;
|
|
4
4
|
readonly selectOption: (index: number, sessionId?: string) => SelectOptionCommand;
|
|
@@ -29,6 +29,7 @@ export declare const agentCommand: {
|
|
|
29
29
|
readonly apmeVibe: (runId: string, verdict: "approve" | "reject" | "neutral", note?: string) => ApmeVibeFeedbackCommand;
|
|
30
30
|
readonly apmeRecommend: (taskKind?: string, budgetUsd?: number, latencyBudgetMs?: number, preferLocal?: boolean) => ApmeRecommendCommand;
|
|
31
31
|
readonly permissionDecision: (requestId: string, decision: "allow" | "deny") => PermissionDecisionCommand;
|
|
32
|
+
readonly reviewRun: (sessionId: string) => ReviewRunCommand;
|
|
32
33
|
readonly esp32OtaAck: (otaId: string, stage: "begin" | "chunk" | "end" | "abort", seq?: number, offset?: number, written?: number) => Esp32OtaAckCommand;
|
|
33
34
|
readonly esp32OtaError: (error: string, otaId?: string, stage?: string) => Esp32OtaErrorCommand;
|
|
34
35
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-builders.d.ts","sourceRoot":"","sources":["../src/command-builders.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,2BAA2B,EAC3B,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,yBAAyB,EACzB,kBAAkB,EAClB,oBAAoB,EACrB,MAAM,eAAe,CAAC;AAEvB,eAAO,MAAM,YAAY;8BACR,MAAM,KAAG,eAAe;mCAGnB,MAAM,cAAc,MAAM,KAAG,mBAAmB;yCAG1C,IAAI,GAAG,MAAM,KAAG,qBAAqB;gCAG9C,MAAM,KAAG,iBAAiB;iCAGzB,MAAM,GAAG,aAAa,GAAG,SAAS,KAAG,iBAAiB;8BAG3D,gBAAgB;2BAGnB,aAAa;6BAGT,OAAO,GAAG,MAAM,GAAG,QAAQ,KAAG,YAAY;+BAG1C,iBAAiB;+CAGC,MAAM,KAAG,2BAA2B;4BAGvD,MAAM,GAAG,SAAS,KAAG,WAAW;+BAG7B,eAAe,GAAG,aAAa,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,YAAY,GAAG,YAAY,UAAU,MAAM,KAAG,cAAc;kCAGtI,UAAU,GAAG,aAAa,KAAG,kBAAkB;uCAG1C,MAAM,KAAG,mBAAmB;sCAG/B,wBAAwB;yCAGnB,MAAM,WAAW;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KAAE,KAAG,cAAc;0CAG1E,MAAM,gBAAgB,MAAM,YAAY,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;KAAE,CAAC,KAAG,qBAAqB;+BAGjK,MAAM,WAAW,SAAS,GAAG,QAAQ,GAAG,SAAS,SAAS,MAAM,KAAG,uBAAuB;wCAGjF,MAAM,cAAc,MAAM,oBAAoB,MAAM,gBAAgB,OAAO,KAAG,oBAAoB;6CAG7F,MAAM,YAAY,OAAO,GAAG,MAAM,KAAG,yBAAyB;
|
|
1
|
+
{"version":3,"file":"command-builders.d.ts","sourceRoot":"","sources":["../src/command-builders.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,2BAA2B,EAC3B,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,yBAAyB,EACzB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACrB,MAAM,eAAe,CAAC;AAEvB,eAAO,MAAM,YAAY;8BACR,MAAM,KAAG,eAAe;mCAGnB,MAAM,cAAc,MAAM,KAAG,mBAAmB;yCAG1C,IAAI,GAAG,MAAM,KAAG,qBAAqB;gCAG9C,MAAM,KAAG,iBAAiB;iCAGzB,MAAM,GAAG,aAAa,GAAG,SAAS,KAAG,iBAAiB;8BAG3D,gBAAgB;2BAGnB,aAAa;6BAGT,OAAO,GAAG,MAAM,GAAG,QAAQ,KAAG,YAAY;+BAG1C,iBAAiB;+CAGC,MAAM,KAAG,2BAA2B;4BAGvD,MAAM,GAAG,SAAS,KAAG,WAAW;+BAG7B,eAAe,GAAG,aAAa,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,YAAY,GAAG,YAAY,UAAU,MAAM,KAAG,cAAc;kCAGtI,UAAU,GAAG,aAAa,KAAG,kBAAkB;uCAG1C,MAAM,KAAG,mBAAmB;sCAG/B,wBAAwB;yCAGnB,MAAM,WAAW;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KAAE,KAAG,cAAc;0CAG1E,MAAM,gBAAgB,MAAM,YAAY,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;KAAE,CAAC,KAAG,qBAAqB;+BAGjK,MAAM,WAAW,SAAS,GAAG,QAAQ,GAAG,SAAS,SAAS,MAAM,KAAG,uBAAuB;wCAGjF,MAAM,cAAc,MAAM,oBAAoB,MAAM,gBAAgB,OAAO,KAAG,oBAAoB;6CAG7F,MAAM,YAAY,OAAO,GAAG,MAAM,KAAG,yBAAyB;oCAGvE,MAAM,KAAG,gBAAgB;kCAG3B,MAAM,SAAS,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,QAAQ,MAAM,WAAW,MAAM,YAAY,MAAM,KAAG,kBAAkB;oCAGtH,MAAM,UAAU,MAAM,UAAU,MAAM,KAAG,oBAAoB;CAG1E,CAAC"}
|
package/dist/command-builders.js
CHANGED
|
@@ -65,6 +65,9 @@ export const agentCommand = {
|
|
|
65
65
|
permissionDecision(requestId, decision) {
|
|
66
66
|
return { type: "permission_decision", requestId, decision };
|
|
67
67
|
},
|
|
68
|
+
reviewRun(sessionId) {
|
|
69
|
+
return { type: "review_run", sessionId };
|
|
70
|
+
},
|
|
68
71
|
esp32OtaAck(otaId, stage, seq, offset, written) {
|
|
69
72
|
return { type: "esp32_ota_ack", otaId, stage, seq, offset, written };
|
|
70
73
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-builders.js","sourceRoot":"","sources":["../src/command-builders.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,iEAAiE;AACjE,EAAE;AACF,2EAA2E;AAC3E,4EAA4E;AAC5E,6EAA6E;
|
|
1
|
+
{"version":3,"file":"command-builders.js","sourceRoot":"","sources":["../src/command-builders.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** One creature's placement within its agent-type band. */
|
|
2
|
+
export interface CreatureSlot {
|
|
3
|
+
/** Center X as a fraction of world width. */
|
|
4
|
+
x: number;
|
|
5
|
+
/** Center Y as a fraction of world height. */
|
|
6
|
+
y: number;
|
|
7
|
+
/** Crowd-driven size multiplier — shrinks as a row fills up. */
|
|
8
|
+
scale: number;
|
|
9
|
+
}
|
|
10
|
+
/** Coding agents (Claude Code) — left-center band. */
|
|
11
|
+
export declare function layoutOctopuses(count: number): CreatureSlot[];
|
|
12
|
+
/** Codex clouds — float in the upper-center, above the octopuses. */
|
|
13
|
+
export declare function layoutCloudCreatures(count: number): CreatureSlot[];
|
|
14
|
+
/** OpenCode rings — mid-center band. */
|
|
15
|
+
export declare function layoutOpenCodeCreatures(count: number): CreatureSlot[];
|
|
16
|
+
/** Antigravity peak/arc marks — upper-right band. */
|
|
17
|
+
export declare function layoutAntigravityCreatures(count: number): CreatureSlot[];
|
|
18
|
+
//# sourceMappingURL=creature-layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creature-layout.d.ts","sourceRoot":"","sources":["../src/creature-layout.ts"],"names":[],"mappings":"AAYA,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC3B,6CAA6C;IAC7C,CAAC,EAAE,MAAM,CAAC;IACV,8CAA8C;IAC9C,CAAC,EAAE,MAAM,CAAC;IACV,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,sDAAsD;AACtD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAY7D;AAED,qEAAqE;AACrE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAYlE;AAED,wCAAwC;AACxC,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAYrE;AAED,qDAAqD;AACrD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAYxE"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// creature-layout.ts — Multi-session creature positioning.
|
|
2
|
+
//
|
|
3
|
+
// Third mirror of the canonical band layout. The other two:
|
|
4
|
+
// android/app/src/main/kotlin/dev/agentdeck/terrarium/CreatureLayout.kt
|
|
5
|
+
// apple/AgentDeck/Terrarium/CreatureLayout.swift
|
|
6
|
+
// Keep the band constants, row rules, and the overlap cap identical across all
|
|
7
|
+
// three — a divergence shows up as the same session set drawn in different
|
|
8
|
+
// places on the Pixoo depending on which daemon is driving it.
|
|
9
|
+
//
|
|
10
|
+
// Kotlin/Swift compute in Float and this mirror in double; the sub-1e-7 drift
|
|
11
|
+
// that introduces cannot survive the rounding to a 64px device grid.
|
|
12
|
+
/** Coding agents (Claude Code) — left-center band. */
|
|
13
|
+
export function layoutOctopuses(count) {
|
|
14
|
+
return layoutBand({
|
|
15
|
+
count,
|
|
16
|
+
xMin: 0.20,
|
|
17
|
+
xMax: 0.50,
|
|
18
|
+
frontY: 0.42,
|
|
19
|
+
backY: 0.52,
|
|
20
|
+
singleRowLimit: 4,
|
|
21
|
+
baseScale: 1.0,
|
|
22
|
+
minScale: 0.58,
|
|
23
|
+
creatureWidth: 0.11,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/** Codex clouds — float in the upper-center, above the octopuses. */
|
|
27
|
+
export function layoutCloudCreatures(count) {
|
|
28
|
+
return layoutBand({
|
|
29
|
+
count,
|
|
30
|
+
xMin: 0.30,
|
|
31
|
+
xMax: 0.55,
|
|
32
|
+
frontY: 0.16,
|
|
33
|
+
backY: 0.28,
|
|
34
|
+
singleRowLimit: 3,
|
|
35
|
+
baseScale: 0.98,
|
|
36
|
+
minScale: 0.56,
|
|
37
|
+
creatureWidth: 0.080,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** OpenCode rings — mid-center band. */
|
|
41
|
+
export function layoutOpenCodeCreatures(count) {
|
|
42
|
+
return layoutBand({
|
|
43
|
+
count,
|
|
44
|
+
xMin: 0.45,
|
|
45
|
+
xMax: 0.68,
|
|
46
|
+
frontY: 0.34,
|
|
47
|
+
backY: 0.46,
|
|
48
|
+
singleRowLimit: 3,
|
|
49
|
+
baseScale: 0.96,
|
|
50
|
+
minScale: 0.56,
|
|
51
|
+
creatureWidth: 0.078,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** Antigravity peak/arc marks — upper-right band. */
|
|
55
|
+
export function layoutAntigravityCreatures(count) {
|
|
56
|
+
return layoutBand({
|
|
57
|
+
count,
|
|
58
|
+
xMin: 0.58,
|
|
59
|
+
xMax: 0.82,
|
|
60
|
+
frontY: 0.22,
|
|
61
|
+
backY: 0.34,
|
|
62
|
+
singleRowLimit: 3,
|
|
63
|
+
baseScale: 0.96,
|
|
64
|
+
minScale: 0.56,
|
|
65
|
+
creatureWidth: 0.096,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Hard floor for the crowd-driven shrink. Below the per-band `minScale` so
|
|
70
|
+
* tightly packed bands can still shrink enough to honor the overlap cap before
|
|
71
|
+
* we give up and accept brief overlap.
|
|
72
|
+
*/
|
|
73
|
+
const CROWDED_MIN_SCALE = 0.40;
|
|
74
|
+
/**
|
|
75
|
+
* Max fraction of a creature's width that two neighbors may overlap. 0.5 →
|
|
76
|
+
* centers stay at least half a body-width apart (≤50% overlap).
|
|
77
|
+
*/
|
|
78
|
+
const MAX_OVERLAP_FRACTION = 0.5;
|
|
79
|
+
function layoutBand(spec) {
|
|
80
|
+
const { count, xMin, xMax, frontY, backY, singleRowLimit, baseScale, minScale, creatureWidth } = spec;
|
|
81
|
+
if (count <= 0)
|
|
82
|
+
return [];
|
|
83
|
+
const rows = count <= singleRowLimit ? 1 : count <= singleRowLimit * 2 ? 2 : 3;
|
|
84
|
+
const scale = Math.max(minScale, baseScale - Math.max(0, count - 1) * 0.055);
|
|
85
|
+
const rowCounts = distribute(count, rows);
|
|
86
|
+
const slots = [];
|
|
87
|
+
let absoluteIndex = 0;
|
|
88
|
+
for (let row = 0; row < rows; row++) {
|
|
89
|
+
const rowCount = rowCounts[row];
|
|
90
|
+
if (rowCount <= 0)
|
|
91
|
+
continue;
|
|
92
|
+
const rowT = rows === 1 ? 0 : row / (rows - 1);
|
|
93
|
+
const rowY = frontY + (backY - frontY) * rowT;
|
|
94
|
+
const rowInset = 0.015 + row * 0.02;
|
|
95
|
+
const rowMinX = xMin + rowInset;
|
|
96
|
+
const rowMaxX = xMax - rowInset;
|
|
97
|
+
// Alternating jitter magnitude — constant within a row.
|
|
98
|
+
const spread = Math.max(0.003, Math.min(0.012, (rowMaxX - rowMinX) / Math.max(rowCount * 5, 1)));
|
|
99
|
+
// Overlap cap: keep neighbor center-spacing ≥ MAX_OVERLAP_FRACTION of the
|
|
100
|
+
// on-screen body width so creatures never overlap by more than ~half. When
|
|
101
|
+
// the band is too tight to honor that at the count-based scale, shrink every
|
|
102
|
+
// creature in the row by the same ratio (down to CROWDED_MIN_SCALE) instead
|
|
103
|
+
// of letting them pile up.
|
|
104
|
+
let rowScale = Math.max(minScale, scale - row * 0.04);
|
|
105
|
+
if (rowCount >= 2) {
|
|
106
|
+
// Worst-case gap after the alternating jitter squeezes a pair.
|
|
107
|
+
const spacing = (rowMaxX - rowMinX) / (rowCount - 1) - 2 * spread;
|
|
108
|
+
const overlapCapScale = Math.max(0, spacing) / (MAX_OVERLAP_FRACTION * creatureWidth);
|
|
109
|
+
rowScale = Math.max(CROWDED_MIN_SCALE, Math.min(rowScale, overlapCapScale));
|
|
110
|
+
}
|
|
111
|
+
for (let col = 0; col < rowCount; col++) {
|
|
112
|
+
const t = rowCount === 1 ? 0.5 : col / (rowCount - 1);
|
|
113
|
+
const baseX = rowMinX + (rowMaxX - rowMinX) * t;
|
|
114
|
+
const phase = (absoluteIndex + row) % 2 === 0 ? -1 : 1;
|
|
115
|
+
const x = Math.min(xMax, Math.max(xMin, baseX + spread * phase));
|
|
116
|
+
const yJitter = ((absoluteIndex % 3) - 1) * 0.008;
|
|
117
|
+
slots.push({ x, y: rowY + yJitter, scale: rowScale });
|
|
118
|
+
absoluteIndex += 1;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return slots;
|
|
122
|
+
}
|
|
123
|
+
function distribute(count, rows) {
|
|
124
|
+
if (rows <= 0)
|
|
125
|
+
return [];
|
|
126
|
+
const result = new Array(rows).fill(Math.floor(count / rows));
|
|
127
|
+
for (let index = 0; index < count % rows; index++)
|
|
128
|
+
result[index] += 1;
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=creature-layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creature-layout.js","sourceRoot":"","sources":["../src/creature-layout.ts"],"names":[],"mappings":"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"}
|
package/dist/d200h-layout.d.ts
CHANGED
|
@@ -53,6 +53,21 @@ export declare function parseState(evt: any): DashState;
|
|
|
53
53
|
export declare function renderUsageButton(label: string, percent: number, color: string, known?: boolean): string;
|
|
54
54
|
/** Wide merged slot (3_2) — 288×144 SVG. Two columns: 5H | 7D. Direct-HID only. */
|
|
55
55
|
export declare function renderUsageWideSlot(fiveHourPct: number, sevenDayPct: number, known?: boolean): string;
|
|
56
|
+
/**
|
|
57
|
+
* Compact label for a rolling-window length in minutes, mirroring the Swift
|
|
58
|
+
* `TopologyRail.windowLabel`: whole days → "Nd" (10080 → "7D"), whole hours →
|
|
59
|
+
* "Nh" (300 → "5H"), else "Nm". Codex now sometimes reports the weekly window
|
|
60
|
+
* in the `primary` slot with `secondary` null, so usage gauges MUST label by a
|
|
61
|
+
* window's own length, never by its slot position — otherwise the 7D gauge
|
|
62
|
+
* silently vanishes and the weekly window is mislabelled "5H".
|
|
63
|
+
*/
|
|
64
|
+
export declare function usageWindowLabel(windowMinutes: number | undefined): string;
|
|
65
|
+
/**
|
|
66
|
+
* Which gauge bucket ('5h' short vs '7d' long) a rolling window belongs to,
|
|
67
|
+
* derived from its length (≥ a day → long) so the clip id / styling is right
|
|
68
|
+
* regardless of whether the window arrived in the primary or secondary slot.
|
|
69
|
+
*/
|
|
70
|
+
export declare function usageWindowKind(windowMinutes: number | undefined): '5h' | '7d';
|
|
56
71
|
export interface UsageTankData {
|
|
57
72
|
agent: 'claude' | 'codex';
|
|
58
73
|
/** Rolling window this tile represents (drives the clip id + label fallback). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"d200h-layout.d.ts","sourceRoot":"","sources":["../src/d200h-layout.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAKpF,kFAAkF;AAClF,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAEnE,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;CAChC;AAED,uEAAuE;AACvE,eAAO,MAAM,SAAS,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"d200h-layout.d.ts","sourceRoot":"","sources":["../src/d200h-layout.ts"],"names":[],"mappings":"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"}
|
package/dist/d200h-layout.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* D200H / deck layout engine
|
|
3
|
-
* (
|
|
4
|
-
*
|
|
2
|
+
* D200H / deck layout engine used by the Ulanzi Studio plugin
|
|
3
|
+
* (plugin-ulanzi) and Apple device previews. Given the current agent state it
|
|
4
|
+
* computes, for a 5×3 key
|
|
5
5
|
* grid, what each key shows (SVG) and does (command) — reflowing dynamically:
|
|
6
6
|
* idle → sessions; a focused awaiting session → its options + ESC; processing →
|
|
7
7
|
* STOP; plus mode / model / usage / tokens / cost.
|
|
@@ -16,10 +16,22 @@ import { PASSIVE_OFFLINE_LABEL, OPEN_AGENTDECK_LABEL } from './connection-status
|
|
|
16
16
|
import { CLAUDE_LOGO_PATH, CODEX_LOGO_PATH } from './svg-renderers/agent-logos.js';
|
|
17
17
|
/** 5 columns × 3 rows. Physical key index == row * GRID_COLS + col. */
|
|
18
18
|
export const GRID_COLS = 5;
|
|
19
|
-
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
|
|
19
|
+
/**
|
|
20
|
+
* D200H usage placement: the three bottom-row keys immediately LEFT of the wide
|
|
21
|
+
* bottom-right clock widget (0_2, 1_2, 2_2) — a single horizontal quota strip
|
|
22
|
+
* along the bottom edge, so the whole upper grid stays session tiles.
|
|
23
|
+
*
|
|
24
|
+
* The strip is filled from its RIGHT end (see `buildList`), keeping the gauges
|
|
25
|
+
* flush against the clock: with the usual three tiles (Claude 5H, Claude 7D,
|
|
26
|
+
* Codex) it fills exactly; when a tile is absent — Codex now commonly reports
|
|
27
|
+
* only its weekly window, and an unlinked Claude reports none — the LEFTMOST
|
|
28
|
+
* key falls out of the strip and flows back to sessions instead of leaving a
|
|
29
|
+
* hole in the middle of the row.
|
|
30
|
+
*
|
|
31
|
+
* Its length also caps usage at three keys: a fourth tile (Codex 5H *and* 7D)
|
|
32
|
+
* drops, Claude prioritised by `buildUsageTiles` order.
|
|
33
|
+
*/
|
|
34
|
+
const USAGE_PREFERRED_POS = ['0_2', '1_2', '2_2'];
|
|
23
35
|
export function parseState(evt) {
|
|
24
36
|
return {
|
|
25
37
|
state: evt?.state ?? 'DISCONNECTED',
|
|
@@ -167,6 +179,31 @@ function usageRampColor(used, stale = false) {
|
|
|
167
179
|
return { fill: '#eab308', hi: '#fde047' };
|
|
168
180
|
return { fill: '#22c55e', hi: '#86efac' };
|
|
169
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Compact label for a rolling-window length in minutes, mirroring the Swift
|
|
184
|
+
* `TopologyRail.windowLabel`: whole days → "Nd" (10080 → "7D"), whole hours →
|
|
185
|
+
* "Nh" (300 → "5H"), else "Nm". Codex now sometimes reports the weekly window
|
|
186
|
+
* in the `primary` slot with `secondary` null, so usage gauges MUST label by a
|
|
187
|
+
* window's own length, never by its slot position — otherwise the 7D gauge
|
|
188
|
+
* silently vanishes and the weekly window is mislabelled "5H".
|
|
189
|
+
*/
|
|
190
|
+
export function usageWindowLabel(windowMinutes) {
|
|
191
|
+
if (!windowMinutes || windowMinutes <= 0)
|
|
192
|
+
return '';
|
|
193
|
+
if (windowMinutes % 1440 === 0)
|
|
194
|
+
return `${windowMinutes / 1440}D`;
|
|
195
|
+
if (windowMinutes % 60 === 0)
|
|
196
|
+
return `${windowMinutes / 60}H`;
|
|
197
|
+
return `${windowMinutes}M`;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Which gauge bucket ('5h' short vs '7d' long) a rolling window belongs to,
|
|
201
|
+
* derived from its length (≥ a day → long) so the clip id / styling is right
|
|
202
|
+
* regardless of whether the window arrived in the primary or secondary slot.
|
|
203
|
+
*/
|
|
204
|
+
export function usageWindowKind(windowMinutes) {
|
|
205
|
+
return (windowMinutes ?? 0) >= 1440 ? '7d' : '5h';
|
|
206
|
+
}
|
|
170
207
|
export function renderUsageGauge(data) {
|
|
171
208
|
const W = 144, H = 144, RX = 12;
|
|
172
209
|
const known = data.known !== false;
|
|
@@ -236,7 +273,8 @@ export function renderCreditsTile(data) {
|
|
|
236
273
|
/**
|
|
237
274
|
* Usage tiles for the session deck, in placement order. Every tile is
|
|
238
275
|
* hide-if-absent: Claude 5H/7D appear only when that window's quota is actually
|
|
239
|
-
* known, and Codex
|
|
276
|
+
* known, and each present Codex window (labelled by its own length, not its
|
|
277
|
+
* primary/secondary slot) only when present in
|
|
240
278
|
* `codexRateLimits`. An unlinked or partial usage state therefore emits fewer
|
|
241
279
|
* (or zero) tiles, so `buildList` reserves fewer keys and the freed slots flow
|
|
242
280
|
* to session tiles instead of leaving reserved "—" ghost gauges behind.
|
|
@@ -255,12 +293,14 @@ function buildUsageTiles(state) {
|
|
|
255
293
|
}
|
|
256
294
|
const cx = state.codexRateLimits;
|
|
257
295
|
// Codex windows carry the same short "5H"/"7D" labels — the brand dot conveys
|
|
258
|
-
// the agent, not a "CX " prefix.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
296
|
+
// the agent, not a "CX " prefix. Label each present window by its own length
|
|
297
|
+
// (windowMinutes), never by slot: Codex now sometimes reports the weekly
|
|
298
|
+
// (10080-min) window as `primary` with `secondary` null, so a slot-based "7D
|
|
299
|
+
// = secondary" would drop the gauge entirely.
|
|
300
|
+
for (const w of [cx?.primary, cx?.secondary]) {
|
|
301
|
+
if (!w)
|
|
302
|
+
continue;
|
|
303
|
+
tiles.push({ svg: renderUsageGauge({ agent: 'codex', window: usageWindowKind(w.windowMinutes), label: usageWindowLabel(w.windowMinutes) || '5H', usedPercent: w.usedPercent, resetsAt: w.resetsAt, known: true, stale: w.stale === true }), action });
|
|
264
304
|
}
|
|
265
305
|
// Credit-based Codex plan: no windows, show the credits balance instead so the
|
|
266
306
|
// Codex usage doesn't silently vanish.
|
|
@@ -447,6 +487,47 @@ function actionTile(label, color, subtitle) {
|
|
|
447
487
|
].join('');
|
|
448
488
|
return svgFrame('#16181d', els);
|
|
449
489
|
}
|
|
490
|
+
/**
|
|
491
|
+
* REVIEW = independent on-demand eval (review_run), NOT a prompt to the
|
|
492
|
+
* agent — the daemon judges the session's latest COMPLETED work with a
|
|
493
|
+
* separate model, so this tile is valid for EVERY session type (managed,
|
|
494
|
+
* observed Claude/OpenCode, even control-less observed Codex) — but only
|
|
495
|
+
* once the turn has ended. Shows the last verdict as a badge; REVIEWING
|
|
496
|
+
* while the judge runs.
|
|
497
|
+
*/
|
|
498
|
+
function reviewTile(sess, sid) {
|
|
499
|
+
if (sess?.reviewStatus === 'running') {
|
|
500
|
+
return { svg: renderInfoSlot('REVIEWING', 'judge running', 'activity', 'info'), action: null };
|
|
501
|
+
}
|
|
502
|
+
const risk = sess?.reviewRisk;
|
|
503
|
+
const subtitle = risk
|
|
504
|
+
? `risk ${risk}${sess?.reviewFindings != null ? ` · ${sess.reviewFindings}` : ''}`
|
|
505
|
+
: 'independent eval';
|
|
506
|
+
const color = risk === 'high' ? '#f87171' : risk === 'medium' ? '#fbbf24' : '#93c5fd';
|
|
507
|
+
return {
|
|
508
|
+
svg: actionTile('REVIEW', color, subtitle),
|
|
509
|
+
action: { kind: 'command', command: { type: 'review_run', sessionId: sid } },
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Non-pressable review status for the PROCESSING branch: REVIEWING while the
|
|
514
|
+
* judge runs, or the last verdict as an inert badge. Null when there is
|
|
515
|
+
* nothing to show. Mid-turn the tile must not fire review_run — the work is
|
|
516
|
+
* not complete yet (the Swift daemon judges the session trajectory, which
|
|
517
|
+
* mid-turn has no assistant response and systematically reads as
|
|
518
|
+
* "incomplete/unverified"; the Node daemon would judge a half-written diff).
|
|
519
|
+
*/
|
|
520
|
+
function reviewBadgeTile(sess) {
|
|
521
|
+
if (sess?.reviewStatus === 'running') {
|
|
522
|
+
return { svg: renderInfoSlot('REVIEWING', 'judge running', 'activity', 'info'), action: null };
|
|
523
|
+
}
|
|
524
|
+
const risk = sess?.reviewRisk;
|
|
525
|
+
if (!risk)
|
|
526
|
+
return null;
|
|
527
|
+
const subtitle = `risk ${risk}${sess?.reviewFindings != null ? ` · ${sess.reviewFindings}` : ''}`;
|
|
528
|
+
const color = risk === 'high' ? '#f87171' : risk === 'medium' ? '#fbbf24' : '#93c5fd';
|
|
529
|
+
return { svg: actionTile('REVIEW', color, subtitle), action: null };
|
|
530
|
+
}
|
|
450
531
|
export function buildSessionDeck(stateEvt, view, positions) {
|
|
451
532
|
const state = parseState(stateEvt);
|
|
452
533
|
const slots = sortPositions(positions);
|
|
@@ -482,29 +563,30 @@ export function buildSessionDeck(stateEvt, view, positions) {
|
|
|
482
563
|
}
|
|
483
564
|
function buildList(state, view, slots, animFrame, animated, out) {
|
|
484
565
|
const sessions = sortSessions(foldCodexSessionsForDisplay(state.allSessions));
|
|
485
|
-
// Pin the
|
|
486
|
-
// the D200H
|
|
487
|
-
// Deck
|
|
488
|
-
// as we have usage tiles (Claude 5H/7D + any
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
// the
|
|
492
|
-
//
|
|
493
|
-
// global), and paging math below treats them as
|
|
566
|
+
// Pin the bottom-row usage strip to the global quota gauges (opt-in,
|
|
567
|
+
// water-tank style). On the D200H the strip sits just left of the native clock
|
|
568
|
+
// widget; on classic Stream Deck it replaces the encoder LCD this surface
|
|
569
|
+
// lacks. We reserve as many keys as we have usage tiles (Claude 5H/7D + any
|
|
570
|
+
// Codex window), but never more than the strip is wide, and never more than
|
|
571
|
+
// `slots.length - 1` so at least one key stays for sessions — the latter is
|
|
572
|
+
// the fix for the old `slots.length >= 6` gate that silently dropped ALL usage
|
|
573
|
+
// when the user placed only a few AgentDeck keys. Reserved keys are pinned on
|
|
574
|
+
// EVERY page (usage is global), and paging math below treats them as
|
|
575
|
+
// unavailable for sessions.
|
|
494
576
|
const usageHere = new Map();
|
|
495
577
|
if (view.showUsage) {
|
|
496
578
|
const usageTiles = buildUsageTiles(state);
|
|
497
579
|
const maxReserve = Math.max(0, slots.length - 1);
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
//
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const rest = slots.filter((p) => !
|
|
506
|
-
const fallback = rest.slice(rest.length - Math.max(0, reserveCount -
|
|
507
|
-
const reserved = sortPositions([...
|
|
580
|
+
const preferred = sortPositions(USAGE_PREFERRED_POS.filter((p) => slots.includes(p)));
|
|
581
|
+
const reserveCount = Math.min(usageTiles.length, USAGE_PREFERRED_POS.length, maxReserve);
|
|
582
|
+
// Fill the strip from its RIGHT end so a missing tile frees the LEFTMOST key
|
|
583
|
+
// (which flows back to sessions) and the gauges stay flush against the clock
|
|
584
|
+
// — never a hole mid-strip.
|
|
585
|
+
const pinned = preferred.slice(Math.max(0, preferred.length - reserveCount));
|
|
586
|
+
// Tiles whose strip key the user didn't place fall back to trailing keys.
|
|
587
|
+
const rest = slots.filter((p) => !pinned.includes(p));
|
|
588
|
+
const fallback = rest.slice(rest.length - Math.max(0, reserveCount - pinned.length));
|
|
589
|
+
const reserved = sortPositions([...pinned, ...fallback]).slice(0, reserveCount);
|
|
508
590
|
reserved.forEach((pos, i) => usageHere.set(pos, usageTiles[i]));
|
|
509
591
|
}
|
|
510
592
|
// Positions left for sessions / NEXT after carving out usage.
|
|
@@ -552,7 +634,10 @@ function buildDetail(state, stateEvt, view, slots, animFrame, animated, out) {
|
|
|
552
634
|
const sState = (focused ? state.state : (sess?.state ?? 'idle')).toLowerCase();
|
|
553
635
|
const options = (focused ? state.options : (sess?.options ?? [])) ?? [];
|
|
554
636
|
const tool = focused ? state.currentTool : sess?.currentTool;
|
|
555
|
-
|
|
637
|
+
// A selected session with no model is UNKNOWN, not an invitation to borrow
|
|
638
|
+
// the daemon-global model from another agent. Only a matching focused event
|
|
639
|
+
// may override the sessions_list row.
|
|
640
|
+
const model = focused ? (state.modelName || sess?.modelName || '') : (sess?.modelName || '');
|
|
556
641
|
const heroSess = sess ?? {
|
|
557
642
|
id: sid, port: 0, alive: true, projectName: state.projectName,
|
|
558
643
|
agentType: state.agentType, state: sState, modelName: model,
|
|
@@ -564,14 +649,52 @@ function buildDetail(state, stateEvt, view, slots, animFrame, animated, out) {
|
|
|
564
649
|
svg: renderDetailInfo(heroSess, sState, tool, model, undefined),
|
|
565
650
|
action: null,
|
|
566
651
|
});
|
|
652
|
+
// Observed (hook-only, no PTY) sessions steer through hook primitives, so
|
|
653
|
+
// every actionable cell must map to something the daemon can actually
|
|
654
|
+
// deliver: STOP → soft stop (deny at next tool call), quick actions →
|
|
655
|
+
// queued for turn end, ALLOW/DENY → held PreToolUse gate. Anything
|
|
656
|
+
// undeliverable renders inert instead of pretending to work.
|
|
657
|
+
const isObserved = sess?.controlMode === 'observed';
|
|
658
|
+
// Which observed agents actually have a delivery path: Claude = hook RPC
|
|
659
|
+
// (soft stop / turn-end queue / gate), OpenCode = observer-plugin command
|
|
660
|
+
// queue (immediate abort / prompt injection). Codex hooks are notify-only —
|
|
661
|
+
// no steering exists, so its buttons must stay inert.
|
|
662
|
+
const observedSteerable = isObserved
|
|
663
|
+
&& (sess?.agentType === 'claude-code' || sess?.agentType === 'opencode');
|
|
664
|
+
const stopRequested = Boolean(sess?.stopRequested);
|
|
665
|
+
const gateRequestId = sess?.requestId;
|
|
567
666
|
if (processingState(sState)) {
|
|
568
|
-
|
|
667
|
+
if (isObserved && !observedSteerable) {
|
|
668
|
+
out.set(last, { svg: renderStopButton(false), action: null });
|
|
669
|
+
}
|
|
670
|
+
else if (isObserved && stopRequested) {
|
|
671
|
+
// Soft stop already requested — pressing again does nothing new.
|
|
672
|
+
out.set(last, { svg: renderInfoSlot('STOPPING', 'at next tool', 'status', 'warning'), action: null });
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
const command = isObserved
|
|
676
|
+
? { type: 'session_command', sessionId: sid, command: { type: 'interrupt' } }
|
|
677
|
+
: { type: 'interrupt' };
|
|
678
|
+
out.set(last, { svg: renderStopButton(true), action: { kind: 'command', command } });
|
|
679
|
+
}
|
|
569
680
|
}
|
|
570
681
|
else if (awaitingState(sState)) {
|
|
571
|
-
|
|
682
|
+
if (isObserved) {
|
|
683
|
+
// ESC can't reach an observed terminal; the gate (if any) is answered
|
|
684
|
+
// by the ALLOW/DENY content cells below.
|
|
685
|
+
out.set(last, { svg: renderEscButton(false), action: null });
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
out.set(last, { svg: renderEscButton(true), action: { kind: 'command', command: { type: 'escape' } } });
|
|
689
|
+
}
|
|
572
690
|
}
|
|
573
691
|
else {
|
|
574
|
-
|
|
692
|
+
// Idle: an observed session has nothing to interrupt — render STOP inert
|
|
693
|
+
// (the old always-armed interrupt here was a silent-drop trap).
|
|
694
|
+
out.set(last, {
|
|
695
|
+
svg: renderStopButton(false),
|
|
696
|
+
action: isObserved ? null : { kind: 'command', command: { type: 'interrupt' } },
|
|
697
|
+
});
|
|
575
698
|
}
|
|
576
699
|
// Content slots between INFO and STOP.
|
|
577
700
|
const content = slots.slice(2, slots.length - 1);
|
|
@@ -593,6 +716,19 @@ function buildDetail(state, stateEvt, view, slots, animFrame, animated, out) {
|
|
|
593
716
|
cells.push({ svg: renderOptionButton(opt, i), action: { kind: 'command', command } });
|
|
594
717
|
});
|
|
595
718
|
}
|
|
719
|
+
else if (isObserved && gateRequestId) {
|
|
720
|
+
// Held PreToolUse gate: these two answers are device-native semantics
|
|
721
|
+
// (permit/deny THIS tool call), not a mirror of the TUI prompt — the
|
|
722
|
+
// daemon only holds calls it verified Claude would genuinely prompt for.
|
|
723
|
+
cells.push({
|
|
724
|
+
svg: actionTile('ALLOW', '#22c55e', 'this tool call'),
|
|
725
|
+
action: { kind: 'command', command: { type: 'permission_decision', requestId: gateRequestId, decision: 'allow' } },
|
|
726
|
+
});
|
|
727
|
+
cells.push({
|
|
728
|
+
svg: actionTile('DENY', '#f87171', 'this tool call'),
|
|
729
|
+
action: { kind: 'command', command: { type: 'permission_decision', requestId: gateRequestId, decision: 'deny' } },
|
|
730
|
+
});
|
|
731
|
+
}
|
|
596
732
|
else {
|
|
597
733
|
// Awaiting but no real options to render — only PTY-managed sessions expose
|
|
598
734
|
// Claude's actual choices. Don't fabricate Allow/Deny that may not match the
|
|
@@ -601,12 +737,57 @@ function buildDetail(state, stateEvt, view, slots, animFrame, animated, out) {
|
|
|
601
737
|
}
|
|
602
738
|
}
|
|
603
739
|
else if (processingState(sState)) {
|
|
604
|
-
|
|
740
|
+
const queued = sess?.queuedDirectives ?? 0;
|
|
741
|
+
cells.push({
|
|
742
|
+
svg: renderInfoSlot('RUNNING', queued > 0 ? `${queued} queued` : (tool || 'working'), 'activity', 'info'),
|
|
743
|
+
action: null,
|
|
744
|
+
});
|
|
745
|
+
// No queued task buttons mid-turn: PROCESSING is deliberately limited to
|
|
746
|
+
// live status + STOP so users cannot mistake a future directive for the
|
|
747
|
+
// agent's current work. Keep the
|
|
748
|
+
// REVIEWING spinner / last-verdict badge visible as inert status.
|
|
749
|
+
const badge = reviewBadgeTile(sess);
|
|
750
|
+
if (badge)
|
|
751
|
+
cells.push(badge);
|
|
752
|
+
}
|
|
753
|
+
else if (isObserved) {
|
|
754
|
+
if (sess?.agentType === 'opencode') {
|
|
755
|
+
// OpenCode observed injects immediately even while idle (observer
|
|
756
|
+
// plugin + in-process SDK) — same semantics as managed idle presets.
|
|
757
|
+
const inject = [
|
|
758
|
+
['GO ON', 'continue'], ['COMMIT', 'commit the changes'],
|
|
759
|
+
];
|
|
760
|
+
inject.forEach(([label, text]) => cells.push({
|
|
761
|
+
svg: actionTile(label, '#cbd5e1', 'inject now'),
|
|
762
|
+
action: { kind: 'command', command: { type: 'session_command', sessionId: sid, command: { type: 'send_prompt', text } } },
|
|
763
|
+
}));
|
|
764
|
+
cells.push(reviewTile(sess, sid));
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
// Idle observed Claude/Codex: no prompt-delivery path exists, but the
|
|
768
|
+
// independent review needs no control at all — it is the one action
|
|
769
|
+
// that stays live here.
|
|
770
|
+
cells.push(reviewTile(sess, sid));
|
|
771
|
+
cells.push({ svg: renderInfoSlot('OBSERVED', 'control in terminal', 'status', 'info'), action: null });
|
|
772
|
+
}
|
|
605
773
|
}
|
|
606
774
|
else {
|
|
607
|
-
// idle quick-actions
|
|
608
|
-
|
|
609
|
-
|
|
775
|
+
// Managed idle quick-actions. REVIEW routes to the independent eval
|
|
776
|
+
// (uniform semantics across every session type); the rest type into the
|
|
777
|
+
// PTY as before.
|
|
778
|
+
cells.push({
|
|
779
|
+
svg: actionTile('GO ON', '#cbd5e1'),
|
|
780
|
+
action: { kind: 'command', command: { type: 'send_prompt', text: 'continue' } },
|
|
781
|
+
});
|
|
782
|
+
cells.push(reviewTile(sess, sid));
|
|
783
|
+
cells.push({
|
|
784
|
+
svg: actionTile('COMMIT', '#cbd5e1'),
|
|
785
|
+
action: { kind: 'command', command: { type: 'send_prompt', text: 'commit the changes' } },
|
|
786
|
+
});
|
|
787
|
+
cells.push({
|
|
788
|
+
svg: actionTile('CLEAR', '#cbd5e1'),
|
|
789
|
+
action: { kind: 'command', command: { type: 'send_prompt', text: '/clear' } },
|
|
790
|
+
});
|
|
610
791
|
}
|
|
611
792
|
// Paginate cells into content slots; reserve last content slot for MORE if overflow.
|
|
612
793
|
const cap = content.length;
|