@intentic/sandbox-contract 1.329.0 → 1.330.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/dist/chores/probes.d.ts.map +1 -1
- package/dist/chores/probes.js +1 -2
- package/dist/chores/probes.js.map +1 -1
- package/dist/contracts/agents.contract.d.ts +69 -0
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.d.ts +6 -0
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.js +2 -1
- package/dist/contracts/device.contract.js.map +1 -1
- package/dist/contracts/runs.contract.d.ts +35 -0
- package/dist/contracts/runs.contract.d.ts.map +1 -0
- package/dist/contracts/runs.contract.js +22 -0
- package/dist/contracts/runs.contract.js.map +1 -0
- package/dist/contracts/settings.contract.d.ts +8 -0
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +42 -30
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +10 -1
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +37 -0
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js +11 -1
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/events/system-events.d.ts +8 -0
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/index.d.ts +165 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/protocol/doors/listener-protocol.d.ts +0 -1
- package/dist/protocol/doors/listener-protocol.d.ts.map +1 -1
- package/dist/protocol/doors/listener-protocol.js +3 -2
- package/dist/protocol/doors/listener-protocol.js.map +1 -1
- package/dist/protocol/raw/raw-routes.d.ts +25 -0
- package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw/raw-routes.js +7 -0
- package/dist/protocol/raw/raw-routes.js.map +1 -1
- package/dist/schemas/agents-list.d.ts +4 -0
- package/dist/schemas/agents-list.d.ts.map +1 -1
- package/dist/schemas/agents.d.ts +13 -0
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +4 -3
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/device-artifacts.d.ts +13 -0
- package/dist/schemas/device-artifacts.d.ts.map +1 -1
- package/dist/schemas/device-artifacts.js +13 -0
- package/dist/schemas/device-artifacts.js.map +1 -1
- package/dist/schemas/run-targets.d.ts +82 -0
- package/dist/schemas/run-targets.d.ts.map +1 -0
- package/dist/schemas/run-targets.js +49 -0
- package/dist/schemas/run-targets.js.map +1 -0
- package/dist/schemas/settings.d.ts +8 -0
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +2 -1
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/speech.d.ts +75 -0
- package/dist/schemas/speech.d.ts.map +1 -0
- package/dist/schemas/speech.js +66 -0
- package/dist/schemas/speech.js.map +1 -0
- package/dist/schemas/terminal.d.ts +4 -0
- package/dist/schemas/terminal.d.ts.map +1 -1
- package/dist/schemas/terminal.js +4 -0
- package/dist/schemas/terminal.js.map +1 -1
- package/dist/schemas/turn-break.d.ts +7 -1
- package/dist/schemas/turn-break.d.ts.map +1 -1
- package/dist/schemas/turn-break.js +4 -2
- package/dist/schemas/turn-break.js.map +1 -1
- package/dist/schemas/workspace/workspace-graph.d.ts +133 -0
- package/dist/schemas/workspace/workspace-graph.d.ts.map +1 -0
- package/dist/schemas/workspace/workspace-graph.js +83 -0
- package/dist/schemas/workspace/workspace-graph.js.map +1 -0
- package/dist/schemas/workspace/workspace-repos.d.ts +0 -39
- package/dist/schemas/workspace/workspace-repos.d.ts.map +1 -1
- package/dist/schemas/workspace/workspace-repos.js +0 -15
- package/dist/schemas/workspace/workspace-repos.js.map +1 -1
- package/dist/state/definition.d.ts +4 -0
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/workspace-state.d.ts +2 -1
- package/dist/state/workspace-state.d.ts.map +1 -1
- package/dist/state/workspace-state.js +5 -1
- package/dist/state/workspace-state.js.map +1 -1
- package/dist/text/transcript-page.d.ts +5 -0
- package/dist/text/transcript-page.d.ts.map +1 -0
- package/dist/text/transcript-page.js +17 -0
- package/dist/text/transcript-page.js.map +1 -0
- package/package.json +5 -5
- package/src/chores/probes.ts +1 -2
- package/src/contracts/device.contract.ts +6 -1
- package/src/contracts/runs.contract.ts +26 -0
- package/src/contracts/system.contract.ts +13 -0
- package/src/contracts/workspace.contract.ts +12 -1
- package/src/index.ts +7 -1
- package/src/models/agent-runtimes.ts +2 -2
- package/src/protocol/doors/listener-protocol.ts +5 -4
- package/src/protocol/raw/raw-routes.ts +12 -0
- package/src/schemas/agents.ts +6 -3
- package/src/schemas/device-artifacts.ts +21 -0
- package/src/schemas/run-targets.ts +93 -0
- package/src/schemas/settings.ts +6 -1
- package/src/schemas/speech.ts +96 -0
- package/src/schemas/terminal.ts +4 -0
- package/src/schemas/turn-break.ts +18 -9
- package/src/schemas/workspace/workspace-graph.ts +116 -0
- package/src/schemas/workspace/workspace-repos.ts +0 -22
- package/src/state/workspace-state.test.ts +42 -1
- package/src/state/workspace-state.ts +12 -1
- package/src/text/transcript-page.test.ts +39 -0
- package/src/text/transcript-page.ts +37 -0
- package/dist/text/whisper.d.ts +0 -3
- package/dist/text/whisper.d.ts.map +0 -1
- package/dist/text/whisper.js +0 -11
- package/dist/text/whisper.js.map +0 -1
- package/src/text/whisper.test.ts +0 -18
- package/src/text/whisper.ts +0 -17
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// Dictation on this sandbox: which local model hears a language, how far it is from ready, and the live wire the
|
|
4
|
+
// composer streams a spoken phrase over. The daemon's engine is src/speech; the composer's half is
|
|
5
|
+
// _editor/web/src/features/chat/composer/voice.
|
|
6
|
+
|
|
7
|
+
// The models a sandbox can hear with, by what they are for:
|
|
8
|
+
// - `parakeet` (NVIDIA Parakeet TDT 0.6B v3): 25 European languages, punctuated, ~30x faster than real time on two
|
|
9
|
+
// cores. Baked into the standard image, and the default for any language it covers.
|
|
10
|
+
// - `whisper` (OpenAI Whisper large-v3-turbo): the other ~75 languages, slower, fetched the first time one is asked for.
|
|
11
|
+
export const SpeechEngineSchema = z.enum(["parakeet", "whisper"]);
|
|
12
|
+
export type SpeechEngine = z.infer<typeof SpeechEngineSchema>;
|
|
13
|
+
|
|
14
|
+
// The 25 Parakeet hears, as ISO 639-1 primary subtags: it detects which on its own, so a browser in English and a
|
|
15
|
+
// speaker in Polish still land on it.
|
|
16
|
+
export const PARAKEET_LANGUAGES: ReadonlySet<string> = new Set([
|
|
17
|
+
"bg",
|
|
18
|
+
"cs",
|
|
19
|
+
"da",
|
|
20
|
+
"de",
|
|
21
|
+
"el",
|
|
22
|
+
"en",
|
|
23
|
+
"es",
|
|
24
|
+
"et",
|
|
25
|
+
"fi",
|
|
26
|
+
"fr",
|
|
27
|
+
"hr",
|
|
28
|
+
"hu",
|
|
29
|
+
"it",
|
|
30
|
+
"lt",
|
|
31
|
+
"lv",
|
|
32
|
+
"mt",
|
|
33
|
+
"nl",
|
|
34
|
+
"pl",
|
|
35
|
+
"pt",
|
|
36
|
+
"ro",
|
|
37
|
+
"ru",
|
|
38
|
+
"sk",
|
|
39
|
+
"sl",
|
|
40
|
+
"sv",
|
|
41
|
+
"uk",
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
// A locale's primary subtag, lowercased, or undefined for anything that is not one (`""`, `*`, `auto`).
|
|
45
|
+
export const speechLanguage = (locale: string | undefined): string | undefined => {
|
|
46
|
+
const primary = (locale ?? "").trim().toLowerCase().split(/[-_]/u)[0] ?? "";
|
|
47
|
+
return /^[a-z]{2,3}$/u.test(primary) && primary !== "auto" ? primary : undefined;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Which model hears a locale: Parakeet for its own languages and for anything unreadable, Whisper for the rest.
|
|
51
|
+
export const speechEngineFor = (locale: string | undefined): SpeechEngine => {
|
|
52
|
+
const language = speechLanguage(locale);
|
|
53
|
+
return language === undefined || PARAKEET_LANGUAGES.has(language) ? "parakeet" : "whisper";
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const SpeechStatusSchema = z.object({
|
|
57
|
+
provisioned: z.boolean().describe("This sandbox can run speech recognition at all."),
|
|
58
|
+
model: z
|
|
59
|
+
.enum(["absent", "downloading", "ready", "failed"])
|
|
60
|
+
.describe("Whether the model for the asked language is on disk: baked into the image, fetched, being fetched, or its fetch failed."),
|
|
61
|
+
engine: SpeechEngineSchema.optional().describe("Which model hears the asked language."),
|
|
62
|
+
loaded: z.boolean().optional().describe("The model is in memory, so the next phrase is heard without a load first."),
|
|
63
|
+
received: z.number().optional().describe("Bytes of the model fetched so far, while it downloads."),
|
|
64
|
+
total: z.number().optional().describe("The model's whole size in bytes, while it downloads."),
|
|
65
|
+
error: z.string().optional().describe("Why the last fetch failed, when it did."),
|
|
66
|
+
});
|
|
67
|
+
export type SpeechStatus = z.infer<typeof SpeechStatusSchema>;
|
|
68
|
+
|
|
69
|
+
// GET /speech/stream, a WebSocket: the composer cuts the microphone into phrases itself (its silence segmenter) and
|
|
70
|
+
// streams each one as it is spoken. A text frame `begin` opens phrase `id`; binary frames that follow are its audio,
|
|
71
|
+
// 16 kHz mono s16le; `end` closes it, keeping its first `keep` samples (the trailing pause trimmed off); `drop`
|
|
72
|
+
// abandons it. The daemon answers with `partial` text while the phrase is still being spoken, one `final` per ended
|
|
73
|
+
// phrase, in order, and `status` whenever the model's readiness moves. The language is the socket's `lang` query.
|
|
74
|
+
export const SpeechClientMessageSchema = z.discriminatedUnion("type", [
|
|
75
|
+
z.object({ type: z.literal("begin"), id: z.number() }),
|
|
76
|
+
z.object({ type: z.literal("end"), id: z.number(), keep: z.number() }),
|
|
77
|
+
z.object({ type: z.literal("drop"), id: z.number() }),
|
|
78
|
+
]);
|
|
79
|
+
export type SpeechClientMessage = z.infer<typeof SpeechClientMessageSchema>;
|
|
80
|
+
|
|
81
|
+
export const SpeechServerMessageSchema = z.discriminatedUnion("type", [
|
|
82
|
+
z.object({ type: z.literal("status"), status: SpeechStatusSchema }),
|
|
83
|
+
z.object({ type: z.literal("partial"), id: z.number(), text: z.string() }),
|
|
84
|
+
z.object({
|
|
85
|
+
type: z.literal("final"),
|
|
86
|
+
id: z.number(),
|
|
87
|
+
text: z.string().describe("Empty when the phrase held no words."),
|
|
88
|
+
decodeMs: z.number().describe("How long hearing it took once its audio was in."),
|
|
89
|
+
}),
|
|
90
|
+
z.object({ type: z.literal("error"), id: z.number().optional(), message: z.string() }),
|
|
91
|
+
]);
|
|
92
|
+
export type SpeechServerMessage = z.infer<typeof SpeechServerMessageSchema>;
|
|
93
|
+
|
|
94
|
+
// The longest phrase either door takes: a minute at 16 kHz. The composer's segmenter cuts a monologue at the same
|
|
95
|
+
// length, so nothing it sends is ever refused.
|
|
96
|
+
export const MAX_SPEECH_SAMPLES = 60 * 16_000;
|
package/src/schemas/terminal.ts
CHANGED
|
@@ -140,6 +140,10 @@ export const BrowsersListSchema = z.object({
|
|
|
140
140
|
});
|
|
141
141
|
export type BrowsersList = z.infer<typeof BrowsersListSchema>;
|
|
142
142
|
export const BrowserNameParamSchema = z.object({ name: z.string().describe("Which browser.") });
|
|
143
|
+
export const BrowserPageParamSchema = z.object({
|
|
144
|
+
name: z.string().describe("Which browser."),
|
|
145
|
+
pageId: z.string().describe("Which of its tabs: a page id from the roster."),
|
|
146
|
+
});
|
|
143
147
|
export const OpenBrowserInputSchema = z.object({
|
|
144
148
|
url: z.string().optional().describe("Where the new tab goes. Absent opens a blank tab, the way a browser's own new tab does."),
|
|
145
149
|
});
|
|
@@ -7,20 +7,23 @@
|
|
|
7
7
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
|
-
// The
|
|
11
|
-
// `TurnEndingSchema.reason` spells them, so the ending a surface reads and the
|
|
12
|
-
//
|
|
13
|
-
|
|
10
|
+
// The walls a turn can stop at that take a standing answer. The first three end a turn that leaves finished work behind
|
|
11
|
+
// a live session, spelled exactly as `TurnEndingSchema.reason` spells them, so the ending a surface reads and the
|
|
12
|
+
// question it asks are one word. `memory` is the door: the sandbox too short of memory to start the turn at all, which
|
|
13
|
+
// holds the message (or the turn the sandbox kept) until a press or room comes back. A restart is handled apart
|
|
14
|
+
// (SandboxSettings.autoResumeOnRestart): nobody is watching a restart, so it has no in-chat question.
|
|
15
|
+
export const TurnBreakSchema = z.enum(["limit", "outage", "stopped", "memory"]);
|
|
14
16
|
export type TurnBreak = z.infer<typeof TurnBreakSchema>;
|
|
15
17
|
|
|
16
|
-
// Every ending that leaves a held turn behind:
|
|
17
|
-
// partway. That one has no standing answer and never re-runs by itself: the
|
|
18
|
-
// model again and another one (2026-09-30, the owner's call over an automatic
|
|
19
|
-
|
|
18
|
+
// Every ending that leaves a held turn behind: a spent allowance, an outage, a turn that stopped short, plus the
|
|
19
|
+
// provider's safety classifier stopping a turn partway. That one has no standing answer and never re-runs by itself: the
|
|
20
|
+
// person picks, each time, between the same model again and another one (2026-09-30, the owner's call over an automatic
|
|
21
|
+
// model switch). A low-memory hold is no ending: nothing ran, so it is never one of these.
|
|
22
|
+
export const HeldEndingSchema = z.enum(["limit", "outage", "stopped", "flagged"]);
|
|
20
23
|
export type HeldEnding = z.infer<typeof HeldEndingSchema>;
|
|
21
24
|
|
|
22
25
|
/** Whether this ending takes a standing answer (turn-break policy), which `flagged` never does. */
|
|
23
|
-
export const isTurnBreak = (ending: HeldEnding): ending is
|
|
26
|
+
export const isTurnBreak = (ending: HeldEnding): ending is Exclude<HeldEnding, "flagged"> => ending !== "flagged";
|
|
24
27
|
|
|
25
28
|
// `move` implies `resend`: an account with room is tried at once, and the reset appointment stands as its fallback.
|
|
26
29
|
// There is deliberately no "move, else hold" — a reader willing to spend a second account is willing to wait.
|
|
@@ -31,6 +34,11 @@ export type LimitPolicy = z.infer<typeof LimitPolicySchema>;
|
|
|
31
34
|
export const RetryPolicySchema = z.enum(["wait", "retry"]);
|
|
32
35
|
export type RetryPolicy = z.infer<typeof RetryPolicySchema>;
|
|
33
36
|
|
|
37
|
+
// The memory wall's two answers: hold for a press, or send once the sandbox has room again. Nothing ran, so sending it
|
|
38
|
+
// then spends nothing twice.
|
|
39
|
+
export const RoomPolicySchema = z.enum(["wait", "resend"]);
|
|
40
|
+
export type RoomPolicy = z.infer<typeof RoomPolicySchema>;
|
|
41
|
+
|
|
34
42
|
// One answer, whichever ending asked. Read against the ending: only `limit` can be `move`.
|
|
35
43
|
export const TurnBreakPolicySchema = z.enum(["wait", "resend", "move", "retry"]);
|
|
36
44
|
export type TurnBreakPolicy = z.infer<typeof TurnBreakPolicySchema>;
|
|
@@ -40,6 +48,7 @@ export const TURN_BREAK_POLICIES: Readonly<Record<TurnBreak, readonly TurnBreakP
|
|
|
40
48
|
limit: ["wait", "resend", "move"],
|
|
41
49
|
outage: ["wait", "retry"],
|
|
42
50
|
stopped: ["wait", "retry"],
|
|
51
|
+
memory: ["wait", "resend"],
|
|
43
52
|
};
|
|
44
53
|
|
|
45
54
|
/** Whether this ending may be answered this way; the one gate every writer goes through. */
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// A monorepo's architecture as its files state it: the packages pnpm-workspace.yaml names, which siblings each declares
|
|
4
|
+
// and what its own files do with them, and, inside one package, how its modules import each other. Facts only: what to
|
|
5
|
+
// draw, and what counts as a finding, is the reader's.
|
|
6
|
+
|
|
7
|
+
// A component of the repository's map, docs/architecture/repo.json, which the documentation extension writes.
|
|
8
|
+
export const WorkspaceComponentSchema = z.object({
|
|
9
|
+
id: z.string().describe("The component's id."),
|
|
10
|
+
name: z.string().describe("What the map calls it."),
|
|
11
|
+
accent: z.string().optional().describe('The map\'s colour slot for it: "1" to "5", or "neutral".'),
|
|
12
|
+
});
|
|
13
|
+
export type WorkspaceComponent = z.infer<typeof WorkspaceComponentSchema>;
|
|
14
|
+
|
|
15
|
+
// A pnpm-workspace package, discovered from pnpm-workspace.yaml's globs. group is the top-level dir segment; component
|
|
16
|
+
// is the repo.json component listing its dir, when the repository has a map.
|
|
17
|
+
export const WorkspacePackageSchema = z.object({
|
|
18
|
+
name: z.string().describe("The name the package declares."),
|
|
19
|
+
dir: z.string().describe("Where it lives, relative to the repository."),
|
|
20
|
+
group: z.string().describe("The top-level folder it sits under, which is what a diagram colours by."),
|
|
21
|
+
component: z.string().optional().describe("The component of docs/architecture/repo.json that lists it, by id. Absent without a map."),
|
|
22
|
+
});
|
|
23
|
+
export type WorkspacePackage = z.infer<typeof WorkspacePackageSchema>;
|
|
24
|
+
export const WorkspaceDepTypeSchema = z.enum(["prod", "dev", "peer"]);
|
|
25
|
+
export type WorkspaceDepType = z.infer<typeof WorkspaceDepTypeSchema>;
|
|
26
|
+
|
|
27
|
+
// What the dependent's own files do with a sibling it declares, read from them: the strongest use found wins.
|
|
28
|
+
export const WorkspaceDepUsageSchema = z
|
|
29
|
+
.enum(["code", "types", "tooling", "reference", "none"])
|
|
30
|
+
.describe(
|
|
31
|
+
"What the dependent's own files do with it: `code`, a shipped file imports a value from it; `types`, shipped files import only its types; `tooling`, only its tests, scripts and config files use it; `reference`, a shipped file names it without importing it (a Dockerfile copying its build, a manifest listing its files); `none`, nothing in the dependent names it.",
|
|
32
|
+
);
|
|
33
|
+
export type WorkspaceDepUsage = z.infer<typeof WorkspaceDepUsageSchema>;
|
|
34
|
+
|
|
35
|
+
// A workspace-internal dependency edge: from depends on to, typed by which dependency block declared it.
|
|
36
|
+
export const WorkspaceDepEdgeSchema = z.object({
|
|
37
|
+
from: z.string().describe("The package that depends."),
|
|
38
|
+
to: z.string().describe("The package it depends on."),
|
|
39
|
+
type: WorkspaceDepTypeSchema.describe("Which kind of dependency declared it."),
|
|
40
|
+
usage: WorkspaceDepUsageSchema.optional(),
|
|
41
|
+
});
|
|
42
|
+
export type WorkspaceDepEdge = z.infer<typeof WorkspaceDepEdgeSchema>;
|
|
43
|
+
export const WorkspaceGraphSchema = z.object({
|
|
44
|
+
packages: z.array(WorkspacePackageSchema).describe("Every package in the repository."),
|
|
45
|
+
edges: z.array(WorkspaceDepEdgeSchema).describe("Which of them use which. Pure data: how to lay it out is yours to decide."),
|
|
46
|
+
components: z
|
|
47
|
+
.array(WorkspaceComponentSchema)
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("The components of docs/architecture/repo.json, in the map's order. Absent when the repository has no map."),
|
|
50
|
+
});
|
|
51
|
+
export type WorkspaceGraph = z.infer<typeof WorkspaceGraphSchema>;
|
|
52
|
+
|
|
53
|
+
// layers.json beside a package's package.json: the layers its modules sit in, lowest first. A module may import its own
|
|
54
|
+
// layer and any below it; an import reaching higher is a finding, and so is a cycle between units of one layer.
|
|
55
|
+
export const PackageLayerSchema = z.object({
|
|
56
|
+
name: z.string().min(1).describe("The layer's name."),
|
|
57
|
+
about: z.string().optional().describe("What sits in it, in a sentence."),
|
|
58
|
+
units: z
|
|
59
|
+
.array(z.string().min(1))
|
|
60
|
+
.describe(
|
|
61
|
+
"Path prefixes under the root it holds: a directory, a subdirectory, or a module named without its extension. A file belongs to the layer whose unit matches it longest.",
|
|
62
|
+
),
|
|
63
|
+
});
|
|
64
|
+
export type PackageLayer = z.infer<typeof PackageLayerSchema>;
|
|
65
|
+
export const PackageLayersFileSchema = z.object({
|
|
66
|
+
root: z.string().optional().describe('The directory the units are relative to, from the package. Default "src".'),
|
|
67
|
+
shelves: z.array(z.string()).optional().describe("Top-level directories whose every subdirectory is a unit of its own, such as one per feature."),
|
|
68
|
+
surface: z
|
|
69
|
+
.array(z.string())
|
|
70
|
+
.optional()
|
|
71
|
+
.describe('File-name patterns, `*` a wildcard, of modules that sit above every layer, such as "*.routes.ts". Nothing below may import one.'),
|
|
72
|
+
layers: z.array(PackageLayerSchema).min(1).describe("The layers, lowest first."),
|
|
73
|
+
});
|
|
74
|
+
export type PackageLayersFile = z.infer<typeof PackageLayersFileSchema>;
|
|
75
|
+
|
|
76
|
+
export const PackageModulesParamSchema = z.object({
|
|
77
|
+
repo: z.string().describe("Which repository."),
|
|
78
|
+
package: z.string().min(1).describe("Which of its workspace packages, by the name it declares."),
|
|
79
|
+
});
|
|
80
|
+
export const PackageUnitSchema = z.object({
|
|
81
|
+
id: z
|
|
82
|
+
.string()
|
|
83
|
+
.describe(
|
|
84
|
+
'The unit: a directory under the root, a shelf\'s subdirectory ("features/chat"), a unit layers.json names, "(surface)" for every surface module, or "(root)" for the modules directly in the root, which no rule judges.',
|
|
85
|
+
),
|
|
86
|
+
modules: z.number().int().nonnegative().describe("How many source modules it holds."),
|
|
87
|
+
layer: z
|
|
88
|
+
.number()
|
|
89
|
+
.int()
|
|
90
|
+
.nonnegative()
|
|
91
|
+
.optional()
|
|
92
|
+
.describe("The declared layer it sits in, by index, lowest 0. Absent when the package declares no layers, or none places it."),
|
|
93
|
+
surface: z.literal(true).optional().describe("The surface modules' unit, above every layer."),
|
|
94
|
+
});
|
|
95
|
+
export type PackageUnit = z.infer<typeof PackageUnitSchema>;
|
|
96
|
+
export const PackageUnitEdgeSchema = z.object({
|
|
97
|
+
from: z.string().describe("The importing unit."),
|
|
98
|
+
to: z.string().describe("The unit it imports."),
|
|
99
|
+
imports: z.number().int().positive().describe("How many value imports make the edge."),
|
|
100
|
+
sites: z.array(z.string()).describe("Up to five of them, as `path:line` relative to the root."),
|
|
101
|
+
});
|
|
102
|
+
export type PackageUnitEdge = z.infer<typeof PackageUnitEdgeSchema>;
|
|
103
|
+
export const PackageModulesSchema = z.object({
|
|
104
|
+
package: z.string().describe("The package."),
|
|
105
|
+
root: z.string().describe("The directory read, relative to the repository."),
|
|
106
|
+
layers: z
|
|
107
|
+
.array(PackageLayerSchema.pick({ name: true, about: true }))
|
|
108
|
+
.optional()
|
|
109
|
+
.describe("Its declared layers, lowest first. Absent when it has no layers.json."),
|
|
110
|
+
invalid: z.string().optional().describe("Why its layers.json could not be read, when it has one that does not parse."),
|
|
111
|
+
units: z.array(PackageUnitSchema).describe("Every unit with a module in it."),
|
|
112
|
+
edges: z
|
|
113
|
+
.array(PackageUnitEdgeSchema)
|
|
114
|
+
.describe("Value imports between units: type-only imports erase and tests import their subject, so neither counts."),
|
|
115
|
+
});
|
|
116
|
+
export type PackageModules = z.infer<typeof PackageModulesSchema>;
|
|
@@ -91,28 +91,6 @@ export const RepoAppSchema = z.object({
|
|
|
91
91
|
export type RepoApp = z.infer<typeof RepoAppSchema>;
|
|
92
92
|
export const AppsListSchema = z.object({ apps: z.array(RepoAppSchema).describe("The apps in this repository.") });
|
|
93
93
|
export type AppsList = z.infer<typeof AppsListSchema>;
|
|
94
|
-
// A pnpm-workspace package, discovered from pnpm-workspace.yaml's globs. group is the top-level dir segment, the
|
|
95
|
-
// dependencies view's coloring axis.
|
|
96
|
-
export const WorkspacePackageSchema = z.object({
|
|
97
|
-
name: z.string().describe("The name the package declares."),
|
|
98
|
-
dir: z.string().describe("Where it lives, relative to the repository."),
|
|
99
|
-
group: z.string().describe("The top-level folder it sits under, which is what a diagram colours by."),
|
|
100
|
-
});
|
|
101
|
-
export type WorkspacePackage = z.infer<typeof WorkspacePackageSchema>;
|
|
102
|
-
export const WorkspaceDepTypeSchema = z.enum(["prod", "dev", "peer"]);
|
|
103
|
-
export type WorkspaceDepType = z.infer<typeof WorkspaceDepTypeSchema>;
|
|
104
|
-
// A workspace-internal dependency edge: from depends on to, typed by which dependency block declared it.
|
|
105
|
-
export const WorkspaceDepEdgeSchema = z.object({
|
|
106
|
-
from: z.string().describe("The package that depends."),
|
|
107
|
-
to: z.string().describe("The package it depends on."),
|
|
108
|
-
type: WorkspaceDepTypeSchema.describe("Which kind of dependency declared it."),
|
|
109
|
-
});
|
|
110
|
-
export type WorkspaceDepEdge = z.infer<typeof WorkspaceDepEdgeSchema>;
|
|
111
|
-
export const WorkspaceGraphSchema = z.object({
|
|
112
|
-
packages: z.array(WorkspacePackageSchema).describe("Every package in the repository."),
|
|
113
|
-
edges: z.array(WorkspaceDepEdgeSchema).describe("Which of them use which. Pure data: how to lay it out is yours to decide."),
|
|
114
|
-
});
|
|
115
|
-
export type WorkspaceGraph = z.infer<typeof WorkspaceGraphSchema>;
|
|
116
94
|
// Path params for the per-repo apps routes: repo names the monorepo, validated like PanelRepoParam.
|
|
117
95
|
export const RepoAppsParamSchema = z.object({ repo: z.string().describe("Which repository.") });
|
|
118
96
|
export const AppParamSchema = z.object({
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
isLockedWorkspacePath,
|
|
6
6
|
isReportedManifest,
|
|
7
7
|
isReviewableLockedPath,
|
|
8
|
+
isUnversionedStatePath,
|
|
8
9
|
LOCKED_STATE_ENTRIES,
|
|
9
10
|
lockedWorkspaceEntry,
|
|
10
11
|
PLAN_DOCUMENTS_DIR,
|
|
@@ -29,7 +30,13 @@ const AUTOMATIONS: readonly FileContribution[] = [
|
|
|
29
30
|
|
|
30
31
|
describe(`staleQueryKeys`, () => {
|
|
31
32
|
it(`maps a manifest write to the queries it makes stale`, () => {
|
|
32
|
-
expect(staleQueryKeys([`.intentic/config/capabilities.json`], [])).toEqual([
|
|
33
|
+
expect(staleQueryKeys([`.intentic/config/capabilities.json`], [])).toEqual([
|
|
34
|
+
`capabilities`,
|
|
35
|
+
`extensions`,
|
|
36
|
+
`environment`,
|
|
37
|
+
`panels`,
|
|
38
|
+
`manifests`,
|
|
39
|
+
]);
|
|
33
40
|
});
|
|
34
41
|
|
|
35
42
|
it(`refreshes the unreadable-manifest notice for the four files a person hand-edits`, () => {
|
|
@@ -299,6 +306,40 @@ describe(`isReviewableLockedPath`, () => {
|
|
|
299
306
|
});
|
|
300
307
|
});
|
|
301
308
|
|
|
309
|
+
// What lets a git-reading reaction skip a batch: the daemon's own churn under `.intentic` must not rescan every repo,
|
|
310
|
+
// while a tracked config file still has to.
|
|
311
|
+
describe(`isUnversionedStatePath`, () => {
|
|
312
|
+
it(`holds the machine state the daemon rewrites constantly`, () => {
|
|
313
|
+
expect(isUnversionedStatePath(`.intentic/local/privacy-log.json`)).toBe(true);
|
|
314
|
+
expect(isUnversionedStatePath(`.intentic/local/.privacy-log.json.55.3275.tmp`)).toBe(true);
|
|
315
|
+
expect(isUnversionedStatePath(`.intentic/records/extension-usage.json`)).toBe(true);
|
|
316
|
+
expect(isUnversionedStatePath(`.intentic/records/artifacts/browser/page.png`)).toBe(true);
|
|
317
|
+
// An unversioned file beside versioned ones in config is still invisible to git.
|
|
318
|
+
expect(isUnversionedStatePath(`.intentic/config/docs/index.json`)).toBe(true);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it(`lets every versioned entry through, files and directories alike`, () => {
|
|
322
|
+
for (const path of VERSIONED_STATE_PATHS) {
|
|
323
|
+
const inside = path.endsWith(`/`) ? `${path}nested/file.json` : path;
|
|
324
|
+
expect([inside, isUnversionedStatePath(inside)]).toEqual([inside, false]);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it(`never claims a path outside the workspace's own state dir`, () => {
|
|
329
|
+
expect(isUnversionedStatePath(`intentic/_sandbox/sandbox/src/main.ts`)).toBe(false);
|
|
330
|
+
expect(isUnversionedStatePath(`README.md`)).toBe(false);
|
|
331
|
+
// A repo's nested state dir is its project's content, which git does track.
|
|
332
|
+
expect(isUnversionedStatePath(`myrepo/.intentic/local/privacy-log.json`)).toBe(false);
|
|
333
|
+
expect(isUnversionedStatePath(`.intentic`)).toBe(false);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it(`reads a platform path and a dot-relative one the same as a posix one`, () => {
|
|
337
|
+
expect(isUnversionedStatePath(`.intentic\\local\\privacy-log.json`)).toBe(true);
|
|
338
|
+
expect(isUnversionedStatePath(`./.intentic/local/privacy-log.json`)).toBe(true);
|
|
339
|
+
expect(isUnversionedStatePath(`./.intentic/config/capabilities.json`)).toBe(false);
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
302
343
|
describe(`VERSIONED_STATE_PATHS`, () => {
|
|
303
344
|
// versioned exempts an entry from the `.intentic` exclusion; a mismarked credential can't be unmarked once
|
|
304
345
|
// committed.
|
|
@@ -397,7 +397,7 @@ const STATE_FILES = [
|
|
|
397
397
|
{
|
|
398
398
|
path: ".intentic/local/cache/",
|
|
399
399
|
invalidates: [],
|
|
400
|
-
why: "Rebuildable indexes and caches, the iq index and its vector sidecar, the
|
|
400
|
+
why: "Rebuildable indexes and caches, the iq index and its vector sidecar, the speech models an image does not bake, fileq's derived/ markdown shadows of binary files; ignored by the watcher and recreated from carried workspace content.",
|
|
401
401
|
portability: "derived",
|
|
402
402
|
},
|
|
403
403
|
// Extension runtime scratch, one directory per extension via `extensionStateDir` below (the only way an extension
|
|
@@ -769,6 +769,17 @@ export const isReviewableLockedPath = (relPath: string): boolean => {
|
|
|
769
769
|
return isLockedWorkspacePath(rel) && VERSIONED_STATE_PATHS.some((path) => (path.endsWith("/") ? rel.startsWith(path) : rel === path));
|
|
770
770
|
};
|
|
771
771
|
|
|
772
|
+
// Machine state the root repo never tracks: everything under the workspace's own `.intentic/` but the `versioned`
|
|
773
|
+
// entries history.ts carves back out of the wholesale exclusion. A write here moves no git status, no landing and no
|
|
774
|
+
// review, so a reaction that exists to re-read git can pass over a batch made only of these. The daemon rewrites
|
|
775
|
+
// some of them every few seconds under load (the privacy ledger, extension usage, browser screenshots), and each
|
|
776
|
+
// one used to rescan every repo. Root-level only, like the exclusion it mirrors: a repo's own nested `.intentic` is
|
|
777
|
+
// its project's content. Accepts either slash.
|
|
778
|
+
export const isUnversionedStatePath = (relPath: string): boolean => {
|
|
779
|
+
const rel = relPath.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
780
|
+
return rel.startsWith(`${STATE_DIR}/`) && !VERSIONED_STATE_PATHS.some((path) => (path.endsWith("/") ? rel.startsWith(path) : rel === path));
|
|
781
|
+
};
|
|
782
|
+
|
|
772
783
|
// Every path this table declares, as a type; the daemon joins paths only through `statePath`
|
|
773
784
|
// (workspace/state-paths.ts), which accepts one of these and nothing else, so renaming a store's file is a compile
|
|
774
785
|
// error everywhere it's named rather than a view that silently stops refreshing.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { TranscriptRow, TranscriptTool } from "../events/transcript.js";
|
|
2
|
+
import { fitNestedTool, fitPageRow, PAGE_TEXT_CAP } from "./transcript-page.js";
|
|
3
|
+
|
|
4
|
+
// What a page carries of a row, which the daemon serves and the editor mirrors: the two have to agree to the byte.
|
|
5
|
+
|
|
6
|
+
const long = `x`.repeat(PAGE_TEXT_CAP + 500);
|
|
7
|
+
const call = (id: string, extra: Partial<TranscriptTool> = {}): TranscriptTool => ({
|
|
8
|
+
id,
|
|
9
|
+
name: `Bash`,
|
|
10
|
+
category: `execute`,
|
|
11
|
+
status: `completed`,
|
|
12
|
+
...extra,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe(`fitPageRow`, () => {
|
|
16
|
+
it(`hands back a row with no calls as it is`, () => {
|
|
17
|
+
const row: TranscriptRow = { role: `assistant`, text: `done` };
|
|
18
|
+
expect(fitPageRow(row)).toBe(row);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it(`keeps each output's start and counts a delegation's calls instead of carrying them`, () => {
|
|
22
|
+
const row: TranscriptRow = {
|
|
23
|
+
role: `assistant`,
|
|
24
|
+
text: ``,
|
|
25
|
+
tools: [call(`a`, { content: [{ type: `text`, text: long }] }), call(`b`, { name: `Agent`, children: [call(`c`), call(`d`)] })],
|
|
26
|
+
};
|
|
27
|
+
const fitted = fitPageRow(row);
|
|
28
|
+
expect(fitted.tools?.[0]?.content).toEqual([{ type: `text`, text: long.slice(0, PAGE_TEXT_CAP) }]);
|
|
29
|
+
expect(fitted.tools?.[1]?.children).toBeUndefined();
|
|
30
|
+
expect(fitted.tools?.[1]?.nested).toBe(2);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe(`fitNestedTool`, () => {
|
|
35
|
+
it(`keeps every call under it, each output fitted at every depth`, () => {
|
|
36
|
+
const fitted = fitNestedTool(call(`a`, { children: [call(`b`, { content: [{ type: `text`, text: long }] })] }));
|
|
37
|
+
expect(fitted.children?.[0]?.content).toEqual([{ type: `text`, text: long.slice(0, PAGE_TEXT_CAP) }]);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ToolCallContent } from "../events/requests.js";
|
|
2
|
+
import type { TranscriptRow, TranscriptTool } from "../events/transcript.js";
|
|
3
|
+
|
|
4
|
+
// WHAT A TRANSCRIPT PAGE CARRIES OF A ROW, written once for both ends: the daemon fits every row of a page it serves to
|
|
5
|
+
// this (sessions/agent-transcript.ts), and the editor fits the rows it mirrors to disk the same way, so a reopened chat
|
|
6
|
+
// paints from its mirror exactly the rows the daemon's page then hands back, and keeps them rather than drawing them
|
|
7
|
+
// again (transcriptState.ts rebuildKeeping matches rows by their contents).
|
|
8
|
+
|
|
9
|
+
// What a page carries of one tool output: the pane truncates text at 4000 characters of its own accord
|
|
10
|
+
// (toolPresentation.ts TEXT_CAP), so twice that leaves room to raise that cap without a second round trip.
|
|
11
|
+
export const PAGE_TEXT_CAP = 8_000;
|
|
12
|
+
|
|
13
|
+
const fitContent = (entry: ToolCallContent): ToolCallContent =>
|
|
14
|
+
entry.type === "text" && entry.text.length > PAGE_TEXT_CAP ? { ...entry, text: entry.text.slice(0, PAGE_TEXT_CAP) } : entry;
|
|
15
|
+
|
|
16
|
+
// A delegation's own calls are left behind, counted rather than carried: the card draws collapsed until it is opened,
|
|
17
|
+
// and the daemon answers that press with the calls themselves. On the records this was measured on they were 86% of the
|
|
18
|
+
// bytes of the longest conversation.
|
|
19
|
+
const fitPageTool = (tool: TranscriptTool): TranscriptTool => {
|
|
20
|
+
const { children, content, ...carried } = tool;
|
|
21
|
+
return {
|
|
22
|
+
...carried,
|
|
23
|
+
...(content !== undefined ? { content: content.map(fitContent) } : {}),
|
|
24
|
+
...(children !== undefined && children.length > 0 ? { nested: children.length } : {}),
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** A row as a page carries it; a row with no calls is handed back as it is. */
|
|
29
|
+
export const fitPageRow = <Row extends TranscriptRow>(row: Row): Row =>
|
|
30
|
+
row.tools === undefined ? row : { ...row, tools: row.tools.map(fitPageTool) };
|
|
31
|
+
|
|
32
|
+
/** A call with everything under it, each output at every depth fitted the same way: what an opened delegation draws. */
|
|
33
|
+
export const fitNestedTool = (tool: TranscriptTool): TranscriptTool => ({
|
|
34
|
+
...tool,
|
|
35
|
+
...(tool.content !== undefined ? { content: tool.content.map(fitContent) } : {}),
|
|
36
|
+
...(tool.children !== undefined ? { children: tool.children.map(fitNestedTool) } : {}),
|
|
37
|
+
});
|
package/dist/text/whisper.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"whisper.d.ts","sourceRoot":"","sources":["../../src/text/whisper.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,kBAAkB,0BAA0B,CAAC;AAI1D,eAAO,MAAM,kBAAkB,WAAY,MAAM,KAAG,MAAM,GAAG,SAQ5D,CAAC"}
|
package/dist/text/whisper.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export const WHISPER_MODEL_REPO = "ggerganov/whisper.cpp";
|
|
2
|
-
export const cleanTranscription = (stdout) => {
|
|
3
|
-
const text = stdout
|
|
4
|
-
.split("\n")
|
|
5
|
-
.map((line) => line.trim())
|
|
6
|
-
.filter((line) => line !== "" && !/^[[(].*[\])]$/.test(line))
|
|
7
|
-
.join(" ")
|
|
8
|
-
.trim();
|
|
9
|
-
return text === "" ? undefined : text;
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=whisper.js.map
|
package/dist/text/whisper.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"whisper.js","sourceRoot":"","sources":["../../src/text/whisper.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,kBAAkB,GAAG,uBAAuB,CAAC;AAI1D,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAAc,EAAsB,EAAE;IACrE,MAAM,IAAI,GAAG,MAAM;SACd,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SAC5D,IAAI,CAAC,GAAG,CAAC;SACT,IAAI,EAAE,CAAC;IACZ,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1C,CAAC,CAAC"}
|
package/src/text/whisper.test.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { cleanTranscription } from "./whisper.js";
|
|
2
|
-
|
|
3
|
-
test("flattens whisper output onto one line", () => {
|
|
4
|
-
expect(cleanTranscription(" Hello there.\n General Kenobi.\n")).toBe("Hello there. General Kenobi.");
|
|
5
|
-
});
|
|
6
|
-
|
|
7
|
-
// The whole reason this filter exists: whisper narrates silence, and a narration dispatched as speech is a
|
|
8
|
-
// turn nobody asked for.
|
|
9
|
-
test("drops noise-only annotations, in both bracket styles", () => {
|
|
10
|
-
expect(cleanTranscription(" [BLANK_AUDIO]\n")).toBeUndefined();
|
|
11
|
-
expect(cleanTranscription("(wind blowing)")).toBeUndefined();
|
|
12
|
-
expect(cleanTranscription("")).toBeUndefined();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
// An annotation next to real speech drops the annotation and keeps the speech: the utterance did happen.
|
|
16
|
-
test("keeps speech that arrived alongside an annotation", () => {
|
|
17
|
-
expect(cleanTranscription("[music] \nreal words")).toBe("real words");
|
|
18
|
-
});
|
package/src/text/whisper.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// Shared reading of whisper.cpp's stdout, used by the daemon's speech route and Discord's voice session, so both filter
|
|
2
|
-
// non-speech annotations the same way.
|
|
3
|
-
|
|
4
|
-
// Hugging Face repo the ggml-*.bin weights pull from on first use; each integration downloads its own size.
|
|
5
|
-
export const WHISPER_MODEL_REPO = "ggerganov/whisper.cpp";
|
|
6
|
-
|
|
7
|
-
// Cleans whisper's stdout to one line of text, or undefined for silence/noise-only output — a line that is entirely a
|
|
8
|
-
// bracketed annotation like `[BLANK_AUDIO]`.
|
|
9
|
-
export const cleanTranscription = (stdout: string): string | undefined => {
|
|
10
|
-
const text = stdout
|
|
11
|
-
.split("\n")
|
|
12
|
-
.map((line) => line.trim())
|
|
13
|
-
.filter((line) => line !== "" && !/^[[(].*[\])]$/.test(line))
|
|
14
|
-
.join(" ")
|
|
15
|
-
.trim();
|
|
16
|
-
return text === "" ? undefined : text;
|
|
17
|
-
};
|