@cotal-ai/connector-jcode 0.48.2 → 0.50.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/extension.d.ts.map +1 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +1866 -391
- package/dist/index.js +61 -26
- package/dist/mcp.js +183 -104
- package/dist/queue-fallback.d.ts +206 -0
- package/dist/queue-fallback.d.ts.map +1 -0
- package/dist/route-identity.d.ts +11 -10
- package/dist/route-identity.d.ts.map +1 -1
- package/dist/startup-diagnostics.d.ts +28 -11
- package/dist/startup-diagnostics.d.ts.map +1 -1
- package/dist/stored-sessions.d.ts +41 -0
- package/dist/stored-sessions.d.ts.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serving policy for automatic deliveries a busy Jcode session has not accepted yet (#1233).
|
|
3
|
+
*
|
|
4
|
+
* Every path that hands a queued peer message to the live session is EDGE-TRIGGERED: a new
|
|
5
|
+
* `incoming` while the session is busy, or an idle transition at the end of a turn. When the
|
|
6
|
+
* mid-turn handoff (`soft_interrupt`) rejects — the measured case is the SDK's 30s request timeout
|
|
7
|
+
* — the catch logs and returns, and both edges can then be absent indefinitely: no further message
|
|
8
|
+
* arrives, and the session never reports idle. The queue is served by nothing, the seat stays
|
|
9
|
+
* healthy and productive, and its automatic depth is static while the oldest age grows. Measured
|
|
10
|
+
* live at 27 queued for 13.8 hours on a seat that answered a DM in under a minute.
|
|
11
|
+
*
|
|
12
|
+
* The repair is a LEVEL-TRIGGERED serving loop: while automatic work is owed, something re-attempts
|
|
13
|
+
* delivery on its own schedule rather than waiting for an edge that may never come. This module is
|
|
14
|
+
* the decision that loop makes, extracted so it can be graded directly — every accepting branch AND
|
|
15
|
+
* its refusal — rather than only through a live seat, exactly as `retry-policy.ts` was for #790.
|
|
16
|
+
*
|
|
17
|
+
* TWO DELIVERY PATHS, and the second is the point. `steer` is the existing mid-turn handoff, which
|
|
18
|
+
* needs a `soft_interrupt` reply. `queue-turn` needs no such reply: the Harness accepts an ordinary
|
|
19
|
+
* message while the agent is busy, acknowledges it with `message_accepted`, and runs it as a turn
|
|
20
|
+
* when the current one ends. So a soft interrupt that never answers can no longer strand the queue,
|
|
21
|
+
* because the fallback does not ask it anything.
|
|
22
|
+
*
|
|
23
|
+
* IT NEVER DROPS. No branch here discards a delivery: losing a peer's message is worse than
|
|
24
|
+
* delivering it late, so the only bound is pacing. The one refusal that persists is the turn-level
|
|
25
|
+
* give-up (#790), which leaves the batch UN-ACKED and therefore redeliverable rather than acking
|
|
26
|
+
* work the seat never saw — and which the reported connection state now refuses to call `ready`.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Whether a REFUSED queued turn must force a connection boundary before the batch is re-delivered.
|
|
30
|
+
*
|
|
31
|
+
* BOTH OBVIOUS ANSWERS ARE WRONG AND EACH WAS MEASURED BY A REVIEWER, so the discriminator is
|
|
32
|
+
* neither flag but whether the thing blocking attribution CAN CLEAR ON ITS OWN.
|
|
33
|
+
*
|
|
34
|
+
* Trigger only on `!acknowledged` and a run-debt duplicate survives: a run whose acceptance window
|
|
35
|
+
* lapsed owes debt until that TURN ends, while the fallback's own sends are genuinely acknowledged,
|
|
36
|
+
* so the batch is refused, stays owed, and is re-delivered into a healthy Harness that accepts and
|
|
37
|
+
* RUNS each copy. Measured at 4 executions from 4 send frames.
|
|
38
|
+
*
|
|
39
|
+
* Trigger on every refusal and a live run becomes silent starvation: inside the refusal arm
|
|
40
|
+
* `!acknowledged || !attributable` is TAUTOLOGICALLY TRUE, so that spelling is `= true` with extra
|
|
41
|
+
* steps. It suppresses every write until the bridge is replaced, and with a turn held open for
|
|
42
|
+
* minutes the batch cannot reach the seat at all. Measured at 0 executions from 0 send frames.
|
|
43
|
+
*
|
|
44
|
+
* So the question is what owes the debt. A RUN settles its own promise when the turn ends, which
|
|
45
|
+
* genuinely proves the Harness is finished with that request, so attribution returns WITHOUT a
|
|
46
|
+
* boundary and waiting is correct: the batch is late by one turn, not lost. A LAPSED SEND settles
|
|
47
|
+
* nothing, because the SDK resolved it on its own accept timeout while the request stayed live at
|
|
48
|
+
* the Harness; no amount of waiting restores attribution, so only replacing the connection can.
|
|
49
|
+
*
|
|
50
|
+
* `unsettledRunDispatches` is therefore the self-clearing kind and must NOT force a boundary. But
|
|
51
|
+
* waiting alone is not enough either, because that is precisely the state sol measured duplicating:
|
|
52
|
+
* a send issued while a run is open CAN NEVER BE ATTRIBUTED, so it is refused every time and the
|
|
53
|
+
* loop re-sends it forever, and each refused copy was still ACCEPTED AND RUN by the Harness. The
|
|
54
|
+
* answer is to not issue it at all. See {@link attributionBlockedByOpenRun}: the batch is deferred,
|
|
55
|
+
* no frame is written, and the send happens once after the turn ends, when it can be attributed.
|
|
56
|
+
*/
|
|
57
|
+
export declare function refusalNeedsBoundary(sendLapsed: boolean): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Whether a queued-turn send must be DEFERRED because no acknowledgement it receives could be
|
|
60
|
+
* attributed to it.
|
|
61
|
+
*
|
|
62
|
+
* This is the half that stops the duplicate at its source rather than cleaning up after it. While a
|
|
63
|
+
* run's acceptance window has lapsed, `message_accepted` carries only a session id, so an event
|
|
64
|
+
* arriving now may belong to that run. A send issued into that window is therefore refused on
|
|
65
|
+
* arrival no matter how healthy it was, stays owed, and is re-sent on the next tick, while the
|
|
66
|
+
* Harness accepts and EXECUTES every copy: 4 executions from 4 send frames.
|
|
67
|
+
*
|
|
68
|
+
* Not writing is what prevents it, and it costs only latency: the run's own promise settles when the
|
|
69
|
+
* turn ends, which genuinely proves the Harness is done with that request, so the very next tick can
|
|
70
|
+
* send once and attribute the answer. Late by one turn, delivered exactly once, and no connection
|
|
71
|
+
* replacement is involved, so a live turn can never suppress the queue for its whole duration or end
|
|
72
|
+
* in the seat shutting down once recovery is spent.
|
|
73
|
+
*
|
|
74
|
+
* THE DEFERRAL MUST BE BOUNDED OR IT IS THE ORIGINAL BUG AGAIN, which a reviewer put precisely: run
|
|
75
|
+
* debt clears only when `run()` settles, and a turn that missed its acceptance AND never completes
|
|
76
|
+
* settles nothing, so an unbounded deferral starves the queue exactly as the un-fallen-back stall
|
|
77
|
+
* did. Nothing in the protocol bounds a turn's duration, so waiting on one is waiting on an edge
|
|
78
|
+
* that may never come, which is the defect this whole tier exists to remove.
|
|
79
|
+
*
|
|
80
|
+
* So the deferral expires. Past {@link DEFERRAL_MAX_MS} the run is treated as unsettleable rather
|
|
81
|
+
* than merely slow, which is the LAPSED-SEND case, and it takes that case's answer: force the
|
|
82
|
+
* connection boundary, which ends the ambiguity at the root because a replaced bridge cannot deliver
|
|
83
|
+
* an event for a request made on the old one. The bound is generous enough that an ordinary long
|
|
84
|
+
* turn defers and delivers late rather than reconnecting, and finite so a wedged one cannot defer
|
|
85
|
+
* forever. Delivery is late, bounded, and exactly once in every branch.
|
|
86
|
+
*
|
|
87
|
+
* NOT GRADED BY A LIVE CELL, AND SAYING SO RATHER THAN IMPLYING OTHERWISE. A mutation severing the
|
|
88
|
+
* call site SURVIVED the loss suite: that suite's A is a swallowed SEND, not an open unacknowledged
|
|
89
|
+
* RUN, so the deferral is never consulted there. I then built the run-debt cell (the fixture knob
|
|
90
|
+
* `FAKE_JCODE_RUN_WITHOUT_ACCEPT_ON_CONTENT` exists for it) and it PASSED WITH THE DEFERRAL SEVERED,
|
|
91
|
+
* because `nextFallbackAction` takes `drive` on an idle seat and only reaches `queue-turn` on the
|
|
92
|
+
* busy branch after a soft interrupt has failed, so my probe measured a path the defect cannot
|
|
93
|
+
* touch. I deleted the mutation instead of keeping one whose SURVIVED verdict I could not turn into
|
|
94
|
+
* a red, and left the knob so the next attempt starts further along. The behaviour here is covered
|
|
95
|
+
* by the policy cells and by a reviewer's live reproduction, NOT by a mutation-graded live cell.
|
|
96
|
+
*/
|
|
97
|
+
export declare function attributionBlockedByOpenRun(unsettledRunDispatches: number, blockedForMs?: number): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* How long a send may be deferred for an open run before that run is treated as unsettleable.
|
|
100
|
+
*
|
|
101
|
+
* Five minutes is longer than any acceptance round trip and longer than the ordinary long turns this
|
|
102
|
+
* connector serves, so a healthy slow turn is never mistaken for a wedged one, while a genuinely
|
|
103
|
+
* hung run cannot hold the queue past it. It is deliberately much larger than the 60s fallback
|
|
104
|
+
* ceiling: the pacing loop re-attempts throughout, and only the write is withheld.
|
|
105
|
+
*/
|
|
106
|
+
export declare const DEFERRAL_MAX_MS: number;
|
|
107
|
+
/**
|
|
108
|
+
* Whether a deferral has outlived its bound, so the open run must be treated as unsettleable and the
|
|
109
|
+
* connection boundary forced. The exact complement of the deferral's second condition, kept as its
|
|
110
|
+
* own name so the call site reads as the decision it is rather than as a comparison.
|
|
111
|
+
*/
|
|
112
|
+
export declare function deferralExhausted(unsettledRunDispatches: number, blockedForMs: number): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* What an EXHAUSTED deferral should do, which is a three-way decision and not a two-way one.
|
|
115
|
+
*
|
|
116
|
+
* Both obvious answers were measured harmful by reviewers, one of them after I had already written
|
|
117
|
+
* it. Forcing the boundary when the one-shot recovery is spent does not replace anything: it calls
|
|
118
|
+
* `shutdown(1)` and the seat exits, so a starvation report is answered by killing the seat. And
|
|
119
|
+
* continuing to WRITE is the same defect as doing nothing: the send is refused as unattributable,
|
|
120
|
+
* the batch stays owed, and the loop re-sends once a minute while the Harness accepts and RUNS each
|
|
121
|
+
* copy, bounded only by how long the run stays open, which is to say not bounded.
|
|
122
|
+
*
|
|
123
|
+
* So the third outcome is the #790 give-up's: STOP ATTEMPTING, leave the batch owed and un-acked. No
|
|
124
|
+
* frame is written so the instruction cannot execute again, nothing is acked so the durable copy
|
|
125
|
+
* redelivers elsewhere, and the reported connection state stops calling the seat `ready`. Terminal
|
|
126
|
+
* for the SEAT's queued-turn tier, never for the MESSAGE.
|
|
127
|
+
*/
|
|
128
|
+
export type ExhaustedDeferralAction = "replace-bridge" | "stop-attempting";
|
|
129
|
+
export declare function exhaustedDeferralAction(bridgeRecoveryAvailable: boolean): ExhaustedDeferralAction;
|
|
130
|
+
/** First delay after work is owed. Short, because most stalls clear on the next attempt. */
|
|
131
|
+
export declare const FALLBACK_INITIAL_MS = 1000;
|
|
132
|
+
/** Ceiling on the delay. One attempt a minute against a session that is not accepting. */
|
|
133
|
+
export declare const FALLBACK_MAX_MS = 60000;
|
|
134
|
+
/**
|
|
135
|
+
* What the serving loop should do on this tick.
|
|
136
|
+
*
|
|
137
|
+
* - `drive` — run an ordinary turn carrying the queued batch. Only when the session is not busy.
|
|
138
|
+
* - `steer` — attempt the mid-turn handoff into the live session.
|
|
139
|
+
* - `queue-turn` — the fallback: hand the batch to the Harness as an ordinary message, which it
|
|
140
|
+
* accepts while busy and runs as its own turn. Needs no `soft_interrupt` reply.
|
|
141
|
+
* - `wait` — nothing can be attempted right now; stay armed and look again later.
|
|
142
|
+
* - `stop` — this host is shutting down; disarm.
|
|
143
|
+
*/
|
|
144
|
+
export type FallbackAction = "drive" | "steer" | "queue-turn" | "wait" | "stop";
|
|
145
|
+
export interface FallbackState {
|
|
146
|
+
/** The host is shutting down. */
|
|
147
|
+
stopping: boolean;
|
|
148
|
+
/** The queued-turn tier has terminalized: an unsettleable run with no bridge recovery left. The
|
|
149
|
+
* batch stays owed and un-acked, but nothing here will attempt it again, so the loop disarms
|
|
150
|
+
* rather than re-deriving the same terminal decision once a minute forever. */
|
|
151
|
+
queuedTurnTierStopped: boolean;
|
|
152
|
+
/** A bridge replacement owns the session; it redrives the durable batch itself. */
|
|
153
|
+
reconnecting: boolean;
|
|
154
|
+
/** Startup has finished its readiness proof and post-join notice. */
|
|
155
|
+
initialized: boolean;
|
|
156
|
+
/** A live Harness client and session id are both held. */
|
|
157
|
+
hasSession: boolean;
|
|
158
|
+
/** `driving || turnActive`: a turn owns the provider. */
|
|
159
|
+
sessionBusy: boolean;
|
|
160
|
+
/** A `soft_interrupt` is already in flight; a second would race its own ledger. */
|
|
161
|
+
steering: boolean;
|
|
162
|
+
/** A `soft_interrupt` has failed since the last accepted handoff — the #1233 trigger. */
|
|
163
|
+
softInterruptFailed: boolean;
|
|
164
|
+
/** Automatic deliveries the live session has NOT accepted yet. */
|
|
165
|
+
unserved: number;
|
|
166
|
+
/** The host's own drive predicate: a kickoff, a pending wake, or a queued wake. */
|
|
167
|
+
driveWork: boolean;
|
|
168
|
+
/** Consecutive failed turns, for the #790 give-up bound. */
|
|
169
|
+
consecutiveFailures: number;
|
|
170
|
+
/** The #790 bound itself, passed in so this module owns no copy of it. */
|
|
171
|
+
giveUpAfter: number;
|
|
172
|
+
}
|
|
173
|
+
export interface FallbackDecision {
|
|
174
|
+
action: FallbackAction;
|
|
175
|
+
/** Why, so a refusal is attributable rather than an unexplained `wait`. */
|
|
176
|
+
reason: "stopping" | "not-initialized" | "no-session" | "reconnecting" | "turn-work" | "gave-up" | "mid-turn-handoff" | "soft-interrupt-timed-out" | "steer-in-flight" | "nothing-owed";
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The next action for the serving loop.
|
|
180
|
+
*
|
|
181
|
+
* ORDER IS MEANING. Shutdown outranks everything (a retry into a child being torn down is the
|
|
182
|
+
* hazard, not the queue). An unfinished startup and a bridge replacement both already own a redrive
|
|
183
|
+
* of the same durable batch, so attempting one here would race theirs. Only then does the queue's
|
|
184
|
+
* own state decide: an idle session takes the whole batch as its own turn, a busy one gets the
|
|
185
|
+
* mid-turn handoff — or, once that handoff has failed, the fallback that does not depend on it.
|
|
186
|
+
*/
|
|
187
|
+
export declare function nextFallbackAction(s: FallbackState): FallbackDecision;
|
|
188
|
+
/** The next delay, doubling from `current` and clamped at the ceiling. */
|
|
189
|
+
export declare function nextFallbackDelay(current: number): number;
|
|
190
|
+
/**
|
|
191
|
+
* Whether the loop must stay armed.
|
|
192
|
+
*
|
|
193
|
+
* Deliberately NOT the same question as {@link nextFallbackAction}. A tick that can do nothing right
|
|
194
|
+
* now (startup, a bridge replacement, the #790 bound) still owes the queue, and disarming on a
|
|
195
|
+
* temporary refusal is precisely how an edge-triggered server strands a message. Only shutdown, an
|
|
196
|
+
* empty ledger, and TERMINALIZATION end the loop.
|
|
197
|
+
*
|
|
198
|
+
* The third one is a reviewer's catch. Once the queued-turn tier has stopped attempting, every later
|
|
199
|
+
* tick re-derives the same terminal decision, writes nothing and re-arms, so the timer runs once a
|
|
200
|
+
* minute forever against a tier that will never act again. Nothing is lost by that, but a terminal
|
|
201
|
+
* state that keeps a timer alive is not terminal, it is a quiet loop, and this connector's whole
|
|
202
|
+
* argument is that invisible background activity is what let #1233 hide. Disarming here is safe
|
|
203
|
+
* because the flag is cleared only by a bridge replacement, which re-arms the loop itself.
|
|
204
|
+
*/
|
|
205
|
+
export declare function fallbackStillOwed(s: FallbackState): boolean;
|
|
206
|
+
//# sourceMappingURL=queue-fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-fallback.d.ts","sourceRoot":"","sources":["../src/queue-fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,CAEjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,2BAA2B,CAAC,sBAAsB,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,OAAO,CAGrG;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,QAAsB,CAAC;AAsBnD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,sBAAsB,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAE/F;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,uBAAuB,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;AAE3E,wBAAgB,uBAAuB,CAAC,uBAAuB,EAAE,OAAO,GAAG,uBAAuB,CAEjG;AAED,4FAA4F;AAC5F,eAAO,MAAM,mBAAmB,OAAQ,CAAC;AACzC,0FAA0F;AAC1F,eAAO,MAAM,eAAe,QAAS,CAAC;AAEtC;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhF,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB;;oFAEgF;IAChF,qBAAqB,EAAE,OAAO,CAAC;IAC/B,mFAAmF;IACnF,YAAY,EAAE,OAAO,CAAC;IACtB,qEAAqE;IACrE,WAAW,EAAE,OAAO,CAAC;IACrB,0DAA0D;IAC1D,UAAU,EAAE,OAAO,CAAC;IACpB,yDAAyD;IACzD,WAAW,EAAE,OAAO,CAAC;IACrB,mFAAmF;IACnF,QAAQ,EAAE,OAAO,CAAC;IAClB,yFAAyF;IACzF,mBAAmB,EAAE,OAAO,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,SAAS,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,2EAA2E;IAC3E,MAAM,EACF,UAAU,GACV,iBAAiB,GACjB,YAAY,GACZ,cAAc,GACd,WAAW,GACX,SAAS,GACT,kBAAkB,GAClB,0BAA0B,GAC1B,iBAAiB,GACjB,cAAc,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,aAAa,GAAG,gBAAgB,CAmBrE;AAED,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAI3D"}
|
package/dist/route-identity.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Say which provider is actually carrying a seat's model.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* so establishing it meant reading the seat's private log by hand (#785).
|
|
4
|
+
* A seat can be pinned to the model an operator asked for while its traffic is carried by a component
|
|
5
|
+
* nobody named: one seat was requested as a given model, logged every line under a different
|
|
6
|
+
* provider's name, and died inside a third component's plugin. Nothing in the roster, the spawn
|
|
7
|
+
* confirmation, or the manager's exit line named the route, so establishing it meant reading the
|
|
8
|
+
* seat's private log by hand (#785).
|
|
10
9
|
*
|
|
11
10
|
* `RuntimeInfo` carries `provider` and `routes` in the same response the model check already reads,
|
|
12
11
|
* so this is a reporting gap, not a discovery problem.
|
|
@@ -25,13 +24,15 @@ export interface RuntimeIdentity {
|
|
|
25
24
|
providers?: string[];
|
|
26
25
|
routes?: RouteInfo[];
|
|
27
26
|
}
|
|
27
|
+
/** The route serving `model`. RuntimeInfo's active provider disambiguates duplicate model ids. */
|
|
28
|
+
export declare function activeModelRoute(runtime: RuntimeIdentity | undefined, model: string): RouteInfo | undefined;
|
|
28
29
|
/**
|
|
29
30
|
* One line naming the effective route for `model`.
|
|
30
31
|
*
|
|
31
|
-
* The
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* The requested pin selects the model whose route is described. RuntimeInfo.model can lag behind a
|
|
33
|
+
* successful setModel, while RuntimeInfo.provider identifies the active route among duplicate model
|
|
34
|
+
* ids. When no matching route is known, the provider remains useful diagnostic context. When neither
|
|
35
|
+
* is known the line says so explicitly rather than guessing or going quiet.
|
|
35
36
|
*/
|
|
36
37
|
export declare function describeRoute(runtime: RuntimeIdentity | undefined, model: string): string;
|
|
37
38
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-identity.d.ts","sourceRoot":"","sources":["../src/route-identity.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"route-identity.d.ts","sourceRoot":"","sources":["../src/route-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,uFAAuF;AACvF,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAG3G;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOzF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAIrG"}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
export type JcodeConnectorFailureCode = "model_prefix_rejected" | "model_refused" | "model_mismatch" | "private_state" | "readiness_timeout";
|
|
1
|
+
export type JcodeConnectorFailureCode = "model_prefix_rejected" | "model_refused" | "model_mismatch" | "private_state" | "readiness_timeout" | "sessions_enumeration_failed";
|
|
2
2
|
/** A bounded connector-owned startup refusal. Only its allow-listed code is rendered publicly. */
|
|
3
3
|
export declare class JcodeConnectorError extends Error {
|
|
4
4
|
readonly code: JcodeConnectorFailureCode;
|
|
5
5
|
constructor(code: JcodeConnectorFailureCode, message: string, options?: ErrorOptions);
|
|
6
6
|
}
|
|
7
|
+
/** Listing stored sessions killed the harness. Cause and path are connector-owned, not child stderr. */
|
|
8
|
+
export declare class JcodeSessionsEnumerationFailure extends Error {
|
|
9
|
+
readonly sessionsPath: string;
|
|
10
|
+
readonly causeText: string;
|
|
11
|
+
readonly code: "sessions_enumeration_failed";
|
|
12
|
+
constructor(sessionsPath: string, causeText: string);
|
|
13
|
+
}
|
|
7
14
|
/** Start the per-seat connector diagnostic log before any private Jcode process is launched. */
|
|
8
15
|
export declare function installJcodeDiagnosticLog(home: string): string;
|
|
9
16
|
/** Write one connector-owned diagnostic to the terminal and the seat's private connector log. */
|
|
@@ -23,21 +30,31 @@ export declare class JcodeReadinessProviderRefusal extends Error {
|
|
|
23
30
|
export declare class JcodeEffortRefusal extends Error {
|
|
24
31
|
readonly requestedTier: string;
|
|
25
32
|
readonly effectiveModel: string;
|
|
33
|
+
readonly provider: string;
|
|
34
|
+
readonly apiMethod: string | undefined;
|
|
26
35
|
readonly acceptedLadder: readonly string[];
|
|
27
36
|
readonly providerCode = "invalid_request";
|
|
28
|
-
constructor(requestedTier: string, effectiveModel: string, acceptedLadder: readonly string[]);
|
|
37
|
+
constructor(requestedTier: string, effectiveModel: string, provider: string, apiMethod: string | undefined, acceptedLadder: readonly string[]);
|
|
38
|
+
}
|
|
39
|
+
/** A verified model route whose provider/profile has no reasoning-effort surface. */
|
|
40
|
+
export declare class JcodeEffortUnsupported extends Error {
|
|
41
|
+
readonly requestedTier: string;
|
|
42
|
+
readonly effectiveModel: string;
|
|
43
|
+
readonly provider: string;
|
|
44
|
+
readonly apiMethod: string | undefined;
|
|
45
|
+
constructor(requestedTier: string, effectiveModel: string, provider: string, apiMethod: string | undefined);
|
|
29
46
|
}
|
|
30
47
|
/** Compose a bounded effort-refusal diagnostic. `invalid_request` is intentionally fixed: Jcode
|
|
31
48
|
* rejected this API operation, while provider-supplied codes and text are untrusted. */
|
|
32
|
-
export
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*
|
|
39
|
-
*/
|
|
40
|
-
export declare function
|
|
49
|
+
export interface JcodeEffortIdentity {
|
|
50
|
+
model: string;
|
|
51
|
+
provider: string;
|
|
52
|
+
apiMethod?: string;
|
|
53
|
+
}
|
|
54
|
+
/** Classify the two stable invalid-request outcomes without rendering downstream text. HarnessError
|
|
55
|
+
* prefixes its message with the stable code, so the anchored `invalid_request:` predicate below is
|
|
56
|
+
* also the code gate; a second `error.code` check would be redundant. */
|
|
57
|
+
export declare function jcodeEffortRefusal(error: unknown, requestedTier: string, identity: JcodeEffortIdentity): JcodeEffortRefusal | JcodeEffortUnsupported;
|
|
41
58
|
/**
|
|
42
59
|
* Classify only the Jcode SDK's invalid-request response and only when both fields are safely
|
|
43
60
|
* extractable. Everything else retains the existing scrubbed startup diagnostic.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startup-diagnostics.d.ts","sourceRoot":"","sources":["../src/startup-diagnostics.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,yBAAyB,GACjC,uBAAuB,GACvB,eAAe,GACf,gBAAgB,GAChB,eAAe,GACf,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"startup-diagnostics.d.ts","sourceRoot":"","sources":["../src/startup-diagnostics.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,yBAAyB,GACjC,uBAAuB,GACvB,eAAe,GACf,gBAAgB,GAChB,eAAe,GACf,mBAAmB,GACnB,6BAA6B,CAAC;AAElC,kGAAkG;AAClG,qBAAa,mBAAoB,SAAQ,KAAK;IAChC,QAAQ,CAAC,IAAI,EAAE,yBAAyB;gBAA/B,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAG9F;AAED,wGAAwG;AACxG,qBAAa,+BAAgC,SAAQ,KAAK;IAItD,QAAQ,CAAC,YAAY,EAAE,MAAM;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM;IAJ5B,QAAQ,CAAC,IAAI,EAAG,6BAA6B,CAAU;gBAG5C,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM;CAI7B;AAID,gGAAgG;AAChG,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoB9D;AAED,iGAAiG;AACjG,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAG1D;AAED;;mGAEmG;AACnG,qBAAa,6BAA8B,SAAQ,KAAK;IAEpD,QAAQ,CAAC,YAAY,EAAE,MAAM;IAC7B,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,kBAAkB;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAFb,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,OAAO,GAAG,kBAAkB,EACvC,KAAK,EAAE,MAAM;CAIzB;AAED;;4CAE4C;AAC5C,qBAAa,kBAAmB,SAAQ,KAAK;IAIzC,QAAQ,CAAC,aAAa,EAAE,MAAM;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS;IACtC,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE;IAP5C,QAAQ,CAAC,YAAY,qBAAqB;gBAG/B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,cAAc,EAAE,SAAS,MAAM,EAAE;CAI7C;AAED,qFAAqF;AACrF,qBAAa,sBAAuB,SAAQ,KAAK;IAE7C,QAAQ,CAAC,aAAa,EAAE,MAAM;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS;gBAH7B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS;CAIzC;AAsDD;wFACwF;AACxF,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;yEAEyE;AACzE,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,EACd,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,mBAAmB,GAC5B,kBAAkB,GAAG,sBAAsB,CAc7C;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,6BAA6B,GAAG,SAAS,CAM1G"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare function storedSessionsPath(jcodeHome: string): string;
|
|
2
|
+
export type StoredSessionsInspection = {
|
|
3
|
+
kind: "absent";
|
|
4
|
+
path: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: "empty-directory";
|
|
7
|
+
path: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: "populated";
|
|
10
|
+
path: string;
|
|
11
|
+
entries: number;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "not-a-directory";
|
|
14
|
+
path: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "unreadable";
|
|
17
|
+
path: string;
|
|
18
|
+
code: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* A local read of the seat's own `sessions/` directory. This runs on the startup path of every
|
|
22
|
+
* managed seat, so it must never throw: a home the connector cannot read is a question for the
|
|
23
|
+
* harness, not a reason to kill a seat that would otherwise start. Both reads here can fail on a
|
|
24
|
+
* home whose permissions were perturbed from outside (operator chmod, restored backup, container
|
|
25
|
+
* UID remap): `lstat` when the home itself is unreadable, `readdir` when only `sessions/` is.
|
|
26
|
+
* Anything but a real empty directory falls through to the ordinary listing attempt.
|
|
27
|
+
*/
|
|
28
|
+
export declare function inspectStoredSessions(jcodeHome: string): StoredSessionsInspection;
|
|
29
|
+
/** True only for a real empty directory. A missing directory is a first launch, not this defect. */
|
|
30
|
+
export declare function isEmptyStoredSessionsDirectory(inspection: StoredSessionsInspection): boolean;
|
|
31
|
+
export declare function classifyStoredSessionPanic(stderr: string): string | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Operator-facing cause: allow-listed phrases only, never arbitrary child bytes.
|
|
34
|
+
*
|
|
35
|
+
* The fallback is deliberately non-committal. Naming a specific cause for text we do not
|
|
36
|
+
* recognise sends the operator to the wrong place: an out-of-memory spawn failure rendered as
|
|
37
|
+
* "harness connection closed" points at sockets. An unrecognised cause is reported as such.
|
|
38
|
+
*/
|
|
39
|
+
export declare const UNRECOGNISED_STORED_SESSION_CAUSE = "unrecognised harness failure";
|
|
40
|
+
export declare function boundStoredSessionCause(text: string): string;
|
|
41
|
+
//# sourceMappingURL=stored-sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stored-sessions.d.ts","sourceRoot":"","sources":["../src/stored-sessions.ts"],"names":[],"mappings":"AAYA,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,MAAM,MAAM,wBAAwB,GAChC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,wBAAwB,CAajF;AAED,oGAAoG;AACpG,wBAAgB,8BAA8B,CAAC,UAAU,EAAE,wBAAwB,GAAG,OAAO,CAE5F;AAaD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG7E;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,iCAAiC,CAAC;AAEhF,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5D"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cotal-ai/connector-jcode",
|
|
3
3
|
"description": "Cotal connector for Jcode: a host-mode mesh peer driven through Jcode's Harness API bridge.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.50.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"esbuild": "^0.28.0",
|
|
30
30
|
"tsx": "^4.22.4",
|
|
31
31
|
"zod": "^4.4.3",
|
|
32
|
-
"@cotal-ai/core": "0.
|
|
33
|
-
"@cotal-ai/
|
|
34
|
-
"@cotal-ai/
|
|
32
|
+
"@cotal-ai/connector-core": "0.50.0",
|
|
33
|
+
"@cotal-ai/core": "0.50.0",
|
|
34
|
+
"@cotal-ai/smoke-kit": "0.0.0"
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
37
|
"dist"
|