@itookit/dsht 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +4 -2
- package/README.zh.md +6 -4
- package/dist/catalog/controller.d.ts +26 -6
- package/dist/catalog/controller.js +73 -45
- package/dist/catalog/index.d.ts +1 -0
- package/dist/cli/dsht.js +4 -1
- package/dist/cli/startup.js +30 -11
- package/dist/cli/verifier.d.ts +4 -0
- package/dist/cli/verifier.js +28 -5
- package/dist/contracts.d.ts +20 -5
- package/dist/controller/connection-streams.d.ts +22 -0
- package/dist/controller/connection-streams.js +105 -0
- package/dist/controller/connection.d.ts +14 -3
- package/dist/controller/connection.js +40 -69
- package/dist/controller/controller.d.ts +17 -233
- package/dist/controller/controller.js +108 -811
- package/dist/controller/foreground.d.ts +44 -0
- package/dist/controller/foreground.js +79 -0
- package/dist/controller/loop-coordinator.d.ts +48 -0
- package/dist/controller/loop-coordinator.js +647 -0
- package/dist/controller/verifier.d.ts +4 -0
- package/dist/cost/controller.d.ts +1 -1
- package/dist/cost/controller.js +12 -5
- package/dist/cost/scanner.js +1 -0
- package/dist/session/controller.d.ts +23 -35
- package/dist/session/controller.js +113 -363
- package/dist/session/history-reader.d.ts +32 -0
- package/dist/session/history-reader.js +170 -0
- package/dist/session/index.d.ts +1 -1
- package/dist/session/info.d.ts +3 -38
- package/dist/session/info.js +14 -1
- package/dist/session/interactions.d.ts +26 -0
- package/dist/session/interactions.js +75 -0
- package/dist/session/navigator.d.ts +47 -0
- package/dist/session/navigator.js +158 -0
- package/dist/session/prompt-backfill.d.ts +23 -0
- package/dist/session/prompt-backfill.js +88 -0
- package/dist/session/state.d.ts +20 -0
- package/dist/session/state.js +1 -0
- package/dist/session/telemetry.d.ts +15 -6
- package/dist/session/telemetry.js +44 -7
- package/dist/session/transcript.d.ts +5 -1
- package/dist/slash/index.d.ts +1 -1
- package/dist/slash/parse.d.ts +2 -126
- package/dist/slash/registry.d.ts +1 -1
- package/dist/slash/types.d.ts +126 -0
- package/dist/slash/types.js +1 -0
- package/dist/state.d.ts +5 -17
- package/dist/state.js +1 -1
- package/dist/transport/client.d.ts +4 -3
- package/dist/transport/client.js +71 -25
- package/dist/ui/app.js +86 -301
- package/dist/ui/chat/shell-view.d.ts +2 -0
- package/dist/ui/chat/shell-view.js +8 -0
- package/dist/ui/chat/use-history-view.d.ts +69 -0
- package/dist/ui/chat/use-history-view.js +123 -0
- package/dist/ui/dialogs/use-panels.d.ts +53 -0
- package/dist/ui/dialogs/use-panels.js +51 -0
- package/dist/ui/input/use-composer.d.ts +35 -0
- package/dist/ui/input/use-composer.js +109 -0
- package/dist/ui/input/use-deferred-lines.d.ts +16 -0
- package/dist/ui/input/use-deferred-lines.js +54 -0
- package/dist/ui/input/use-history-recall.d.ts +20 -0
- package/dist/ui/input/use-history-recall.js +47 -0
- package/package.json +1 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** Bounded background recall indexing, owned by one selected record and connection lifetime. */
|
|
2
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
3
|
+
import { object } from "../json.js";
|
|
4
|
+
import { Transcript } from "./transcript.js";
|
|
5
|
+
const PAGE_LIMIT = 200;
|
|
6
|
+
export class PromptBackfill {
|
|
7
|
+
host;
|
|
8
|
+
cache;
|
|
9
|
+
tasks = new Set();
|
|
10
|
+
constructor(host, cache) {
|
|
11
|
+
this.host = host;
|
|
12
|
+
this.cache = cache;
|
|
13
|
+
}
|
|
14
|
+
cancel() { for (const task of this.tasks)
|
|
15
|
+
task.abort.abort(); }
|
|
16
|
+
async settle() { await Promise.allSettled([...this.tasks].map(task => task.promise)); }
|
|
17
|
+
start(selection) {
|
|
18
|
+
this.cancel();
|
|
19
|
+
const abort = new AbortController();
|
|
20
|
+
const signal = AbortSignal.any([abort.signal, this.host.signal()]);
|
|
21
|
+
const task = { abort, promise: Promise.resolve() };
|
|
22
|
+
// Register before dispatch, including before any reentrant host callback.
|
|
23
|
+
this.tasks.add(task);
|
|
24
|
+
task.promise = Promise.resolve().then(async () => {
|
|
25
|
+
const check = () => {
|
|
26
|
+
signal.throwIfAborted();
|
|
27
|
+
if (!this.host.online() || !this.host.current(selection))
|
|
28
|
+
throw new Error('Session changed while indexing prompts');
|
|
29
|
+
};
|
|
30
|
+
check();
|
|
31
|
+
const client = this.host.require();
|
|
32
|
+
const deadline = Date.now() + client.timeoutMs;
|
|
33
|
+
const { record, prompts, sessionId } = selection;
|
|
34
|
+
while (!record.ready) {
|
|
35
|
+
check();
|
|
36
|
+
if (Date.now() >= deadline)
|
|
37
|
+
throw new Error('Session snapshot timed out');
|
|
38
|
+
await delay(20, undefined, { signal });
|
|
39
|
+
}
|
|
40
|
+
check();
|
|
41
|
+
const adopt = () => {
|
|
42
|
+
const cached = this.cache.get(sessionId);
|
|
43
|
+
if (!cached?.complete)
|
|
44
|
+
return false;
|
|
45
|
+
prompts.prepend(cached.prompts);
|
|
46
|
+
prompts.settle();
|
|
47
|
+
prompts.markComplete();
|
|
48
|
+
this.host.changed();
|
|
49
|
+
return true;
|
|
50
|
+
};
|
|
51
|
+
if (adopt())
|
|
52
|
+
return;
|
|
53
|
+
const throughSeq = record.readThrough;
|
|
54
|
+
let beforeSeq = record.beforeSeq, hasMore = record.hasMore;
|
|
55
|
+
for (let page = 0; hasMore && beforeSeq !== undefined && page < PAGE_LIMIT; page++) {
|
|
56
|
+
check();
|
|
57
|
+
const result = object(await client.call('session/page', { request: {
|
|
58
|
+
address: { kind: 'session', sessionId }, throughSeq, beforeSeq, maxMessages: 80,
|
|
59
|
+
} }, signal));
|
|
60
|
+
check();
|
|
61
|
+
const temporary = new Transcript();
|
|
62
|
+
try {
|
|
63
|
+
temporary.accept({ type: 'snapshot', cursor: throughSeq, assistantStream: { revision: 0 }, records: result.records, hasMore: result.hasMore });
|
|
64
|
+
const next = temporary.beforeSeq;
|
|
65
|
+
if (temporary.hasMore && (next === undefined || next >= beforeSeq))
|
|
66
|
+
throw new Error('Host history page did not advance');
|
|
67
|
+
prompts.prepend(temporary.promptsSince(-1).prompts);
|
|
68
|
+
// Bound each admitted page, even when the next request fails or never returns.
|
|
69
|
+
prompts.settle();
|
|
70
|
+
beforeSeq = next;
|
|
71
|
+
hasMore = temporary.hasMore;
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
temporary.dispose();
|
|
75
|
+
}
|
|
76
|
+
if (adopt())
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!hasMore) {
|
|
80
|
+
prompts.markComplete();
|
|
81
|
+
this.cache.put(sessionId, { prompts: prompts.durableItems, complete: prompts.exhausted });
|
|
82
|
+
}
|
|
83
|
+
this.host.changed();
|
|
84
|
+
}).catch(() => {
|
|
85
|
+
// Cancellation or unavailable history leaves older prompts reachable through lazy paging.
|
|
86
|
+
}).finally(() => { this.tasks.delete(task); });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** State and publication capabilities owned by the session domain. */
|
|
2
|
+
import type { NavigationState } from './navigator.ts';
|
|
3
|
+
import type { SessionInfo } from './info.ts';
|
|
4
|
+
import type { PendingInteraction } from './types.ts';
|
|
5
|
+
export interface SessionState extends NavigationState {
|
|
6
|
+
online: boolean;
|
|
7
|
+
status: string;
|
|
8
|
+
lastFailure: string;
|
|
9
|
+
pending: PendingInteraction[];
|
|
10
|
+
session: SessionInfo;
|
|
11
|
+
}
|
|
12
|
+
/** The connection and application derive online/pending; session actions cannot overwrite them. */
|
|
13
|
+
export type SessionUpdate = Partial<Omit<SessionState, 'online' | 'pending' | 'session'>>;
|
|
14
|
+
export interface SessionStore {
|
|
15
|
+
readonly state: Readonly<SessionState>;
|
|
16
|
+
update(patch: SessionUpdate): void;
|
|
17
|
+
selection(): number;
|
|
18
|
+
bumpSelection(): void;
|
|
19
|
+
busy(): boolean;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -5,11 +5,24 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { ControlFrame, ProjectionSnapshot, ProjectionValue, QueuedInput } from '../transport/events.ts';
|
|
7
7
|
export type { QueuedInput } from '../transport/events.ts';
|
|
8
|
+
export interface TelemetrySnapshot {
|
|
9
|
+
readonly values: Readonly<Record<string, ProjectionValue>>;
|
|
10
|
+
readonly queued?: number;
|
|
11
|
+
readonly jobs?: number;
|
|
12
|
+
}
|
|
13
|
+
/** The public reader has no projection acceptance or baseline replacement capability. */
|
|
14
|
+
export interface TelemetryReader {
|
|
15
|
+
readonly ready: boolean;
|
|
16
|
+
view(id?: string): TelemetrySnapshot;
|
|
17
|
+
pending(id?: string): readonly Readonly<QueuedInput>[];
|
|
18
|
+
}
|
|
8
19
|
/** Generation-local projection, inbox and background-job data for every session. */
|
|
9
20
|
export declare class Telemetry {
|
|
10
21
|
private readonly retainedKeys?;
|
|
11
22
|
private entries;
|
|
12
23
|
private queues;
|
|
24
|
+
private readonly views;
|
|
25
|
+
readonly reader: TelemetryReader;
|
|
13
26
|
private jobs;
|
|
14
27
|
ready: boolean;
|
|
15
28
|
/** Optionally retain only projection capabilities consumed by this client. */
|
|
@@ -27,15 +40,11 @@ export declare class Telemetry {
|
|
|
27
40
|
* @param id - Selected session identity, if any.
|
|
28
41
|
* @returns Projection values and known queue/job counts.
|
|
29
42
|
*/
|
|
30
|
-
view(id?: string):
|
|
31
|
-
values: Readonly<Record<string, ProjectionValue>>;
|
|
32
|
-
queued?: number;
|
|
33
|
-
jobs?: number;
|
|
34
|
-
};
|
|
43
|
+
view(id?: string): TelemetrySnapshot;
|
|
35
44
|
/** Read the selected session's authoritative pending inputs; absence means no observed queue.
|
|
36
45
|
* @param id - Session identity.
|
|
37
46
|
* @returns Pending inputs in host order.
|
|
38
47
|
*/
|
|
39
|
-
pending(id?: string): readonly QueuedInput[];
|
|
48
|
+
pending(id?: string): readonly Readonly<QueuedInput>[];
|
|
40
49
|
private entry;
|
|
41
50
|
}
|
|
@@ -1,13 +1,40 @@
|
|
|
1
|
+
const EMPTY_VALUES = Object.freeze({});
|
|
2
|
+
const EMPTY_QUEUE = Object.freeze([]);
|
|
3
|
+
const EMPTY_VIEW = Object.freeze({ values: EMPTY_VALUES, queued: undefined, jobs: undefined });
|
|
4
|
+
/** Copy on admission, not on render; no published nested object aliases a wire frame. */
|
|
5
|
+
function retain(value) {
|
|
6
|
+
const copy = structuredClone(value);
|
|
7
|
+
const pending = [copy];
|
|
8
|
+
while (pending.length) {
|
|
9
|
+
const item = pending.pop();
|
|
10
|
+
if (item === null || typeof item !== 'object' || Object.isFrozen(item))
|
|
11
|
+
continue;
|
|
12
|
+
Object.freeze(item);
|
|
13
|
+
for (const child of Object.values(item))
|
|
14
|
+
pending.push(child);
|
|
15
|
+
}
|
|
16
|
+
return copy;
|
|
17
|
+
}
|
|
18
|
+
function retainQueue(items) {
|
|
19
|
+
return Object.freeze(items.map(item => Object.freeze({ ...item })));
|
|
20
|
+
}
|
|
1
21
|
/** Generation-local projection, inbox and background-job data for every session. */
|
|
2
22
|
export class Telemetry {
|
|
3
23
|
retainedKeys;
|
|
4
24
|
entries = new Map();
|
|
5
25
|
queues = new Map();
|
|
26
|
+
views = new Map();
|
|
27
|
+
reader;
|
|
6
28
|
jobs = new Map();
|
|
7
29
|
ready = false;
|
|
8
30
|
/** Optionally retain only projection capabilities consumed by this client. */
|
|
9
31
|
constructor(retainedKeys) {
|
|
10
32
|
this.retainedKeys = retainedKeys;
|
|
33
|
+
const telemetry = this;
|
|
34
|
+
this.reader = Object.freeze({
|
|
35
|
+
get ready() { return telemetry.ready; },
|
|
36
|
+
view: (id) => this.view(id), pending: (id) => this.pending(id),
|
|
37
|
+
});
|
|
11
38
|
}
|
|
12
39
|
/** Replace all control state on a new stream baseline, then accept replacement frames.
|
|
13
40
|
* @param frame - One normalized session/control frame.
|
|
@@ -17,10 +44,11 @@ export class Telemetry {
|
|
|
17
44
|
this.entries.clear();
|
|
18
45
|
this.queues.clear();
|
|
19
46
|
this.jobs.clear();
|
|
47
|
+
this.views.clear();
|
|
20
48
|
for (const [id, snapshot] of frame.projections)
|
|
21
49
|
this.snapshot(id, snapshot);
|
|
22
50
|
for (const [id, items] of frame.queues)
|
|
23
|
-
this.queues.set(id,
|
|
51
|
+
this.queues.set(id, retainQueue(items));
|
|
24
52
|
for (const [id, count] of frame.jobs)
|
|
25
53
|
this.jobs.set(id, count);
|
|
26
54
|
this.ready = true;
|
|
@@ -34,13 +62,14 @@ export class Telemetry {
|
|
|
34
62
|
return;
|
|
35
63
|
if (this.retainedKeys && !this.retainedKeys.has(frame.key))
|
|
36
64
|
return;
|
|
37
|
-
entry.values[frame.key] = frame.value;
|
|
65
|
+
entry.values[frame.key] = retain(frame.value);
|
|
38
66
|
entry.revisions.set(frame.key, frame.seq);
|
|
39
67
|
}
|
|
40
68
|
else if (frame.kind === 'queue')
|
|
41
|
-
this.queues.set(frame.sessionId,
|
|
69
|
+
this.queues.set(frame.sessionId, retainQueue(frame.items));
|
|
42
70
|
else
|
|
43
71
|
this.jobs.set(frame.sessionId, frame.count);
|
|
72
|
+
this.views.delete(frame.sessionId);
|
|
44
73
|
}
|
|
45
74
|
/** Merge a complete follow snapshot without restoring absent or older projection values.
|
|
46
75
|
* @param id - Session identity.
|
|
@@ -58,26 +87,34 @@ export class Telemetry {
|
|
|
58
87
|
if ((entry.revisions.get(key) ?? -1) > baseline.asOfSeq)
|
|
59
88
|
continue;
|
|
60
89
|
if (Object.hasOwn(baseline.values, key))
|
|
61
|
-
entry.values[key] = baseline.values[key];
|
|
90
|
+
entry.values[key] = retain(baseline.values[key]);
|
|
62
91
|
else
|
|
63
92
|
delete entry.values[key];
|
|
64
93
|
entry.revisions.delete(key);
|
|
65
94
|
}
|
|
66
95
|
entry.baseline = baseline.asOfSeq;
|
|
96
|
+
this.views.delete(id);
|
|
67
97
|
}
|
|
68
98
|
/** Read current values for one session; missing capabilities remain absent.
|
|
69
99
|
* @param id - Selected session identity, if any.
|
|
70
100
|
* @returns Projection values and known queue/job counts.
|
|
71
101
|
*/
|
|
72
102
|
view(id) {
|
|
73
|
-
|
|
74
|
-
|
|
103
|
+
if (!id || !this.entries.has(id) && !this.queues.has(id) && !this.jobs.has(id))
|
|
104
|
+
return EMPTY_VIEW;
|
|
105
|
+
let view = this.views.get(id);
|
|
106
|
+
if (!view) {
|
|
107
|
+
view = Object.freeze({ values: Object.freeze({ ...this.entries.get(id)?.values }),
|
|
108
|
+
queued: this.queues.get(id)?.length, jobs: this.jobs.get(id) });
|
|
109
|
+
this.views.set(id, view);
|
|
110
|
+
}
|
|
111
|
+
return view;
|
|
75
112
|
}
|
|
76
113
|
/** Read the selected session's authoritative pending inputs; absence means no observed queue.
|
|
77
114
|
* @param id - Session identity.
|
|
78
115
|
* @returns Pending inputs in host order.
|
|
79
116
|
*/
|
|
80
|
-
pending(id) { return id ? this.queues.get(id) ??
|
|
117
|
+
pending(id) { return id ? this.queues.get(id) ?? EMPTY_QUEUE : EMPTY_QUEUE; }
|
|
81
118
|
entry(id) {
|
|
82
119
|
let entry = this.entries.get(id);
|
|
83
120
|
if (!entry) {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { HistoryLimits } from './memory.ts';
|
|
2
|
-
import type { PromptRecord } from './info.ts';
|
|
3
2
|
import { type Json, type ObjectValue } from '../transport/wire.ts';
|
|
3
|
+
/** A durable user prompt projected from the transcript, before recall retention. */
|
|
4
|
+
export interface PromptRecord {
|
|
5
|
+
seq: number;
|
|
6
|
+
text: string;
|
|
7
|
+
}
|
|
4
8
|
interface ToolSummary {
|
|
5
9
|
name: string;
|
|
6
10
|
operation?: string;
|
package/dist/slash/index.d.ts
CHANGED
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
export { COMMAND_HINTS, COMMAND_LABELS, COMMAND_LABEL_WIDTH, COMMANDS, COMMAND_POLICY, argumentHint, commandMatches, commonPrefix, completeCommand, resolveCommand, suggestedCommands } from './registry.ts';
|
|
6
6
|
export type { CommandHint, CommandPolicy } from './registry.ts';
|
|
7
7
|
export { parseCommand, loopNameQuery, validLoopOption, LOOP_ABORT_USAGE, LOOP_ANSWER_USAGE, LOOP_STOP_USAGE, LOOP_USAGE } from './parse.ts';
|
|
8
|
-
export type { Command, LoopOptions } from './
|
|
8
|
+
export type { Command, LoopOptions } from './types.ts';
|
|
9
9
|
export { interpret, normalize, authorize } from './pipeline.ts';
|
|
10
10
|
export type { AuthorizeFacts, DeferReason, ExecutableSubmission, InterpretFacts, LineCommand, NormalizeFacts, Submission, UiAction, Verdict } from './pipeline.ts';
|
package/dist/slash/parse.d.ts
CHANGED
|
@@ -1,128 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
export type Command
|
|
3
|
-
kind: 'ignore';
|
|
4
|
-
} | {
|
|
5
|
-
kind: 'copy';
|
|
6
|
-
} | {
|
|
7
|
-
kind: 'quit';
|
|
8
|
-
} | {
|
|
9
|
-
kind: 'panel';
|
|
10
|
-
panel: 'cost' | 'status' | 'help';
|
|
11
|
-
} | {
|
|
12
|
-
kind: 'remove';
|
|
13
|
-
target: 'workspace' | 'session';
|
|
14
|
-
query: string;
|
|
15
|
-
} | {
|
|
16
|
-
kind: 'navigate';
|
|
17
|
-
target: 'workspace' | 'session';
|
|
18
|
-
query?: string;
|
|
19
|
-
} | {
|
|
20
|
-
kind: 'latest';
|
|
21
|
-
} | {
|
|
22
|
-
kind: 'models';
|
|
23
|
-
args: string[];
|
|
24
|
-
} | {
|
|
25
|
-
kind: 'queue';
|
|
26
|
-
} | {
|
|
27
|
-
kind: 'newSession';
|
|
28
|
-
}
|
|
29
|
-
/** Open the saved-prompt picker, or save the following text as a shortcut prompt. */
|
|
30
|
-
| {
|
|
31
|
-
kind: 'prompts';
|
|
32
|
-
} | {
|
|
33
|
-
kind: 'savePrompt';
|
|
34
|
-
text: string;
|
|
35
|
-
} | {
|
|
36
|
-
kind: 'history';
|
|
37
|
-
query: string;
|
|
38
|
-
} | {
|
|
39
|
-
kind: 'sessionSearch';
|
|
40
|
-
command: '/ssearch' | '/wsearch';
|
|
41
|
-
query: string;
|
|
42
|
-
} | {
|
|
43
|
-
kind: 'historySearch';
|
|
44
|
-
query: string;
|
|
45
|
-
} | {
|
|
46
|
-
kind: 'think';
|
|
47
|
-
target: string;
|
|
48
|
-
} | {
|
|
49
|
-
kind: 'older';
|
|
50
|
-
} | {
|
|
51
|
-
kind: 'compact';
|
|
52
|
-
}
|
|
53
|
-
/** Clear the client's own HANDOFF.md, then ask the agent to write a fresh session handoff. */
|
|
54
|
-
| {
|
|
55
|
-
kind: 'handoff';
|
|
56
|
-
}
|
|
57
|
-
/** Start the client-driven scored loop for one `loop.yaml` record. */
|
|
58
|
-
| {
|
|
59
|
-
kind: 'loop';
|
|
60
|
-
name: string;
|
|
61
|
-
options: LoopOptions;
|
|
62
|
-
}
|
|
63
|
-
/** Offer the `loop.yaml` records so one can be chosen instead of typed. */
|
|
64
|
-
| {
|
|
65
|
-
kind: 'loops';
|
|
66
|
-
}
|
|
67
|
-
/** Stop the running loop, running or paused. */
|
|
68
|
-
| {
|
|
69
|
-
kind: 'loopStop';
|
|
70
|
-
}
|
|
71
|
-
/** Answer a paused run, so the current artifact is judged again with the operator's addition. */
|
|
72
|
-
| {
|
|
73
|
-
kind: 'loopAnswer';
|
|
74
|
-
text: string;
|
|
75
|
-
} | {
|
|
76
|
-
kind: 'cancel';
|
|
77
|
-
} | {
|
|
78
|
-
kind: 'approval';
|
|
79
|
-
allowed: boolean;
|
|
80
|
-
} | {
|
|
81
|
-
kind: 'hostCommand';
|
|
82
|
-
line: string;
|
|
83
|
-
}
|
|
84
|
-
/** A local `!` command, run on this machine rather than the host. */
|
|
85
|
-
| {
|
|
86
|
-
kind: 'shell';
|
|
87
|
-
command: string;
|
|
88
|
-
} | {
|
|
89
|
-
kind: 'export';
|
|
90
|
-
destination?: string;
|
|
91
|
-
} | {
|
|
92
|
-
kind: 'exportHtml';
|
|
93
|
-
destination?: string;
|
|
94
|
-
} | {
|
|
95
|
-
kind: 'coredump';
|
|
96
|
-
tag?: string;
|
|
97
|
-
} | {
|
|
98
|
-
kind: 'error';
|
|
99
|
-
message: string;
|
|
100
|
-
} | {
|
|
101
|
-
kind: 'prompt';
|
|
102
|
-
text: string;
|
|
103
|
-
};
|
|
104
|
-
/** Options carried by `/loop`.
|
|
105
|
-
*
|
|
106
|
-
* Only the fields the operator actually typed are present, so the syntax layer validates each value
|
|
107
|
-
* it sees and the application owns the defaults — command line first, then the record's `defaults`,
|
|
108
|
-
* then the global ones.
|
|
109
|
-
*/
|
|
110
|
-
export interface LoopOptions {
|
|
111
|
-
/** First step to run; default 1. */
|
|
112
|
-
from?: number;
|
|
113
|
-
/** Last step to run; default the record's last step. */
|
|
114
|
-
to?: number;
|
|
115
|
-
/** Passing score per step, 0-10 and possibly fractional; default the record's, else 8. */
|
|
116
|
-
score?: number;
|
|
117
|
-
/** Attempts allowed per step; default the record's, else 10. */
|
|
118
|
-
tries?: number;
|
|
119
|
-
/** Values that replace the record's own `vars` for this run, such as the document under review.
|
|
120
|
-
*
|
|
121
|
-
* The interactive form fills this in; the command line has no syntax for it, because the record —
|
|
122
|
-
* not the syntax layer — is what knows which names exist.
|
|
123
|
-
*/
|
|
124
|
-
vars?: Readonly<Record<string, string>>;
|
|
125
|
-
}
|
|
1
|
+
import type { Command } from './types.ts';
|
|
2
|
+
export type { Command, LoopOptions } from './types.ts';
|
|
126
3
|
/** The numeric options a flag can carry; `vars` is not one, so it stays out of the flag table. */
|
|
127
4
|
type LoopNumberOption = 'from' | 'to' | 'score' | 'tries';
|
|
128
5
|
/** Whether one numeric value is acceptable for one loop option.
|
|
@@ -163,4 +40,3 @@ export declare const LOOP_ABORT_USAGE = "Use /loop abort (no arguments)";
|
|
|
163
40
|
* @returns The parsed command.
|
|
164
41
|
*/
|
|
165
42
|
export declare function parseCommand(line: string): Command;
|
|
166
|
-
export {};
|
package/dist/slash/registry.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Slash-command catalog shared by completion, `/help` and the submission router. */
|
|
2
|
-
import type { Command } from './
|
|
2
|
+
import type { Command } from './types.ts';
|
|
3
3
|
/** One slash command advertised by completion and `/help`. */
|
|
4
4
|
export interface CommandHint {
|
|
5
5
|
/** Slash command as typed without arguments. */
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/** Command values shared by syntax, discovery and admission; no parser dependency. */
|
|
2
|
+
/** One parsed command; every side effect stays with the caller. */
|
|
3
|
+
export type Command = {
|
|
4
|
+
kind: 'ignore';
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'copy';
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'quit';
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'panel';
|
|
11
|
+
panel: 'cost' | 'status' | 'help';
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'remove';
|
|
14
|
+
target: 'workspace' | 'session';
|
|
15
|
+
query: string;
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'navigate';
|
|
18
|
+
target: 'workspace' | 'session';
|
|
19
|
+
query?: string;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'latest';
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'models';
|
|
24
|
+
args: string[];
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'queue';
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'newSession';
|
|
29
|
+
}
|
|
30
|
+
/** Open the saved-prompt picker, or save the following text as a shortcut prompt. */
|
|
31
|
+
| {
|
|
32
|
+
kind: 'prompts';
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'savePrompt';
|
|
35
|
+
text: string;
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'history';
|
|
38
|
+
query: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'sessionSearch';
|
|
41
|
+
command: '/ssearch' | '/wsearch';
|
|
42
|
+
query: string;
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'historySearch';
|
|
45
|
+
query: string;
|
|
46
|
+
} | {
|
|
47
|
+
kind: 'think';
|
|
48
|
+
target: string;
|
|
49
|
+
} | {
|
|
50
|
+
kind: 'older';
|
|
51
|
+
} | {
|
|
52
|
+
kind: 'compact';
|
|
53
|
+
}
|
|
54
|
+
/** Clear the client's own HANDOFF.md, then ask the agent to write a fresh session handoff. */
|
|
55
|
+
| {
|
|
56
|
+
kind: 'handoff';
|
|
57
|
+
}
|
|
58
|
+
/** Start the client-driven scored loop for one `loop.yaml` record. */
|
|
59
|
+
| {
|
|
60
|
+
kind: 'loop';
|
|
61
|
+
name: string;
|
|
62
|
+
options: LoopOptions;
|
|
63
|
+
}
|
|
64
|
+
/** Offer the `loop.yaml` records so one can be chosen instead of typed. */
|
|
65
|
+
| {
|
|
66
|
+
kind: 'loops';
|
|
67
|
+
}
|
|
68
|
+
/** Stop the running loop, running or paused. */
|
|
69
|
+
| {
|
|
70
|
+
kind: 'loopStop';
|
|
71
|
+
}
|
|
72
|
+
/** Answer a paused run, so the current artifact is judged again with the operator's addition. */
|
|
73
|
+
| {
|
|
74
|
+
kind: 'loopAnswer';
|
|
75
|
+
text: string;
|
|
76
|
+
} | {
|
|
77
|
+
kind: 'cancel';
|
|
78
|
+
} | {
|
|
79
|
+
kind: 'approval';
|
|
80
|
+
allowed: boolean;
|
|
81
|
+
} | {
|
|
82
|
+
kind: 'hostCommand';
|
|
83
|
+
line: string;
|
|
84
|
+
}
|
|
85
|
+
/** A local `!` command, run on this machine rather than the host. */
|
|
86
|
+
| {
|
|
87
|
+
kind: 'shell';
|
|
88
|
+
command: string;
|
|
89
|
+
} | {
|
|
90
|
+
kind: 'export';
|
|
91
|
+
destination?: string;
|
|
92
|
+
} | {
|
|
93
|
+
kind: 'exportHtml';
|
|
94
|
+
destination?: string;
|
|
95
|
+
} | {
|
|
96
|
+
kind: 'coredump';
|
|
97
|
+
tag?: string;
|
|
98
|
+
} | {
|
|
99
|
+
kind: 'error';
|
|
100
|
+
message: string;
|
|
101
|
+
} | {
|
|
102
|
+
kind: 'prompt';
|
|
103
|
+
text: string;
|
|
104
|
+
};
|
|
105
|
+
/** Options carried by `/loop`.
|
|
106
|
+
*
|
|
107
|
+
* Only the fields the operator actually typed are present, so the syntax layer validates each value
|
|
108
|
+
* it sees and the application owns the defaults — command line first, then the record's `defaults`,
|
|
109
|
+
* then the global ones.
|
|
110
|
+
*/
|
|
111
|
+
export interface LoopOptions {
|
|
112
|
+
/** First step to run; default 1. */
|
|
113
|
+
from?: number;
|
|
114
|
+
/** Last step to run; default the record's last step. */
|
|
115
|
+
to?: number;
|
|
116
|
+
/** Passing score per step, 0-10 and possibly fractional; default the record's, else 8. */
|
|
117
|
+
score?: number;
|
|
118
|
+
/** Attempts allowed per step; default the record's, else 10. */
|
|
119
|
+
tries?: number;
|
|
120
|
+
/** Values that replace the record's own `vars` for this run, such as the document under review.
|
|
121
|
+
*
|
|
122
|
+
* The interactive form fills this in; the command line has no syntax for it, because the record —
|
|
123
|
+
* not the syntax layer — is what knows which names exist.
|
|
124
|
+
*/
|
|
125
|
+
vars?: Readonly<Record<string, string>>;
|
|
126
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/state.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import type { HistorySearch, PendingInteraction, RemovalTarget } from './session/types.ts';
|
|
1
|
+
import type { HistorySearch, RemovalTarget } from './session/types.ts';
|
|
2
|
+
import type { SessionState } from './session/state.ts';
|
|
4
3
|
import type { ShellSnapshot } from './shell/index.ts';
|
|
5
4
|
import type { ObjectValue } from './transport/wire.ts';
|
|
6
5
|
/** State shared by the picker and conversation view. */
|
|
7
|
-
export interface State {
|
|
6
|
+
export interface State extends SessionState {
|
|
8
7
|
version: number;
|
|
9
8
|
/** The last failure the application recorded, for a diagnostic line and a refusal's reason.
|
|
10
9
|
*
|
|
@@ -13,15 +12,6 @@ export interface State {
|
|
|
13
12
|
* Whether an operation is running is not stored here — that is `queries.foreground`.
|
|
14
13
|
*/
|
|
15
14
|
lastFailure: string;
|
|
16
|
-
online: boolean;
|
|
17
|
-
screen: 'workspaces' | 'sessions' | 'chat' | 'path';
|
|
18
|
-
status: string;
|
|
19
|
-
workspaces: ObjectValue[];
|
|
20
|
-
sessions: ObjectValue[];
|
|
21
|
-
showAllSessions: boolean;
|
|
22
|
-
workspaceId?: string;
|
|
23
|
-
sessionId?: string;
|
|
24
|
-
pending: PendingInteraction[];
|
|
25
15
|
controlError?: string;
|
|
26
16
|
modelError?: string;
|
|
27
17
|
presetError?: string;
|
|
@@ -29,12 +19,10 @@ export interface State {
|
|
|
29
19
|
defaultModel?: ObjectValue;
|
|
30
20
|
/** Local `!` runs, published so the UI never reads the shell service object. */
|
|
31
21
|
shell: ShellSnapshot;
|
|
32
|
-
/** Record, prompt index, composer, view and interaction state of the selected session. */
|
|
33
|
-
session: SessionInfo;
|
|
34
22
|
}
|
|
35
|
-
/**
|
|
23
|
+
/** Application state access used by connection orchestration. Feature domains use their own ports. */
|
|
36
24
|
export interface ControllerStore {
|
|
37
|
-
/** Current
|
|
25
|
+
/** Current application snapshot; nested session data remains owned by the session domain. */
|
|
38
26
|
readonly state: State;
|
|
39
27
|
/** Publish a state patch and notify observers. */
|
|
40
28
|
update(patch: Partial<State>): void;
|
package/dist/state.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Application
|
|
1
|
+
/** Application composition of session state and the other domains' published snapshots. */
|
|
2
2
|
import { SessionInfo } from "./session/info.js";
|
|
3
3
|
/** Build the initial state before any connection exists.
|
|
4
4
|
* @returns A fresh state whose transcript is empty and disconnected.
|
|
@@ -18,7 +18,7 @@ interface Listener {
|
|
|
18
18
|
item(value: Json | undefined): void;
|
|
19
19
|
end(error?: Error): void;
|
|
20
20
|
}
|
|
21
|
-
/**
|
|
21
|
+
/** One client lifetime. Peer disconnects allow reconnect; close permanently ends the lifetime. */
|
|
22
22
|
export declare class Client {
|
|
23
23
|
readonly timeoutMs: number;
|
|
24
24
|
readonly base: URL;
|
|
@@ -27,6 +27,7 @@ export declare class Client {
|
|
|
27
27
|
private socket;
|
|
28
28
|
private listeners;
|
|
29
29
|
private lifetime;
|
|
30
|
+
private closeTask?;
|
|
30
31
|
constructor(base: string, timeoutMs?: number);
|
|
31
32
|
/** Exchange a startup token only at GET / and retain the server's cookie expiration. */
|
|
32
33
|
authenticate(token: string): Promise<void>;
|
|
@@ -58,9 +59,9 @@ export declare class Client {
|
|
|
58
59
|
/** Archived session IDs from the latest authoritative workspace baseline. */
|
|
59
60
|
archivedSessionIds: ReadonlySet<string>;
|
|
60
61
|
/** List workspaces by consuming and cancelling the authoritative opening baseline. */
|
|
61
|
-
listWorkspaces(): Promise<ObjectValue[]>;
|
|
62
|
+
listWorkspaces(signal?: AbortSignal): Promise<ObjectValue[]>;
|
|
62
63
|
/** List visible sessions, optionally filtering by the workspace's accounted IDs. */
|
|
63
|
-
listSessions(workspaceId?: string): Promise<ObjectValue[]>;
|
|
64
|
+
listSessions(workspaceId?: string, signal?: AbortSignal): Promise<ObjectValue[]>;
|
|
64
65
|
/** Close all streams, abort in-flight HTTP, and await the physical socket's closure. */
|
|
65
66
|
close(): Promise<void>;
|
|
66
67
|
private signal;
|