@intentic/sandbox-contract 1.215.0 → 1.217.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/contracts/settings.contract.d.ts +0 -85
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/index.d.ts +0 -85
- package/dist/index.d.ts.map +1 -1
- package/dist/schemas.d.ts +3 -114
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +2 -9
- package/dist/schemas.js.map +1 -1
- package/package.json +4 -4
- package/src/schemas.ts +43 -114
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentic/sandbox-contract",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.217.0",
|
|
4
4
|
"description": "oRPC wire contract for the intentic sandbox daemon — shared by the daemon and its browser client",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -89,9 +89,9 @@
|
|
|
89
89
|
"@orpc/contract": "1.14.13",
|
|
90
90
|
"tslib": "2.8.1",
|
|
91
91
|
"zod": "4.4.3",
|
|
92
|
-
"@intentic/
|
|
93
|
-
"@intentic/
|
|
94
|
-
"@intentic/constants": "1.
|
|
92
|
+
"@intentic/extension-manifest": "1.217.0",
|
|
93
|
+
"@intentic/registry": "1.217.0",
|
|
94
|
+
"@intentic/constants": "1.217.0"
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
97
|
"@types/node": "24.13.2",
|
package/src/schemas.ts
CHANGED
|
@@ -1725,11 +1725,6 @@ export const SkillRemoveSchema = z.object({ name: SkillNameSchema });
|
|
|
1725
1725
|
// only. Opt-in (default off); the browser Search box uses iq regardless.
|
|
1726
1726
|
// iqSearchHoldout — conversation-level measurement control for iqSearch (UsageTurn.iqSearchArm). The arm
|
|
1727
1727
|
// stays fixed because teaching already loaded into a session cannot be removed next turn.
|
|
1728
|
-
// iqContext — retrieves for the user's message BEFORE the turn starts and prepends the ranked answer
|
|
1729
|
-
// to it, so the model opens with the anchors instead of paying a search round-trip to
|
|
1730
|
-
// find them. Independent of iqSearch: that one teaches the agent to search, this one
|
|
1731
|
-
// answers ahead of it.
|
|
1732
|
-
// iqContextHoldout — measurement control for iqContext, same shape as terseHoldout (UsageTurn.iqContext).
|
|
1733
1728
|
// workspaceMap — computes an AREA index of the project a run starts in and prepends it to the
|
|
1734
1729
|
// conversation's opening message, so the turn does not have to buy its own orientation
|
|
1735
1730
|
// with a directory listing. Generated from the filesystem every time, never stored.
|
|
@@ -1799,29 +1794,12 @@ export const SandboxSettingsSchema = z.object({
|
|
|
1799
1794
|
* a valid control here: once the teaching enters a provider session, withholding it from the next request
|
|
1800
1795
|
* does not make the model forget it. 0 ⇒ no measurement and every conversation receives the teaching. */
|
|
1801
1796
|
iqSearchHoldout: z.number().min(0).max(1).default(0),
|
|
1802
|
-
/* RETRIEVE BEFORE THE TURN, don't wait to be asked. The daemon runs the user's message through the resident
|
|
1803
|
-
* iq engine and prepends the ranked answer to it, so a turn that would have opened with two or three search
|
|
1804
|
-
* calls opens with the anchors already in hand. Independent of `iqSearch`, which only teaches the agent to
|
|
1805
|
-
* reach for the CLI once it decides to search — this one answers ahead of that decision, and the two
|
|
1806
|
-
* compose: the injected capsule names the anchors, the CLI is there for what it missed.
|
|
1807
|
-
*
|
|
1808
|
-
* It rides the USER message (turn-context.ts), never the system prompt, for the same reason the setup
|
|
1809
|
-
* notice does: it changes every turn, and the system prefix is kept byte-stable for the prompt cache.
|
|
1810
|
-
* Off by default — it spends input tokens on every eligible turn, and whether that trade pays is exactly
|
|
1811
|
-
* what the holdout below is for. */
|
|
1812
|
-
iqContext: z.boolean().default(false),
|
|
1813
|
-
// Measurement control for the pre-injection, identical in shape to `terseHoldout`: a fraction [0,1] of
|
|
1814
|
-
// otherwise-eligible turns run WITHOUT the retrieved context and stamp their arm onto the ledger
|
|
1815
|
-
// (UsageTurn.iqContext), so the report compares two real populations of turns instead of asserting a saving.
|
|
1816
|
-
iqContextHoldout: z.number().min(0).max(1).default(0),
|
|
1817
1797
|
/* THE MAP THE TURN OPENS WITH — which areas the project a run starts in has, one derived line on what each
|
|
1818
1798
|
* is for, and where the run is standing among them (agent/workspace-map.ts).
|
|
1819
1799
|
*
|
|
1820
|
-
*
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1823
|
-
* being bought with a directory listing in two turns out of five, and with ~5.3k tokens of tool results
|
|
1824
|
-
* before the job was touched. The two compose: this says which area, retrieval says which file.
|
|
1800
|
+
* It answers the question every first turn has whatever it was asked — "what is this and where am I in
|
|
1801
|
+
* it" — which across a hundred sessions of this workspace was being bought with a directory listing in two
|
|
1802
|
+
* turns out of five, and with ~5.3k tokens of tool results before the job was touched.
|
|
1825
1803
|
*
|
|
1826
1804
|
* ROOTED AT THE RUN'S STARTING FOLDER rather than at the workspace: a persona's start folder, an isolated
|
|
1827
1805
|
* conversation's worktree, or wherever the turn's cwd is. It maps the project containing that folder and
|
|
@@ -2059,34 +2037,17 @@ export type InputSavings = z.infer<typeof InputSavingsSchema>;
|
|
|
2059
2037
|
// without it. A mean PER TURN, because the arms never hold the same number of turns.
|
|
2060
2038
|
export const SavingsArmSchema = z.object({ turns: z.number(), mean: z.number() });
|
|
2061
2039
|
|
|
2062
|
-
/* WHAT BECAME OF A TURN'S PRE-TURN RETRIEVAL. `note` is the only outcome that reaches the model; the rest are
|
|
2063
|
-
* the ordinary ways it declines, and none of them is an error — each is a turn that proceeded exactly as it
|
|
2064
|
-
* would have without the feature.
|
|
2065
|
-
*
|
|
2066
|
-
* ineligible — the prompt failed a lexical gate before retrieval ran (it named its own file, or said nothing
|
|
2067
|
-
* to search for). By design, and the largest share by far.
|
|
2068
|
-
* deadline — the resident engine outran its budget and the turn went on without it.
|
|
2069
|
-
* indexing — the index had not caught up with disk, so any answer would have been confidently partial.
|
|
2070
|
-
* no-hits — the query matched nothing.
|
|
2071
|
-
* failed — retrieval threw; swallowed on purpose, since this is an optimisation nobody asked for.
|
|
2072
|
-
*
|
|
2073
|
-
* It lives in the contract rather than in the daemon because two things downstream of the daemon have to speak
|
|
2074
|
-
* it: the ledger row that records it (UsageTurnSchema.iqContextOutcome) and the experiment that reads it back
|
|
2075
|
-
* (TurnExperimentSchema.outcomes). Declared here, above both. */
|
|
2076
|
-
export const IqContextOutcomeSchema = z.enum(["note", "ineligible", "deadline", "indexing", "no-hits", "failed"]);
|
|
2077
|
-
export type IqContextOutcome = z.infer<typeof IqContextOutcomeSchema>;
|
|
2078
|
-
|
|
2079
2040
|
/* ONE METRIC'S READING of a turn-level experiment: the two arms, and whatever the arithmetic over them will
|
|
2080
2041
|
* stand behind. An experiment can carry several — see TurnExperimentSchema.
|
|
2081
2042
|
*
|
|
2082
2043
|
* `metric` says what `mean` counts and what `deltaPct` is a delta in, and choosing it is most of the work.
|
|
2083
2044
|
* proseChars — the terse steer: the thing it steers, and the only part of the model's output that
|
|
2084
2045
|
* responds to being asked to be brief (UsageTurn.proseChars has why output tokens cannot).
|
|
2085
|
-
* searchCalls —
|
|
2086
|
-
* openingSearches —
|
|
2087
|
-
*
|
|
2088
|
-
*
|
|
2089
|
-
*
|
|
2046
|
+
* searchCalls — the search teaching: the searches a turn ran, which the teaching directly changes.
|
|
2047
|
+
* openingSearches — the same, narrower: the searches before the turn first touched a file.
|
|
2048
|
+
* Search mechanisms must not be judged on COST. Cost is a whole turn's work, a search mechanism moves one part
|
|
2049
|
+
* of it, and the part sits inside the noise of the rest exactly as the steer's effect once sat inside its
|
|
2050
|
+
* tool-call arguments. */
|
|
2090
2051
|
export const TurnMetricReadingSchema = z.object({
|
|
2091
2052
|
metric: z.enum(["proseChars", "searchCalls", "openingSearches"]),
|
|
2092
2053
|
on: SavingsArmSchema,
|
|
@@ -2134,11 +2095,11 @@ export type TurnMetricReading = z.infer<typeof TurnMetricReadingSchema>;
|
|
|
2134
2095
|
* counted: a turn under a custom system prompt drops the terse steer along with everything else the daemon
|
|
2135
2096
|
* appends, so it belongs to neither arm.
|
|
2136
2097
|
*
|
|
2137
|
-
* ONE COIN FLIP, SEVERAL READINGS. `metrics` is a list because
|
|
2138
|
-
* turn ran, and the ones it ran before touching a file — and they are two readings of the SAME
|
|
2139
|
-
* two experiments. Splitting them into separate entries would duplicate the arm assignment and
|
|
2140
|
-
*
|
|
2141
|
-
*
|
|
2098
|
+
* ONE COIN FLIP, SEVERAL READINGS. `metrics` is a list because the search teaching is judged on two — the
|
|
2099
|
+
* searches a turn ran, and the ones it ran before touching a file — and they are two readings of the SAME
|
|
2100
|
+
* experiment, not two experiments. Splitting them into separate entries would duplicate the arm assignment and
|
|
2101
|
+
* let a screen show a turn count on one that disagrees with the other. Headline first: the screens read
|
|
2102
|
+
* `metrics[0]` for the big number and the rest as supporting lines. */
|
|
2142
2103
|
export const TurnExperimentSchema = z.object({
|
|
2143
2104
|
// A head and a tail rather than a plain array, because an experiment judged on nothing is not an experiment:
|
|
2144
2105
|
// the screens take the first reading for their headline and stack the rest under it, and this is what makes
|
|
@@ -2155,32 +2116,16 @@ export const TurnExperimentSchema = z.object({
|
|
|
2155
2116
|
// Content-addressed treatment version. Present where mixing rows from two instruction revisions would turn
|
|
2156
2117
|
// one experiment into two unnamed ones; the reader filters to this (latest) cohort.
|
|
2157
2118
|
cohort: z.string().optional(),
|
|
2158
|
-
/* How much of the treatment arm the treatment actually REACHED, when that is knowable and less than all of
|
|
2159
|
-
* it — pre-injection's arm is the coin flip (intention-to-treat, deliberately), and a turn can be assigned
|
|
2160
|
-
* the retrieval and still have nothing to prepend. Measured at four turns in five, which is the difference
|
|
2161
|
-
* between a mechanism worth little and one worth five times what the delta says.
|
|
2162
|
-
*
|
|
2163
|
-
* Absent ⇒ delivery is not a separate question for this experiment (the terse steer always lands) or no
|
|
2164
|
-
* turn in the window recorded it. The screen shows the delta as diluted rather than silently scaling it:
|
|
2165
|
-
* the correction is a division by a rate this small only when the rate is itself well measured. */
|
|
2166
|
-
deliveredPct: z.number().optional(),
|
|
2167
|
-
/* WHERE THE REST OF THE TREATMENT ARM WENT, most common first. `deliveredPct` says a mechanism reached one
|
|
2168
|
-
* turn in five; this says whether the other four were the eligibility gate declining on purpose or a
|
|
2169
|
-
* two-second deadline quietly eating the feature, which are the same number and opposite problems.
|
|
2170
|
-
*
|
|
2171
|
-
* Absent ⇒ the experiment has no delivery question (the terse steer always lands) or no turn recorded one. */
|
|
2172
|
-
outcomes: z.array(z.object({ outcome: IqContextOutcomeSchema, turns: z.number() })).optional(),
|
|
2173
2119
|
});
|
|
2174
2120
|
export type TurnExperiment = z.infer<typeof TurnExperimentSchema>;
|
|
2175
2121
|
|
|
2176
|
-
// `output`/`
|
|
2122
|
+
// `output`/`search` are absent when that experiment isn't running at all (its flag off, or no holdout set) — a
|
|
2177
2123
|
// section that isn't there reads as "not measured", which is the truth, while zeros would read as "measured,
|
|
2178
2124
|
// worth nothing".
|
|
2179
2125
|
export const SavingsReportSchema = z.object({
|
|
2180
2126
|
input: InputSavingsSchema,
|
|
2181
2127
|
output: TurnExperimentSchema.optional(),
|
|
2182
2128
|
search: TurnExperimentSchema.optional(),
|
|
2183
|
-
context: TurnExperimentSchema.optional(),
|
|
2184
2129
|
});
|
|
2185
2130
|
export type SavingsReport = z.infer<typeof SavingsReportSchema>;
|
|
2186
2131
|
|
|
@@ -5597,8 +5542,25 @@ export type MachinePort = z.infer<typeof MachinePortSchema>;
|
|
|
5597
5542
|
export const MachineWatcherSchema = z.object({
|
|
5598
5543
|
running: z.boolean(),
|
|
5599
5544
|
pid: z.number().int().optional(),
|
|
5545
|
+
/* When the watcher last FINISHED a pass — the field that makes `running` mean something. The agent holds its
|
|
5546
|
+
* SSH transport listeners on its own event loop, so a failure that escapes the loop leaves a process that is
|
|
5547
|
+
* alive and a loop that is gone: pid present, unit "active", mirroring and the git bridge stopped. Absent
|
|
5548
|
+
* means the agent has not reported one (too old to stamp, or its first pass has not landed) — which is not
|
|
5549
|
+
* the same as stalled, and readers must not treat it as either state. */
|
|
5600
5550
|
lastTickAt: z.number().optional(),
|
|
5601
5551
|
});
|
|
5552
|
+
export type MachineWatcher = z.infer<typeof MachineWatcherSchema>;
|
|
5553
|
+
|
|
5554
|
+
/* How long a watcher may go without finishing a pass before "running" stops being the honest word for it. Its
|
|
5555
|
+
* loop polls every 5s and its slowest step is bounded by two 10s network timeouts per pairing, so a minute is
|
|
5556
|
+
* several passes of slack — the same yardstick the Computers view already ages a whole report by.
|
|
5557
|
+
*
|
|
5558
|
+
* The rule lives HERE, next to the field, because the terminal and the browser both answer this question and a
|
|
5559
|
+
* machine that is "running" in one and "stalled" in the other is worse than either answer alone. */
|
|
5560
|
+
export const WATCHER_STALL_AFTER_MS = 60_000;
|
|
5561
|
+
|
|
5562
|
+
export const watcherStalled = (watcher: MachineWatcher, now: number): boolean =>
|
|
5563
|
+
watcher.running && watcher.lastTickAt !== undefined && now - watcher.lastTickAt > WATCHER_STALL_AFTER_MS;
|
|
5602
5564
|
|
|
5603
5565
|
export const MachineReportSchema = z.object({
|
|
5604
5566
|
/* The OS hostname, and the JOIN KEY. A machine can arrive here two ways at once — volunteered by its sync
|
|
@@ -6507,31 +6469,6 @@ export const UsageTurnSchema = z.object({
|
|
|
6507
6469
|
* no control to be compared against. Pooling those into the off-arm would compare steered turns against a
|
|
6508
6470
|
* population selected by something other than the coin flip, which is not a control at all. */
|
|
6509
6471
|
terse: z.boolean().optional(),
|
|
6510
|
-
/* Which arm of the pre-injection experiment this turn ran on (settings.iqContextHoldout), on the same terms
|
|
6511
|
-
* as `terse` above: absent ⇒ outside the experiment.
|
|
6512
|
-
*
|
|
6513
|
-
* TRUE means the turn was ASSIGNED the retrieved context, not that a note was necessarily prepended — a
|
|
6514
|
-
* treatment turn whose retrieval came back empty or unconfident injects nothing. That is deliberate: the
|
|
6515
|
-
* arms have to be the coin flip's populations, and re-labelling a turn by what retrieval happened to find
|
|
6516
|
-
* would sort turns by how searchable their question was, which is a property of the question. The control
|
|
6517
|
-
* arm contains the same unsearchable questions in the same proportion, so they cancel. */
|
|
6518
|
-
iqContext: z.boolean().optional(),
|
|
6519
|
-
/* WHAT ACTUALLY HAPPENED to the retrieval on this turn — the companion to `iqContext`, and the answer to the
|
|
6520
|
-
* question that field's design deliberately refuses to answer.
|
|
6521
|
-
*
|
|
6522
|
-
* Keeping the arm on the coin flip is right, and it costs something: the treatment arm contains turns the
|
|
6523
|
-
* treatment never reached, so the delta it yields is diluted by however many those are. Measured over nine
|
|
6524
|
-
* days of real use that was four turns in five, which makes the difference between "this mechanism is worth
|
|
6525
|
-
* little" and "this mechanism is worth five times what the number says".
|
|
6526
|
-
*
|
|
6527
|
-
* IT RECORDS THE REASON, not merely a yes/no, because the yes/no left the whole loss unexplained. 81% of an
|
|
6528
|
-
* assigned arm delivering nothing is either a gate doing its job or a deadline quietly eating the feature,
|
|
6529
|
-
* and those call for opposite responses — one is working as designed, the other is a two-second timeout to
|
|
6530
|
-
* raise. A boolean cannot tell them apart, so nothing downstream could act on the number it produced.
|
|
6531
|
-
*
|
|
6532
|
-
* `note` is delivery; every other value is a turn that ran exactly as the control arm would have, labelled
|
|
6533
|
-
* with what took the treatment away. Absent ⇒ outside the experiment, exactly as for the arm. */
|
|
6534
|
-
iqContextOutcome: IqContextOutcomeSchema.optional(),
|
|
6535
6472
|
/* Which arm of the iq SEARCH-TEACHING experiment this conversation runs on
|
|
6536
6473
|
* (settings.iqSearchHoldout). Stable for every turn in one conversation: the treatment is instruction
|
|
6537
6474
|
* loaded into a provider session, so flipping it per turn would call a remembered treatment a control.
|
|
@@ -6540,10 +6477,6 @@ export const UsageTurnSchema = z.object({
|
|
|
6540
6477
|
// Hash of the plugin nudge + skill body used for this arm. Control turns carry it too, so a report can keep
|
|
6541
6478
|
// both sides of one treatment revision together and exclude older wording after an upgrade.
|
|
6542
6479
|
iqSearchCohort: z.string().optional(),
|
|
6543
|
-
/* Wall time spent deciding the pre-turn retrieval outcome, including the full deadline on a timeout.
|
|
6544
|
-
* Absent on rows written before latency was recorded or turns outside the retrieval experiment. Keeping it
|
|
6545
|
-
* per outcome is what lets the report distinguish a slow engine from an eligibility gate. */
|
|
6546
|
-
iqContextDurationMs: z.number().nonnegative().optional(),
|
|
6547
6480
|
/* Characters of the model's own PROSE this turn — the `delta` frames only, so no tool-call arguments and no
|
|
6548
6481
|
* thinking. What the terse steer is judged on, and the reason it can be judged at all.
|
|
6549
6482
|
*
|
|
@@ -6562,33 +6495,29 @@ export const UsageTurnSchema = z.object({
|
|
|
6562
6495
|
proseChars: z.number().optional(),
|
|
6563
6496
|
/* SEARCHES THIS TURN RAN — every tool call that went looking for code, the dedicated search tools and the
|
|
6564
6497
|
* CLI searches alike (isSearchCall owns the rule; `iq q` is Bash and would otherwise not be counted at all).
|
|
6565
|
-
* What
|
|
6498
|
+
* What the search teaching is judged on, and the same correction `proseChars` is to the terse steer.
|
|
6566
6499
|
*
|
|
6567
|
-
* COST PER TURN CANNOT SERVE
|
|
6568
|
-
*
|
|
6569
|
-
*
|
|
6570
|
-
*
|
|
6571
|
-
* handing the treatment arm the bigger jobs. Cost is a whole turn's worth of work, and retrieval touches one
|
|
6572
|
-
* part of it, so the part lives inside the noise of the rest — exactly the shape that made output tokens
|
|
6573
|
-
* unable to see the steer.
|
|
6500
|
+
* COST PER TURN CANNOT SERVE: cost is a whole turn's worth of work, a search mechanism touches one part of
|
|
6501
|
+
* it, and the part lives inside the noise of the rest — exactly the shape that made output tokens unable to
|
|
6502
|
+
* see the steer. Nine days of a since-removed retrieval experiment proved it with an interval from −2.9% to
|
|
6503
|
+
* +56.9%, driven entirely by which arm had drawn the bigger jobs.
|
|
6574
6504
|
*
|
|
6575
|
-
* Searches are what the mechanism acts on directly
|
|
6576
|
-
*
|
|
6577
|
-
*
|
|
6505
|
+
* Searches are what the mechanism acts on directly. Turns that never search stay in the population at zero
|
|
6506
|
+
* rather than being filtered out — they dilute both arms equally, while selecting on "did it search" would
|
|
6507
|
+
* select on the treatment itself.
|
|
6578
6508
|
*
|
|
6579
6509
|
* Absent ⇒ the turn predates this being measured; `armOf` drops it rather than reading it as a turn that
|
|
6580
6510
|
* searched nothing. */
|
|
6581
6511
|
searchCalls: z.number().optional(),
|
|
6582
|
-
/* …and how many of them came BEFORE the turn first opened or changed a file — the orientation burst
|
|
6583
|
-
*
|
|
6584
|
-
* that doesn't goes hunting first, and pre-injection's whole claim is that it removes that hunt.
|
|
6512
|
+
/* …and how many of them came BEFORE the turn first opened or changed a file — the orientation burst. A turn
|
|
6513
|
+
* that already knows where to look starts working; one that doesn't goes hunting first.
|
|
6585
6514
|
*
|
|
6586
6515
|
* The narrower of the two readings and the less confounded: `searchCalls` still grows with the size of the
|
|
6587
6516
|
* job, while the walk up to the first file is roughly the same act whatever the job turns out to be.
|
|
6588
6517
|
*
|
|
6589
6518
|
* A turn that never reads or edits counts all of its searches here — it never arrived, so all of it was
|
|
6590
|
-
* orientation. Dropping those instead would select the population by an OUTCOME the treatment moves
|
|
6591
|
-
*
|
|
6519
|
+
* orientation. Dropping those instead would select the population by an OUTCOME the treatment moves, which
|
|
6520
|
+
* is the one bias an arm-based reading cannot absorb.
|
|
6592
6521
|
*
|
|
6593
6522
|
* Absent ⇒ as for `searchCalls`. */
|
|
6594
6523
|
openingSearches: z.number().optional(),
|