@sagmans/dsh-tui 0.3.0 → 0.5.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/LICENSE +1 -1
- package/README.md +407 -53
- package/cordis.patch.yml +14 -0
- package/lib/agent/present.js +1 -1
- package/lib/agent/present.js.map +1 -1
- package/lib/agent/projections.d.ts +22 -6
- package/lib/agent/projections.d.ts.map +1 -1
- package/lib/agent/projections.js +21 -10
- package/lib/agent/projections.js.map +1 -1
- package/lib/agent/prompt-history.d.ts +89 -0
- package/lib/agent/prompt-history.d.ts.map +1 -0
- package/lib/agent/prompt-history.js +382 -0
- package/lib/agent/prompt-history.js.map +1 -0
- package/lib/agent/status.d.ts +6 -0
- package/lib/agent/status.d.ts.map +1 -1
- package/lib/agent/status.js +1 -0
- package/lib/agent/status.js.map +1 -1
- package/lib/cards.d.ts +83 -13
- package/lib/cards.d.ts.map +1 -1
- package/lib/cards.js +96 -28
- package/lib/cards.js.map +1 -1
- package/lib/export.d.ts +3 -3
- package/lib/export.d.ts.map +1 -1
- package/lib/export.js +23 -13
- package/lib/export.js.map +1 -1
- package/lib/fold-cursor.d.ts +12 -5
- package/lib/fold-cursor.d.ts.map +1 -1
- package/lib/fold-cursor.js +16 -5
- package/lib/fold-cursor.js.map +1 -1
- package/lib/gates.d.ts.map +1 -1
- package/lib/gates.js +42 -9
- package/lib/gates.js.map +1 -1
- package/lib/herdr/client.d.ts +57 -0
- package/lib/herdr/client.d.ts.map +1 -0
- package/lib/herdr/client.js +218 -0
- package/lib/herdr/client.js.map +1 -0
- package/lib/herdr/constants.d.ts +93 -0
- package/lib/herdr/constants.d.ts.map +1 -0
- package/lib/herdr/constants.js +79 -0
- package/lib/herdr/constants.js.map +1 -0
- package/lib/herdr/reporter.d.ts +60 -0
- package/lib/herdr/reporter.d.ts.map +1 -0
- package/lib/herdr/reporter.js +217 -0
- package/lib/herdr/reporter.js.map +1 -0
- package/lib/herdr/state.d.ts +57 -0
- package/lib/herdr/state.d.ts.map +1 -0
- package/lib/herdr/state.js +62 -0
- package/lib/herdr/state.js.map +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +700 -81
- package/lib/index.js.map +1 -1
- package/lib/injection.d.ts +9 -0
- package/lib/injection.d.ts.map +1 -0
- package/lib/injection.js +128 -0
- package/lib/injection.js.map +1 -0
- package/lib/input/actions.d.ts +19 -2
- package/lib/input/actions.d.ts.map +1 -1
- package/lib/input/actions.js +60 -11
- package/lib/input/actions.js.map +1 -1
- package/lib/input/cancel.d.ts +55 -0
- package/lib/input/cancel.d.ts.map +1 -0
- package/lib/input/cancel.js +47 -0
- package/lib/input/cancel.js.map +1 -0
- package/lib/input/completion.d.ts +5 -2
- package/lib/input/completion.d.ts.map +1 -1
- package/lib/input/completion.js +79 -4
- package/lib/input/completion.js.map +1 -1
- package/lib/input/file-search.d.ts +105 -0
- package/lib/input/file-search.d.ts.map +1 -0
- package/lib/input/file-search.js +665 -0
- package/lib/input/file-search.js.map +1 -0
- package/lib/input/fuzzy.d.ts +42 -0
- package/lib/input/fuzzy.d.ts.map +1 -0
- package/lib/input/fuzzy.js +289 -0
- package/lib/input/fuzzy.js.map +1 -0
- package/lib/input/ghost.d.ts +46 -0
- package/lib/input/ghost.d.ts.map +1 -0
- package/lib/input/ghost.js +58 -0
- package/lib/input/ghost.js.map +1 -0
- package/lib/input/keymap.d.ts +3 -1
- package/lib/input/keymap.d.ts.map +1 -1
- package/lib/input/keymap.js +16 -3
- package/lib/input/keymap.js.map +1 -1
- package/lib/input/match.d.ts +12 -4
- package/lib/input/match.d.ts.map +1 -1
- package/lib/input/match.js +50 -51
- package/lib/input/match.js.map +1 -1
- package/lib/input/submission.d.ts +34 -1
- package/lib/input/submission.d.ts.map +1 -1
- package/lib/input/submission.js +33 -5
- package/lib/input/submission.js.map +1 -1
- package/lib/keys-command.d.ts +19 -3
- package/lib/keys-command.d.ts.map +1 -1
- package/lib/keys-command.js +34 -31
- package/lib/keys-command.js.map +1 -1
- package/lib/settings-notice.d.ts +5 -0
- package/lib/settings-notice.d.ts.map +1 -1
- package/lib/settings-notice.js +9 -7
- package/lib/settings-notice.js.map +1 -1
- package/lib/stash/lock.d.ts +72 -0
- package/lib/stash/lock.d.ts.map +1 -0
- package/lib/stash/lock.js +374 -0
- package/lib/stash/lock.js.map +1 -0
- package/lib/stash/paths.d.ts +22 -0
- package/lib/stash/paths.d.ts.map +1 -0
- package/lib/stash/paths.js +84 -0
- package/lib/stash/paths.js.map +1 -0
- package/lib/stash/private-fs.d.ts +61 -0
- package/lib/stash/private-fs.d.ts.map +1 -0
- package/lib/stash/private-fs.js +379 -0
- package/lib/stash/private-fs.js.map +1 -0
- package/lib/stash/schema.d.ts +56 -0
- package/lib/stash/schema.d.ts.map +1 -0
- package/lib/stash/schema.js +108 -0
- package/lib/stash/schema.js.map +1 -0
- package/lib/stash/store.d.ts +92 -0
- package/lib/stash/store.d.ts.map +1 -0
- package/lib/stash/store.js +262 -0
- package/lib/stash/store.js.map +1 -0
- package/lib/stash.d.ts +122 -0
- package/lib/stash.d.ts.map +1 -0
- package/lib/stash.js +353 -0
- package/lib/stash.js.map +1 -0
- package/lib/terminal/external-editor.d.ts +93 -0
- package/lib/terminal/external-editor.d.ts.map +1 -0
- package/lib/terminal/external-editor.js +263 -0
- package/lib/terminal/external-editor.js.map +1 -0
- package/lib/terminal/host-writes.d.ts +39 -0
- package/lib/terminal/host-writes.d.ts.map +1 -0
- package/lib/terminal/host-writes.js +136 -0
- package/lib/terminal/host-writes.js.map +1 -0
- package/lib/terminal/signals.d.ts +25 -0
- package/lib/terminal/signals.d.ts.map +1 -0
- package/lib/terminal/signals.js +39 -0
- package/lib/terminal/signals.js.map +1 -0
- package/lib/terminal/warning-screen.d.ts +19 -1
- package/lib/terminal/warning-screen.d.ts.map +1 -1
- package/lib/terminal/warning-screen.js +37 -1
- package/lib/terminal/warning-screen.js.map +1 -1
- package/lib/terminal-text.d.ts +72 -0
- package/lib/terminal-text.d.ts.map +1 -0
- package/lib/terminal-text.js +603 -0
- package/lib/terminal-text.js.map +1 -0
- package/lib/text.d.ts +45 -6
- package/lib/text.d.ts.map +1 -1
- package/lib/text.js +106 -46
- package/lib/text.js.map +1 -1
- package/lib/theme-command.d.ts +7 -4
- package/lib/theme-command.d.ts.map +1 -1
- package/lib/theme-command.js +32 -12
- package/lib/theme-command.js.map +1 -1
- package/lib/theme-files.d.ts +110 -0
- package/lib/theme-files.d.ts.map +1 -0
- package/lib/theme-files.js +352 -0
- package/lib/theme-files.js.map +1 -0
- package/lib/theme-schema.d.ts +121 -0
- package/lib/theme-schema.d.ts.map +1 -0
- package/lib/theme-schema.js +87 -0
- package/lib/theme-schema.js.map +1 -0
- package/lib/theme-settings.d.ts +72 -10
- package/lib/theme-settings.d.ts.map +1 -1
- package/lib/theme-settings.js +162 -45
- package/lib/theme-settings.js.map +1 -1
- package/lib/theme-tokens.d.ts +39 -7
- package/lib/theme-tokens.d.ts.map +1 -1
- package/lib/theme-tokens.js +104 -13
- package/lib/theme-tokens.js.map +1 -1
- package/lib/theme.d.ts +24 -2
- package/lib/theme.d.ts.map +1 -1
- package/lib/theme.js +49 -6
- package/lib/theme.js.map +1 -1
- package/lib/todo-guard.d.ts +116 -0
- package/lib/todo-guard.d.ts.map +1 -0
- package/lib/todo-guard.js +259 -0
- package/lib/todo-guard.js.map +1 -0
- package/lib/tool-display.d.ts +57 -0
- package/lib/tool-display.d.ts.map +1 -0
- package/lib/tool-display.js +51 -0
- package/lib/tool-display.js.map +1 -0
- package/lib/transcript.d.ts +28 -0
- package/lib/transcript.d.ts.map +1 -1
- package/lib/transcript.js +126 -37
- package/lib/transcript.js.map +1 -1
- package/lib/ui/diff.d.ts +49 -0
- package/lib/ui/diff.d.ts.map +1 -0
- package/lib/ui/diff.js +208 -0
- package/lib/ui/diff.js.map +1 -0
- package/lib/ui/dock.d.ts.map +1 -1
- package/lib/ui/dock.js +9 -6
- package/lib/ui/dock.js.map +1 -1
- package/lib/ui/editor.d.ts +41 -1
- package/lib/ui/editor.d.ts.map +1 -1
- package/lib/ui/editor.js +100 -4
- package/lib/ui/editor.js.map +1 -1
- package/lib/ui/frame.d.ts +16 -5
- package/lib/ui/frame.d.ts.map +1 -1
- package/lib/ui/frame.js +32 -13
- package/lib/ui/frame.js.map +1 -1
- package/lib/ui/history-picker.d.ts +15 -0
- package/lib/ui/history-picker.d.ts.map +1 -0
- package/lib/ui/history-picker.js +26 -0
- package/lib/ui/history-picker.js.map +1 -0
- package/lib/ui/keymap-picker.d.ts +16 -0
- package/lib/ui/keymap-picker.d.ts.map +1 -0
- package/lib/ui/keymap-picker.js +41 -0
- package/lib/ui/keymap-picker.js.map +1 -0
- package/lib/ui/layout.d.ts +33 -0
- package/lib/ui/layout.d.ts.map +1 -0
- package/lib/ui/layout.js +33 -0
- package/lib/ui/layout.js.map +1 -0
- package/lib/ui/markdown.d.ts +25 -3
- package/lib/ui/markdown.d.ts.map +1 -1
- package/lib/ui/markdown.js +12 -8
- package/lib/ui/markdown.js.map +1 -1
- package/lib/ui/picker-card.d.ts +42 -0
- package/lib/ui/picker-card.d.ts.map +1 -0
- package/lib/ui/picker-card.js +148 -0
- package/lib/ui/picker-card.js.map +1 -0
- package/lib/ui/picker.d.ts +50 -2
- package/lib/ui/picker.d.ts.map +1 -1
- package/lib/ui/picker.js +66 -7
- package/lib/ui/picker.js.map +1 -1
- package/lib/ui/prompt.d.ts +12 -2
- package/lib/ui/prompt.d.ts.map +1 -1
- package/lib/ui/prompt.js +25 -2
- package/lib/ui/prompt.js.map +1 -1
- package/lib/ui/rows.d.ts +8 -6
- package/lib/ui/rows.d.ts.map +1 -1
- package/lib/ui/rows.js +10 -18
- package/lib/ui/rows.js.map +1 -1
- package/lib/ui/stash-picker.d.ts +43 -0
- package/lib/ui/stash-picker.d.ts.map +1 -0
- package/lib/ui/stash-picker.js +78 -0
- package/lib/ui/stash-picker.js.map +1 -0
- package/lib/ui/status.d.ts +2 -0
- package/lib/ui/status.d.ts.map +1 -1
- package/lib/ui/status.js +25 -16
- package/lib/ui/status.js.map +1 -1
- package/lib/ui/theme-picker.d.ts +18 -0
- package/lib/ui/theme-picker.d.ts.map +1 -0
- package/lib/ui/theme-picker.js +68 -0
- package/lib/ui/theme-picker.js.map +1 -0
- package/lib/ui/view.d.ts +146 -22
- package/lib/ui/view.d.ts.map +1 -1
- package/lib/ui/view.js +461 -154
- package/lib/ui/view.js.map +1 -1
- package/lib/work.d.ts +3 -1
- package/lib/work.d.ts.map +1 -1
- package/lib/work.js +9 -1
- package/lib/work.js.map +1 -1
- package/package.json +7 -1
- package/themes/deepseek-blue.yaml +183 -0
- package/themes/violet-orbit.yaml +178 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import z from '@deepseek-ai/schemastery';
|
|
2
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
3
|
+
import { projectionRead } from "./agent/projections.js";
|
|
4
|
+
/**
|
|
5
|
+
* Advisory todo discipline.
|
|
6
|
+
*
|
|
7
|
+
* The model already owns a whole-list todo tool; what it lacks is a reason to
|
|
8
|
+
* keep the list current over a long turn. This guard watches the harness's own
|
|
9
|
+
* projections and, when a plan ages past a threshold, rides the next tool
|
|
10
|
+
* result with a model-visible reminder — the same `additionalContexts` channel
|
|
11
|
+
* the repeat-tool reminder uses. It never vetoes a call and never steers a
|
|
12
|
+
* stopped turn, so it cannot loop; the per-turn cap bounds the one extra step
|
|
13
|
+
* each injected reminder costs the loop.
|
|
14
|
+
*/
|
|
15
|
+
export const name = 'tui-todo-guard';
|
|
16
|
+
/** The tool registry is read through the injected property, so Cordis must provide it first. */
|
|
17
|
+
export const inject = ['tools'];
|
|
18
|
+
/** The tool this guard exists to keep honest, as the registry publishes it. */
|
|
19
|
+
export const TODO_WRITE_TOOL = 'todo_write';
|
|
20
|
+
/** The durable events the counter follows. */
|
|
21
|
+
const TURN_START = 'turn/start';
|
|
22
|
+
const STEP_START = 'step/start';
|
|
23
|
+
/** The tool's own snapshot event, appended only once a write is committed. */
|
|
24
|
+
const TODO_WRITE_EVENT = 'todo/write';
|
|
25
|
+
/** Projection keys the harness registers for the tool and for plan mode. */
|
|
26
|
+
const TODOS_KEY = 'todos';
|
|
27
|
+
const PLAN_KEY = 'plan';
|
|
28
|
+
/** The reminder source; unlabeled context would read as a user prompt in derived history. */
|
|
29
|
+
const PLUGIN_SOURCE = { kind: 'plugin', plugin: 'tui-todo-guard' };
|
|
30
|
+
/** Defaults every deployment inherits; the bundle patch states them again for readers. */
|
|
31
|
+
export const TODO_GUARD_DEFAULTS = {
|
|
32
|
+
staleSteps: 6,
|
|
33
|
+
missingListSteps: 12,
|
|
34
|
+
maxRemindersPerTurn: 3,
|
|
35
|
+
previewItems: 5,
|
|
36
|
+
};
|
|
37
|
+
/** The loader validates the row against this schema before `apply` sees it. */
|
|
38
|
+
export const Config = z.object({
|
|
39
|
+
staleSteps: z.number().default(TODO_GUARD_DEFAULTS.staleSteps),
|
|
40
|
+
missingListSteps: z.number().default(TODO_GUARD_DEFAULTS.missingListSteps),
|
|
41
|
+
maxRemindersPerTurn: z.number().default(TODO_GUARD_DEFAULTS.maxRemindersPerTurn),
|
|
42
|
+
previewItems: z.number().default(TODO_GUARD_DEFAULTS.previewItems),
|
|
43
|
+
});
|
|
44
|
+
/** One field that must be a positive whole number, or a load-time failure. */
|
|
45
|
+
function positiveInteger(field, value) {
|
|
46
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
47
|
+
throw new Error(`todo-guard: \`${field}\` must be an integer >= 1 (got ${String(value)})`);
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Apply defaults and validate. The loader normally fills the defaults, but a
|
|
53
|
+
* direct `apply` call (a test, or a composition that bypasses schemastery)
|
|
54
|
+
* must fail loud rather than silently guard nothing.
|
|
55
|
+
*/
|
|
56
|
+
export function resolveTodoGuardConfig(config = {}) {
|
|
57
|
+
return {
|
|
58
|
+
staleSteps: positiveInteger('staleSteps', config.staleSteps ?? TODO_GUARD_DEFAULTS.staleSteps),
|
|
59
|
+
missingListSteps: positiveInteger('missingListSteps', config.missingListSteps ?? TODO_GUARD_DEFAULTS.missingListSteps),
|
|
60
|
+
maxRemindersPerTurn: positiveInteger('maxRemindersPerTurn', config.maxRemindersPerTurn ?? TODO_GUARD_DEFAULTS.maxRemindersPerTurn),
|
|
61
|
+
previewItems: positiveInteger('previewItems', config.previewItems ?? TODO_GUARD_DEFAULTS.previewItems),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const OPEN_GLYPHS = { pending: '☐', in_progress: '▸' };
|
|
65
|
+
const isOpenTodo = (todo) => todo.status !== 'completed';
|
|
66
|
+
/** The stale-list reminder: the plan aged, so it is now describing earlier work. */
|
|
67
|
+
export function staleReminder(open, steps, previewItems) {
|
|
68
|
+
const shown = open.slice(0, previewItems);
|
|
69
|
+
const omitted = open.length - shown.length;
|
|
70
|
+
return {
|
|
71
|
+
text: [
|
|
72
|
+
`Your todo list is stale: ${steps} step${steps === 1 ? '' : 's'} have passed without a todo_write, and ${open.length} item${open.length === 1 ? '' : 's'} ${open.length === 1 ? 'is' : 'are'} still open:`,
|
|
73
|
+
...shown.map(todo => ` ${OPEN_GLYPHS[todo.status]} ${todo.content}`),
|
|
74
|
+
...(omitted > 0 ? [` … ${omitted} more`] : []),
|
|
75
|
+
'Update it now: mark finished items completed, mark the item you are working on in_progress, and add work you discovered. Do not leave the list describing earlier work.',
|
|
76
|
+
].join('\n'),
|
|
77
|
+
summary: `todos stale · ${steps} steps`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** The reminder for a long turn that has produced no list at all. */
|
|
81
|
+
export function missingListReminder(steps) {
|
|
82
|
+
return {
|
|
83
|
+
text: `${steps} steps have run in this turn without a todo list. If this work has more than one step, call todo_write now to record the concrete remaining steps and mark the one you are on in_progress.`,
|
|
84
|
+
summary: `todos missing · ${steps} steps`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The decision core, free of Cordis so it can be tested directly.
|
|
89
|
+
*
|
|
90
|
+
* It counts model steps rather than tool calls: a plan ages by how much the
|
|
91
|
+
* agent has thought and acted since its last write, and a parallel batch of
|
|
92
|
+
* calls is one step, so one batch can never spend the whole reminder budget.
|
|
93
|
+
*/
|
|
94
|
+
export class TodoGuard {
|
|
95
|
+
config;
|
|
96
|
+
counters = new WeakMap();
|
|
97
|
+
constructor(config) {
|
|
98
|
+
this.config = config;
|
|
99
|
+
}
|
|
100
|
+
/** Follow the durable events that open a turn or a step. */
|
|
101
|
+
onEvent(session, type) {
|
|
102
|
+
const counters = this.countersFor(session);
|
|
103
|
+
if (type === TURN_START) {
|
|
104
|
+
counters.steps = 0;
|
|
105
|
+
counters.reminders = 0;
|
|
106
|
+
counters.remindedThisStep = false;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (type === STEP_START) {
|
|
110
|
+
counters.steps += 1;
|
|
111
|
+
counters.remindedThisStep = false;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// Only a committed write restarts the age. An attempt the tool rejected
|
|
115
|
+
// never reaches this event, so it cannot buy the stale list quiet steps.
|
|
116
|
+
if (type === TODO_WRITE_EVENT)
|
|
117
|
+
counters.steps = 0;
|
|
118
|
+
}
|
|
119
|
+
/** The reminder this tool result should carry, or undefined to stay silent. */
|
|
120
|
+
observe(session, observation) {
|
|
121
|
+
const counters = this.countersFor(session);
|
|
122
|
+
// Never speak on the write's own result; the committed event restarts the
|
|
123
|
+
// count, so a rejected attempt does not silence the guard either.
|
|
124
|
+
if (observation.toolName === TODO_WRITE_TOOL)
|
|
125
|
+
return undefined;
|
|
126
|
+
if (!observation.hasTodoTool)
|
|
127
|
+
return undefined;
|
|
128
|
+
// Plan mode suppresses the guard, and an unreadable plan projection is not
|
|
129
|
+
// a `false` answer, so only a definite "not in plan mode" lets it speak.
|
|
130
|
+
if (observation.planActive !== false)
|
|
131
|
+
return undefined;
|
|
132
|
+
if (observation.todos.kind === 'unavailable')
|
|
133
|
+
return undefined;
|
|
134
|
+
if (counters.remindedThisStep || counters.reminders >= this.config.maxRemindersPerTurn)
|
|
135
|
+
return undefined;
|
|
136
|
+
const open = observation.todos.kind === 'listed' ? observation.todos.todos.filter(isOpenTodo) : [];
|
|
137
|
+
const reminder = open.length > 0
|
|
138
|
+
? counters.steps >= this.config.staleSteps
|
|
139
|
+
? staleReminder(open, counters.steps, this.config.previewItems)
|
|
140
|
+
: undefined
|
|
141
|
+
: observation.todos.kind === 'absent' && counters.steps >= this.config.missingListSteps
|
|
142
|
+
? missingListReminder(counters.steps)
|
|
143
|
+
: undefined;
|
|
144
|
+
if (reminder === undefined)
|
|
145
|
+
return undefined;
|
|
146
|
+
counters.steps = 0;
|
|
147
|
+
counters.remindedThisStep = true;
|
|
148
|
+
counters.reminders += 1;
|
|
149
|
+
return reminder;
|
|
150
|
+
}
|
|
151
|
+
countersFor(session) {
|
|
152
|
+
let counters = this.counters.get(session);
|
|
153
|
+
if (counters === undefined) {
|
|
154
|
+
counters = { steps: 0, reminders: 0, remindedThisStep: false };
|
|
155
|
+
this.counters.set(session, counters);
|
|
156
|
+
}
|
|
157
|
+
return counters;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Prepend the guard's context while preserving every downstream context's own
|
|
162
|
+
* source, and carry it on a block as well so a denied call still reads it.
|
|
163
|
+
*/
|
|
164
|
+
export function foldReminder(reminder, downstream) {
|
|
165
|
+
const contexts = [reminder, ...downstream.additionalContexts ?? []];
|
|
166
|
+
if (downstream.kind === 'block') {
|
|
167
|
+
return { kind: 'block', feedback: downstream.feedback, additionalContexts: contexts };
|
|
168
|
+
}
|
|
169
|
+
return { ...downstream, additionalContexts: contexts };
|
|
170
|
+
}
|
|
171
|
+
function asRecord(value) {
|
|
172
|
+
return typeof value === 'object' && value !== null ? value : undefined;
|
|
173
|
+
}
|
|
174
|
+
const TODO_STATUSES = new Set(['pending', 'in_progress', 'completed']);
|
|
175
|
+
/** The current whole list from the harness projection. */
|
|
176
|
+
function readTodos(ctx, session) {
|
|
177
|
+
const read = projectionRead(ctx, session, TODOS_KEY);
|
|
178
|
+
// The projection is an array once the tool has written and `null` before
|
|
179
|
+
// that; an unregistered or unreadable unit silences us instead.
|
|
180
|
+
if (read.kind !== 'state')
|
|
181
|
+
return { kind: 'unavailable' };
|
|
182
|
+
const state = read.state;
|
|
183
|
+
if (state === null)
|
|
184
|
+
return { kind: 'absent' };
|
|
185
|
+
if (!Array.isArray(state))
|
|
186
|
+
return { kind: 'unavailable' };
|
|
187
|
+
const todos = [];
|
|
188
|
+
for (const item of state) {
|
|
189
|
+
const record = asRecord(item);
|
|
190
|
+
const content = record?.content;
|
|
191
|
+
const status = record?.status;
|
|
192
|
+
if (typeof content !== 'string')
|
|
193
|
+
continue;
|
|
194
|
+
if (typeof status !== 'string' || !TODO_STATUSES.has(status))
|
|
195
|
+
continue;
|
|
196
|
+
todos.push({ content, status: status });
|
|
197
|
+
}
|
|
198
|
+
return { kind: 'listed', todos };
|
|
199
|
+
}
|
|
200
|
+
/** Whether this session is in plan mode; `undefined` when that cannot be read. */
|
|
201
|
+
function readPlanActive(ctx, session) {
|
|
202
|
+
const read = projectionRead(ctx, session, PLAN_KEY);
|
|
203
|
+
// An unregistered key means plan mode is not composed for this agent, which is
|
|
204
|
+
// a definite "not in plan mode". A registered unit that cannot answer is not a
|
|
205
|
+
// definite anything, so it keeps the guard silent.
|
|
206
|
+
if (read.kind === 'unregistered')
|
|
207
|
+
return false;
|
|
208
|
+
if (read.kind !== 'state')
|
|
209
|
+
return undefined;
|
|
210
|
+
return asRecord(read.state)?.active === true;
|
|
211
|
+
}
|
|
212
|
+
/** Whether `todo_write` is reachable from this agent, so a tool-less preset stays silent. */
|
|
213
|
+
function todoToolAvailable(ctx, agent) {
|
|
214
|
+
try {
|
|
215
|
+
// `get` is presentation-agnostic: under PTC the model sees only `run_code`,
|
|
216
|
+
// but the sub-dispatch it programs still reaches `todo_write`, so a scan of
|
|
217
|
+
// the wire schemas would wrongly silence the guard in the default mode.
|
|
218
|
+
// Read live: a cached answer would outlive a registry that changed.
|
|
219
|
+
return ctx.tools.get(TODO_WRITE_TOOL, agent) !== undefined;
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// A torn-down scope answers nothing; the next call asks again rather than
|
|
223
|
+
// remembering a transient failure as a permanent silence.
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Install the guard's listeners.
|
|
229
|
+
*
|
|
230
|
+
* The list and plan come from the host projections rather than a private fold,
|
|
231
|
+
* so this reads exactly the lifetime every other surface shows. Absent
|
|
232
|
+
* projections mean silence, never an error, because the guard is decoration.
|
|
233
|
+
*/
|
|
234
|
+
export function apply(ctx, config = {}) {
|
|
235
|
+
const resolved = resolveTodoGuardConfig(config);
|
|
236
|
+
const guard = new TodoGuard(resolved);
|
|
237
|
+
ctx.on('session/event', (session, event) => { guard.onEvent(session, event.type); });
|
|
238
|
+
ctx.on('tools/post-execute', async (exec, result, next) => {
|
|
239
|
+
const agent = exec.agent;
|
|
240
|
+
// Observe before delegating so a downstream block cannot hide the fact that
|
|
241
|
+
// the step happened; the reminder then rides whatever decision comes back.
|
|
242
|
+
// A concluding result ends the turn at this step, so a context here would
|
|
243
|
+
// only buy one more step after the tools have already wound down.
|
|
244
|
+
const reminder = agent === undefined || result.concludesTurn === true ? undefined : guard.observe(agent.session, {
|
|
245
|
+
toolName: exec.name,
|
|
246
|
+
hasTodoTool: todoToolAvailable(ctx, agent),
|
|
247
|
+
planActive: readPlanActive(ctx, agent.session),
|
|
248
|
+
todos: readTodos(ctx, agent.session),
|
|
249
|
+
});
|
|
250
|
+
const downstream = await next();
|
|
251
|
+
if (reminder === undefined)
|
|
252
|
+
return downstream;
|
|
253
|
+
return foldReminder(createUserMessage({
|
|
254
|
+
content: [{ type: 'text', text: reminder.text }],
|
|
255
|
+
source: { ...PLUGIN_SOURCE, form: 'notice', summary: reminder.summary },
|
|
256
|
+
}), downstream);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=todo-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todo-guard.js","sourceRoot":"","sources":["../src/todo-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How one tool's cards draw.
|
|
3
|
+
*
|
|
4
|
+
* The reader owns this, because how much of a call a session shows is a
|
|
5
|
+
* preference about their own screen rather than something a tool can declare:
|
|
6
|
+
* the same command card is noise in one session and the answer in another.
|
|
7
|
+
*/
|
|
8
|
+
export interface ToolDisplaySpec {
|
|
9
|
+
/** Whether the tool's cards start folded to one header row. */
|
|
10
|
+
readonly collapsed: boolean;
|
|
11
|
+
/** What a folded card shows of the rows behind it. */
|
|
12
|
+
readonly output: ToolOutputDisplay;
|
|
13
|
+
/** Rows a folded card keeps when {@link ToolOutputDisplay} is `tail`. */
|
|
14
|
+
readonly tail: number;
|
|
15
|
+
}
|
|
16
|
+
/** A tool's row as it is read: only the fields the reader wrote, still writable while they are collected. */
|
|
17
|
+
export type WrittenToolDisplay = {
|
|
18
|
+
-readonly [K in keyof ToolDisplaySpec]?: ToolDisplaySpec[K];
|
|
19
|
+
};
|
|
20
|
+
/** The values the `output` key accepts, declared once for the schema and the refusal message. */
|
|
21
|
+
export declare const TOOL_OUTPUT_DISPLAYS: readonly ["hidden", "tail"];
|
|
22
|
+
export type ToolOutputDisplay = (typeof TOOL_OUTPUT_DISPLAYS)[number];
|
|
23
|
+
/** Bounds the schema and the refusal message share, so neither can drift from the other. */
|
|
24
|
+
export declare const TOOL_DISPLAY_LIMITS: {
|
|
25
|
+
readonly tail: {
|
|
26
|
+
readonly min: 0;
|
|
27
|
+
readonly max: 200;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/** What every tool draws when the reader has written nothing: one clipped line, no output. */
|
|
31
|
+
export declare const DEFAULT_TOOL_DISPLAY: ToolDisplaySpec;
|
|
32
|
+
/** The one name the block reserves: the row every tool without its own inherits. */
|
|
33
|
+
export declare const DEFAULT_TOOL_ENTRY = "default";
|
|
34
|
+
/** The `tools:` block once resolved: the default row, and every tool that named its own. */
|
|
35
|
+
export interface ToolDisplayTable {
|
|
36
|
+
readonly default: ToolDisplaySpec;
|
|
37
|
+
readonly tools: Readonly<Record<string, ToolDisplaySpec>>;
|
|
38
|
+
}
|
|
39
|
+
/** The display one tool draws with, falling back to the block's own default. */
|
|
40
|
+
export declare function toolDisplayFor(table: ToolDisplayTable, tool: string): ToolDisplaySpec;
|
|
41
|
+
/**
|
|
42
|
+
* Fill the fields a partial spec left out.
|
|
43
|
+
*
|
|
44
|
+
* A per-tool entry merges over the block's own `default` rather than over the
|
|
45
|
+
* shipped one, so a reader who writes `default: { output: tail }` keeps that
|
|
46
|
+
* choice for every tool that does not name its own.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveToolDisplay(partial: Partial<ToolDisplaySpec> | undefined, fallback?: ToolDisplaySpec): ToolDisplaySpec;
|
|
49
|
+
/**
|
|
50
|
+
* Build the table a view reads.
|
|
51
|
+
*
|
|
52
|
+
* Per-tool entries merge here rather than at read time: the view asks once per
|
|
53
|
+
* card per frame, and re-merging there would repeat the same answer for every
|
|
54
|
+
* row on screen.
|
|
55
|
+
*/
|
|
56
|
+
export declare function toolDisplayTable(specs?: Readonly<Record<string, Partial<ToolDisplaySpec>>>): ToolDisplayTable;
|
|
57
|
+
//# sourceMappingURL=tool-display.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-display.d.ts","sourceRoot":"","sources":["../src/tool-display.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;IAClC,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,6GAA6G;AAC7G,MAAM,MAAM,kBAAkB,GAAG;IAAE,CAAC,UAAU,CAAC,IAAI,MAAM,eAAe,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC;CAAE,CAAA;AAEhG,iGAAiG;AACjG,eAAO,MAAM,oBAAoB,6BAA8B,CAAA;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AAErE,4FAA4F;AAC5F,eAAO,MAAM,mBAAmB;;;;;CAEtB,CAAA;AAEV,8FAA8F;AAC9F,eAAO,MAAM,oBAAoB,EAAE,eAIlC,CAAA;AAED,oFAAoF;AACpF,eAAO,MAAM,kBAAkB,YAAY,CAAA;AAE3C,4FAA4F;AAC5F,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;IACjC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAA;CAC1D;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAErF;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,SAAS,EAC7C,QAAQ,GAAE,eAAsC,GAC/C,eAAe,CAMjB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAM,GAAG,gBAAgB,CAQjH"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { CARD_DETAIL_MAX, CARD_SHELL_PREVIEW } from "./cards.js";
|
|
2
|
+
/** The values the `output` key accepts, declared once for the schema and the refusal message. */
|
|
3
|
+
export const TOOL_OUTPUT_DISPLAYS = ['hidden', 'tail'];
|
|
4
|
+
/** Bounds the schema and the refusal message share, so neither can drift from the other. */
|
|
5
|
+
export const TOOL_DISPLAY_LIMITS = {
|
|
6
|
+
tail: { min: 0, max: CARD_DETAIL_MAX },
|
|
7
|
+
};
|
|
8
|
+
/** What every tool draws when the reader has written nothing: one clipped line, no output. */
|
|
9
|
+
export const DEFAULT_TOOL_DISPLAY = {
|
|
10
|
+
collapsed: true,
|
|
11
|
+
output: 'hidden',
|
|
12
|
+
tail: CARD_SHELL_PREVIEW,
|
|
13
|
+
};
|
|
14
|
+
/** The one name the block reserves: the row every tool without its own inherits. */
|
|
15
|
+
export const DEFAULT_TOOL_ENTRY = 'default';
|
|
16
|
+
/** The display one tool draws with, falling back to the block's own default. */
|
|
17
|
+
export function toolDisplayFor(table, tool) {
|
|
18
|
+
return table.tools[tool] ?? table.default;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Fill the fields a partial spec left out.
|
|
22
|
+
*
|
|
23
|
+
* A per-tool entry merges over the block's own `default` rather than over the
|
|
24
|
+
* shipped one, so a reader who writes `default: { output: tail }` keeps that
|
|
25
|
+
* choice for every tool that does not name its own.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveToolDisplay(partial, fallback = DEFAULT_TOOL_DISPLAY) {
|
|
28
|
+
return {
|
|
29
|
+
collapsed: partial?.collapsed ?? fallback.collapsed,
|
|
30
|
+
output: partial?.output ?? fallback.output,
|
|
31
|
+
tail: partial?.tail ?? fallback.tail,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build the table a view reads.
|
|
36
|
+
*
|
|
37
|
+
* Per-tool entries merge here rather than at read time: the view asks once per
|
|
38
|
+
* card per frame, and re-merging there would repeat the same answer for every
|
|
39
|
+
* row on screen.
|
|
40
|
+
*/
|
|
41
|
+
export function toolDisplayTable(specs = {}) {
|
|
42
|
+
const base = resolveToolDisplay(specs[DEFAULT_TOOL_ENTRY]);
|
|
43
|
+
const tools = {};
|
|
44
|
+
for (const [tool, partial] of Object.entries(specs)) {
|
|
45
|
+
if (tool === DEFAULT_TOOL_ENTRY)
|
|
46
|
+
continue;
|
|
47
|
+
tools[tool] = resolveToolDisplay(partial, base);
|
|
48
|
+
}
|
|
49
|
+
return { default: base, tools };
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=tool-display.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-display.js","sourceRoot":"","sources":["../src/tool-display.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAqBhE,iGAAiG;AACjG,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAU,CAAA;AAG/D,4FAA4F;AAC5F,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE;CAC9B,CAAA;AAEV,8FAA8F;AAC9F,MAAM,CAAC,MAAM,oBAAoB,GAAoB;IACnD,SAAS,EAAE,IAAI;IACf,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,kBAAkB;CACzB,CAAA;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,kBAAkB,GAAG,SAAS,CAAA;AAQ3C,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAAC,KAAuB,EAAE,IAAY;IAClE,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,CAAA;AAC3C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAA6C,EAC7C,WAA4B,oBAAoB;IAEhD,OAAO;QACL,SAAS,EAAE,OAAO,EAAE,SAAS,IAAI,QAAQ,CAAC,SAAS;QACnD,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,QAAQ,CAAC,MAAM;QAC1C,IAAI,EAAE,OAAO,EAAE,IAAI,IAAI,QAAQ,CAAC,IAAI;KACrC,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAA4D,EAAE;IAC7F,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAA;IAC1D,MAAM,KAAK,GAAoC,EAAE,CAAA;IACjD,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,IAAI,IAAI,KAAK,kBAAkB;YAAE,SAAQ;QACzC,KAAK,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IACjD,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;AACjC,CAAC"}
|
package/lib/transcript.d.ts
CHANGED
|
@@ -15,8 +15,10 @@ export type TranscriptEntry = {
|
|
|
15
15
|
} | {
|
|
16
16
|
readonly kind: 'tool';
|
|
17
17
|
readonly card: ToolCard;
|
|
18
|
+
readonly id: string;
|
|
18
19
|
} | {
|
|
19
20
|
readonly kind: 'reasoning';
|
|
21
|
+
readonly id: string;
|
|
20
22
|
readonly summary: string;
|
|
21
23
|
readonly body: string;
|
|
22
24
|
readonly live: boolean;
|
|
@@ -66,6 +68,14 @@ export declare class TranscriptModel {
|
|
|
66
68
|
* message, so the recorded copy is a repeat rather than a second thought.
|
|
67
69
|
*/
|
|
68
70
|
private reasoningPaintedThisStep;
|
|
71
|
+
/**
|
|
72
|
+
* Ids for thought rows, and the id the live thought will settle under.
|
|
73
|
+
*
|
|
74
|
+
* The counter never restarts: a click outlives the entry it was made on, and
|
|
75
|
+
* a reused id would hand that choice to a later session's thought.
|
|
76
|
+
*/
|
|
77
|
+
private thoughtSeq;
|
|
78
|
+
private liveReasoningId;
|
|
69
79
|
constructor(presenter?: ToolPresenter | undefined,
|
|
70
80
|
/** Clock for "how long has this been thinking"; injected so a test can pin it. */
|
|
71
81
|
now?: () => number);
|
|
@@ -111,6 +121,24 @@ export declare class TranscriptModel {
|
|
|
111
121
|
* never made.
|
|
112
122
|
*/
|
|
113
123
|
private applySubCall;
|
|
124
|
+
/**
|
|
125
|
+
* The row a settle leaves behind, once the outcome the call produced is known.
|
|
126
|
+
*
|
|
127
|
+
* A row that failed keeps only what still holds: its declared change never
|
|
128
|
+
* happened, so those rows go with the outcome that never came, while the
|
|
129
|
+
* outcome stays because the reason it failed is what a reader opens it to read.
|
|
130
|
+
*/
|
|
131
|
+
private settledSubCall;
|
|
132
|
+
/**
|
|
133
|
+
* The outcome a dispatched call opens to, from the view its result presented.
|
|
134
|
+
*
|
|
135
|
+
* A presenter can only rebuild the view it declared when the logged content is
|
|
136
|
+
* enough for it: a shell's output is, while a read's numbered window and a
|
|
137
|
+
* search's hits live in the metadata a dispatch does not carry. The fallback is
|
|
138
|
+
* the content the program was actually shown, so a row whose tool cannot be
|
|
139
|
+
* re-presented still opens to its outcome rather than to nothing.
|
|
140
|
+
*/
|
|
141
|
+
private subCallOutcome;
|
|
114
142
|
private settleToolResult;
|
|
115
143
|
}
|
|
116
144
|
//# sourceMappingURL=transcript.d.ts.map
|
package/lib/transcript.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+F,KAAK,QAAQ,EAAE,KAAK,aAAa,EAA0C,MAAM,YAAY,CAAA;AAKnM,qCAAqC;AACrC,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAEhI,yGAAyG;AACzG,eAAO,MAAM,oBAAoB,QAAS,CAAA;AAgB1C;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CACxB;AA2ED;;;;;;;;GAQG;AACH,qBAAa,eAAe;IA8BxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAC3B,kFAAkF;IAClF,OAAO,CAAC,QAAQ,CAAC,GAAG;IA/BtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiF;IAC5G,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,kBAAkB,CAAoB;IAC9C;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB,CAAQ;IACxC;;;;;OAKG;IACH,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,eAAe,CAAoB;gBAGxB,SAAS,CAAC,EAAE,aAAa,YAAA;IAC1C,kFAAkF;IACjE,GAAG,GAAE,MAAM,MAAyB;IAGvD,sFAAsF;IACtF,OAAO,IAAI,SAAS,eAAe,EAAE;IAkBrC;;;;;;OAMG;IACH,YAAY,IAAI,MAAM;IAItB,yEAAyE;IACzE,OAAO,IAAI,OAAO;IAIlB,KAAK,IAAI,IAAI;IAcb,gFAAgF;IAChF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1B,mFAAmF;IACnF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1B,kDAAkD;IAClD,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAiCtC,2FAA2F;IAC3F,OAAO,CAAC,aAAa;IAiBrB,8EAA8E;IAC9E,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAKjC;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAiBtB,kEAAkE;IAClE,OAAO,CAAC,eAAe;IAYvB,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAoGjC;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAoDpB;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAYtB;;;;;;;;OAQG;IACH,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,gBAAgB;CAyCzB"}
|