@inixiative/foundry-core 0.5.10 → 0.5.12
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/package.json +3 -3
- package/src/adapters/markdown-docs.ts +7 -1
- package/src/adapters/markdown-hierarchy.ts +30 -2
- package/src/event-stream.ts +42 -1
- package/src/feedback/correction.ts +35 -1
- package/src/index.ts +9 -2
- package/src/model-registry.ts +37 -3
- package/src/scope.ts +4 -0
- package/src/thread.ts +6 -15
- package/src/token-tracker.ts +3 -2
- package/src/types.ts +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inixiative/foundry-core",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"bun": ">=1.4.4"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@inixiative/archive": "^0.
|
|
36
|
+
"@inixiative/archive": "^0.33.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependenciesMeta": {
|
|
39
39
|
"@inixiative/archive": {
|
|
@@ -41,6 +41,6 @@
|
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@inixiative/archive": "^0.
|
|
44
|
+
"@inixiative/archive": "^0.33.1"
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -293,7 +293,13 @@ export function secretInstructionPath(path: string, trusted: readonly string[] =
|
|
|
293
293
|
name.startsWith('.') ||
|
|
294
294
|
/^id_[a-z0-9]+/i.test(name) ||
|
|
295
295
|
/\.(pem|key|p12|pfx|keystore|jks)$/i.test(name) ||
|
|
296
|
-
rest.split(sep).some((part) => CREDENTIAL_DIRECTORIES.has(part))
|
|
296
|
+
rest.split(sep).some((part) => CREDENTIAL_DIRECTORIES.has(part)) ||
|
|
297
|
+
// A `.private` path is never read (FEAT-005): a segment named or ending `.private`, or holding `.private.`.
|
|
298
|
+
path
|
|
299
|
+
.split(sep)
|
|
300
|
+
.some(
|
|
301
|
+
(part) => part === '.private' || part.endsWith('.private') || part.includes('.private.'),
|
|
302
|
+
)
|
|
297
303
|
);
|
|
298
304
|
}
|
|
299
305
|
|
|
@@ -37,7 +37,7 @@ export interface HierarchyMatch {
|
|
|
37
37
|
readonly owners?: readonly string[];
|
|
38
38
|
/** The harness the composed prompt is for. */
|
|
39
39
|
readonly harness?: readonly string[];
|
|
40
|
-
/** The role composed for (e.g.
|
|
40
|
+
/** The role composed for (e.g. a thread role, a Warden's agent id), or one of its base roles. */
|
|
41
41
|
readonly roles?: readonly string[];
|
|
42
42
|
/** The slot composed (e.g. "instructions", "domainKnowledge"). */
|
|
43
43
|
readonly slots?: readonly string[];
|
|
@@ -124,6 +124,11 @@ export interface HierarchyLocation {
|
|
|
124
124
|
readonly harnessLoads?: readonly string[];
|
|
125
125
|
/** The role composed for; also `{role}` in paths. */
|
|
126
126
|
readonly role?: string;
|
|
127
|
+
/**
|
|
128
|
+
* Roles whose files come first: a source whose path names `{role}` is read once per base role, in
|
|
129
|
+
* order, then for `role` (e.g. a shared base role under each thread role).
|
|
130
|
+
*/
|
|
131
|
+
readonly baseRoles?: readonly string[];
|
|
127
132
|
/** The slot composed; also `{slot}` in paths. */
|
|
128
133
|
readonly slot?: string;
|
|
129
134
|
/**
|
|
@@ -221,7 +226,14 @@ function matches(when: HierarchyMatch | undefined, location: HierarchyLocation):
|
|
|
221
226
|
if (when.projects && !(location.projectId && when.projects.includes(location.projectId)))
|
|
222
227
|
return false;
|
|
223
228
|
if (when.owners && !(location.owner && when.owners.includes(location.owner))) return false;
|
|
224
|
-
|
|
229
|
+
// A base role counts as the role it sits under (a node for `main` serves every thread role).
|
|
230
|
+
if (
|
|
231
|
+
when.roles &&
|
|
232
|
+
![...(location.baseRoles ?? []), ...(location.role ? [location.role] : [])].some((role) =>
|
|
233
|
+
when.roles!.includes(role),
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
return false;
|
|
225
237
|
if (when.slots && !(location.slot && when.slots.includes(location.slot))) return false;
|
|
226
238
|
if (when.paths && !matchedPath(when.paths, location)) return false;
|
|
227
239
|
return true;
|
|
@@ -480,6 +492,22 @@ export async function discoverHierarchy(
|
|
|
480
492
|
) => {
|
|
481
493
|
if (!matches(source.when, location) || (source.id && loadedNatively.has(source.id))) return;
|
|
482
494
|
if (!slotted && !source.when?.slots && !unslottedApplies(entry.node, location)) return;
|
|
495
|
+
const templates = typeof source.path === 'string' ? [source.path] : source.path;
|
|
496
|
+
const roles =
|
|
497
|
+
location.baseRoles?.length && templates.some((path) => path.includes('{role}'))
|
|
498
|
+
? [...new Set([...location.baseRoles, ...(location.role ? [location.role] : [])])]
|
|
499
|
+
: undefined;
|
|
500
|
+
for (const pass of roles ? roles.map((role) => ({ ...vars, role })) : [vars])
|
|
501
|
+
await collect(entry, source, position, base, pass);
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
const collect = async (
|
|
505
|
+
entry: SelectedHierarchyNode,
|
|
506
|
+
source: HierarchyFileSource,
|
|
507
|
+
position: number,
|
|
508
|
+
base: string | undefined,
|
|
509
|
+
vars: Readonly<Record<string, string | undefined>>,
|
|
510
|
+
) => {
|
|
483
511
|
const place = (path: string) => (isAbsolute(path) ? path : base ? join(base, path) : undefined);
|
|
484
512
|
const literal = (template: string) => {
|
|
485
513
|
const expanded = expandHierarchyPath(template, vars, location.home);
|
package/src/event-stream.ts
CHANGED
|
@@ -42,7 +42,31 @@ export type StreamEvent =
|
|
|
42
42
|
message: string;
|
|
43
43
|
severity: 'error' | 'warn';
|
|
44
44
|
timestamp: number;
|
|
45
|
-
}
|
|
45
|
+
}
|
|
46
|
+
| ({ kind: 'activity'; timestamp: number } & SystemActivity);
|
|
47
|
+
|
|
48
|
+
export type ActivityEvent = Extract<StreamEvent, { kind: 'activity' }>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* What the system is doing around a thread, not in it (Aron, 2026-11-11): a project's Wardens
|
|
52
|
+
* starting, a decision process restarting, a rate limit. The viewer keeps the latest per `key`
|
|
53
|
+
* and shows it above the composer; `error` events stay for what failed and needs the person.
|
|
54
|
+
*/
|
|
55
|
+
export interface SystemActivity {
|
|
56
|
+
/** One thing that changes over time ("project-wardens:<projectId>"); a later event replaces it. */
|
|
57
|
+
readonly key: string;
|
|
58
|
+
readonly source: string;
|
|
59
|
+
/** The project it concerns; absent when it touches every thread (a login's rate limit). */
|
|
60
|
+
readonly projectId?: string;
|
|
61
|
+
/** `idle` is kept for the log only; the others show, in the status colours. */
|
|
62
|
+
readonly state: 'working' | 'ready' | 'degraded' | 'failed' | 'idle';
|
|
63
|
+
/** Shown until then unless a later event of the same key replaces it (a rate limit's end). */
|
|
64
|
+
readonly until?: number;
|
|
65
|
+
/** One plain sentence for the person. */
|
|
66
|
+
readonly summary: string;
|
|
67
|
+
/** The technical line, for the expanded log. */
|
|
68
|
+
readonly detail: string;
|
|
69
|
+
}
|
|
46
70
|
|
|
47
71
|
/**
|
|
48
72
|
* Unified event stream across all threads and sessions.
|
|
@@ -56,6 +80,8 @@ export type StreamEvent =
|
|
|
56
80
|
export class EventStream {
|
|
57
81
|
private _listeners: Array<(event: StreamEvent) => void> = [];
|
|
58
82
|
private _history: StreamEvent[] = [];
|
|
83
|
+
/** The latest activity of each key, kept past the history bound: a failure stays until it clears. */
|
|
84
|
+
private _activities = new Map<string, ActivityEvent>();
|
|
59
85
|
private _maxHistory: number;
|
|
60
86
|
|
|
61
87
|
constructor(maxHistory: number = 5000) {
|
|
@@ -64,6 +90,10 @@ export class EventStream {
|
|
|
64
90
|
|
|
65
91
|
/** Push an event into the stream. */
|
|
66
92
|
push(event: StreamEvent): void {
|
|
93
|
+
if (event.kind === 'activity') {
|
|
94
|
+
this._activities.delete(event.key);
|
|
95
|
+
this._activities.set(event.key, event);
|
|
96
|
+
}
|
|
67
97
|
this._history.push(event);
|
|
68
98
|
if (this._history.length > this._maxHistory) {
|
|
69
99
|
this._history.shift();
|
|
@@ -117,8 +147,19 @@ export class EventStream {
|
|
|
117
147
|
this.push({ kind: 'error', source, message, severity, timestamp: Date.now() });
|
|
118
148
|
}
|
|
119
149
|
|
|
150
|
+
/** Push what the system is doing (shown above the composer, never as a toast). */
|
|
151
|
+
pushActivity(activity: SystemActivity): void {
|
|
152
|
+
this.push({ kind: 'activity', ...activity, timestamp: Date.now() });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** The latest activity of every key, oldest first. */
|
|
156
|
+
activities(): ActivityEvent[] {
|
|
157
|
+
return [...this._activities.values()];
|
|
158
|
+
}
|
|
159
|
+
|
|
120
160
|
/** Clear history. */
|
|
121
161
|
clear(): void {
|
|
122
162
|
this._history = [];
|
|
163
|
+
this._activities.clear();
|
|
123
164
|
}
|
|
124
165
|
}
|
|
@@ -221,7 +221,11 @@ export function extractCorrections(
|
|
|
221
221
|
return { corrections, secretsScrubbed };
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
|
|
224
|
+
/**
|
|
225
|
+
* Credentials by their own shape (a provider's token prefix, a key block): specific enough that a
|
|
226
|
+
* match is a secret. Foundry lifts these from a message even when the person forgot `$$` (FEAT-005).
|
|
227
|
+
*/
|
|
228
|
+
const SHAPED_SECRETS: ReadonlyArray<readonly [RegExp, string]> = [
|
|
225
229
|
[/sk-ant-[A-Za-z0-9_-]{20,}/g, '[REDACTED:anthropic-key]'],
|
|
226
230
|
[/sk-[A-Za-z0-9_-]{20,}/g, '[REDACTED:api-key]'],
|
|
227
231
|
[/gh[pousr]_[A-Za-z0-9]{20,}/g, '[REDACTED:github-token]'],
|
|
@@ -245,6 +249,10 @@ const SECRET_PATTERNS: ReadonlyArray<readonly [RegExp, string]> = [
|
|
|
245
249
|
],
|
|
246
250
|
[/hooks\.slack\.com\/services\/[A-Za-z0-9/]+/g, '[REDACTED:slack-webhook]'],
|
|
247
251
|
[/\bASIA[A-Z0-9]{16}/g, '[REDACTED:aws-key]'],
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
/** Credentials by context (an assignment, a header, a URL): a match may be a secret, so it is scrubbed but never lifted. */
|
|
255
|
+
const CONTEXT_SECRETS: ReadonlyArray<readonly [RegExp, string]> = [
|
|
248
256
|
[/\b(Bearer|Basic|DPoP)\s+[A-Za-z0-9._~+/=-]{16,}/g, '$1 [REDACTED:credential]'],
|
|
249
257
|
[/\b([a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:)[^\s@/]+@/gi, '$1[REDACTED:password]@'],
|
|
250
258
|
[
|
|
@@ -257,6 +265,32 @@ const SECRET_PATTERNS: ReadonlyArray<readonly [RegExp, string]> = [
|
|
|
257
265
|
],
|
|
258
266
|
];
|
|
259
267
|
|
|
268
|
+
const SECRET_PATTERNS: ReadonlyArray<readonly [RegExp, string]> = [
|
|
269
|
+
...SHAPED_SECRETS,
|
|
270
|
+
...CONTEXT_SECRETS,
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* The shaped credentials safe to lift from a message the person forgot to mark: each never starts
|
|
275
|
+
* inside a word (`task-runner-…` holds `sk-…`), and the two loosest shapes (`sk-…` and
|
|
276
|
+
* `<word>_live_<…>`, which long hyphenated or snake_case names also fit) must carry a digit. Fresh
|
|
277
|
+
* regex objects, so lifting never shares match state with `scrubSecrets`. Scrubbing a false match is
|
|
278
|
+
* harmless; lifting one would take words out of the person's message.
|
|
279
|
+
*/
|
|
280
|
+
export const SHAPED_SECRET_PATTERNS: readonly RegExp[] = SHAPED_SECRETS.map(([pattern]) => {
|
|
281
|
+
const loose =
|
|
282
|
+
pattern.source.startsWith('sk-[') || pattern.source.includes('(?:live|test|prod|runtime)');
|
|
283
|
+
const digit = loose ? '(?=[A-Za-z0-9_-]*\\d)' : '';
|
|
284
|
+
return new RegExp(`(?<![A-Za-z0-9_-])${digit}(?:${pattern.source})`, pattern.flags);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
/** How many credentials known only by context (`PASSWORD=…`, `Bearer …`, a URL password) the text holds. */
|
|
288
|
+
export function contextSecretHits(text: string): number {
|
|
289
|
+
let hits = 0;
|
|
290
|
+
for (const [pattern] of CONTEXT_SECRETS) hits += [...text.matchAll(pattern)].length;
|
|
291
|
+
return hits;
|
|
292
|
+
}
|
|
293
|
+
|
|
260
294
|
/** Replace credential-shaped substrings. Transcripts are exactly where leaked keys live. */
|
|
261
295
|
export function scrubSecrets(text: string): { text: string; hits: number } {
|
|
262
296
|
let hits = 0;
|
package/src/index.ts
CHANGED
|
@@ -166,7 +166,13 @@ export {
|
|
|
166
166
|
verifyExpertDelivery,
|
|
167
167
|
} from './delivery-evidence';
|
|
168
168
|
// Observability
|
|
169
|
-
export {
|
|
169
|
+
export {
|
|
170
|
+
type ActivityEvent,
|
|
171
|
+
EventStream,
|
|
172
|
+
type SessionEvent,
|
|
173
|
+
type StreamEvent,
|
|
174
|
+
type SystemActivity,
|
|
175
|
+
} from './event-stream';
|
|
170
176
|
export {
|
|
171
177
|
type ExecuteHandler,
|
|
172
178
|
type ExecuteMeta,
|
|
@@ -186,6 +192,7 @@ export {
|
|
|
186
192
|
type CorrectionDetection,
|
|
187
193
|
type CorrectionEvent,
|
|
188
194
|
type CorrectionSignal,
|
|
195
|
+
contextSecretHits,
|
|
189
196
|
correctionClassifyMessages,
|
|
190
197
|
correctionId,
|
|
191
198
|
detectCorrection,
|
|
@@ -194,6 +201,7 @@ export {
|
|
|
194
201
|
type FeedbackTag,
|
|
195
202
|
parseCorrectionClassifications,
|
|
196
203
|
proseOf,
|
|
204
|
+
SHAPED_SECRET_PATTERNS,
|
|
197
205
|
scrubSecrets,
|
|
198
206
|
type TranscriptTurn,
|
|
199
207
|
} from './feedback/correction';
|
|
@@ -423,7 +431,6 @@ export {
|
|
|
423
431
|
type ThreadPlan,
|
|
424
432
|
type ThreadPlanItem,
|
|
425
433
|
type ThreadReference,
|
|
426
|
-
type ThreadSetup,
|
|
427
434
|
type ThreadSnooze,
|
|
428
435
|
type ThreadSpawn,
|
|
429
436
|
type ThreadStatus,
|
package/src/model-registry.ts
CHANGED
|
@@ -245,7 +245,7 @@ export interface ModelSweepOption {
|
|
|
245
245
|
label: string;
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
export const MODEL_REGISTRY_UPDATED_AT = '2026-
|
|
248
|
+
export const MODEL_REGISTRY_UPDATED_AT = '2026-11-11';
|
|
249
249
|
|
|
250
250
|
/**
|
|
251
251
|
* Decision default: Claude Haiku 5.5, the cheapest current Claude model tagged `judgment`. Held here
|
|
@@ -292,7 +292,7 @@ export const MODEL_REGISTRY: Record<string, FoundryProviderInfo> = {
|
|
|
292
292
|
models: [
|
|
293
293
|
{
|
|
294
294
|
id: 'fable',
|
|
295
|
-
label: 'Fable 5.
|
|
295
|
+
label: 'Fable 5.2',
|
|
296
296
|
tier: 'powerful',
|
|
297
297
|
costTier: 'high',
|
|
298
298
|
contextWindow: 1_000_000,
|
|
@@ -301,7 +301,7 @@ export const MODEL_REGISTRY: Record<string, FoundryProviderInfo> = {
|
|
|
301
301
|
nativeAlias: true,
|
|
302
302
|
capabilities: POWERFUL_REASONING,
|
|
303
303
|
reasoning: CLAUDE_REASONING,
|
|
304
|
-
notes: 'Claude Code alias for claude-fable-5-
|
|
304
|
+
notes: 'Claude Code alias for claude-fable-5-2.',
|
|
305
305
|
},
|
|
306
306
|
{
|
|
307
307
|
id: 'opus',
|
|
@@ -348,6 +348,17 @@ export const MODEL_REGISTRY: Record<string, FoundryProviderInfo> = {
|
|
|
348
348
|
capabilities: POWERFUL_REASONING,
|
|
349
349
|
reasoning: CLAUDE_REASONING,
|
|
350
350
|
},
|
|
351
|
+
{
|
|
352
|
+
id: 'claude-fable-5-2',
|
|
353
|
+
label: 'Fable 5.2',
|
|
354
|
+
tier: 'powerful',
|
|
355
|
+
costTier: 'high',
|
|
356
|
+
contextWindow: 1_000_000,
|
|
357
|
+
maxOutputTokens: 128_000,
|
|
358
|
+
runtimeKind: 'native-harness',
|
|
359
|
+
capabilities: POWERFUL_REASONING,
|
|
360
|
+
reasoning: CLAUDE_REASONING,
|
|
361
|
+
},
|
|
351
362
|
{
|
|
352
363
|
id: 'claude-fable-5-1',
|
|
353
364
|
label: 'Fable 5.1',
|
|
@@ -382,6 +393,18 @@ export const MODEL_REGISTRY: Record<string, FoundryProviderInfo> = {
|
|
|
382
393
|
credential: 'api-key',
|
|
383
394
|
enabledByDefault: true,
|
|
384
395
|
models: [
|
|
396
|
+
{
|
|
397
|
+
id: 'claude-fable-5-2',
|
|
398
|
+
label: 'Claude Fable 5.2',
|
|
399
|
+
tier: 'powerful',
|
|
400
|
+
costTier: 'high',
|
|
401
|
+
contextWindow: 1_000_000,
|
|
402
|
+
maxOutputTokens: 128_000,
|
|
403
|
+
runtimeKind: 'api',
|
|
404
|
+
capabilities: POWERFUL_REASONING,
|
|
405
|
+
reasoning: CLAUDE_REASONING,
|
|
406
|
+
notes: 'Thinking is always on; steer with effort.',
|
|
407
|
+
},
|
|
385
408
|
{
|
|
386
409
|
id: 'claude-fable-5-1',
|
|
387
410
|
label: 'Claude Fable 5.1',
|
|
@@ -416,6 +439,17 @@ export const MODEL_REGISTRY: Record<string, FoundryProviderInfo> = {
|
|
|
416
439
|
capabilities: POWERFUL_REASONING,
|
|
417
440
|
reasoning: CLAUDE_REASONING,
|
|
418
441
|
},
|
|
442
|
+
{
|
|
443
|
+
id: 'claude-sonnet-5-5',
|
|
444
|
+
label: 'Claude Sonnet 5.5',
|
|
445
|
+
tier: 'standard',
|
|
446
|
+
costTier: 'medium',
|
|
447
|
+
contextWindow: 1_000_000,
|
|
448
|
+
maxOutputTokens: 128_000,
|
|
449
|
+
runtimeKind: 'api',
|
|
450
|
+
capabilities: STANDARD_REASONING,
|
|
451
|
+
reasoning: CLAUDE_REASONING,
|
|
452
|
+
},
|
|
419
453
|
{
|
|
420
454
|
id: 'claude-sonnet-5',
|
|
421
455
|
label: 'Claude Sonnet 5',
|
package/src/scope.ts
CHANGED
|
@@ -16,6 +16,10 @@ export interface OwnershipScope {
|
|
|
16
16
|
readonly projectId?: string;
|
|
17
17
|
/** Where the thread's native session runs. Not ownership: sources that read the workspace resolve against it. */
|
|
18
18
|
readonly cwd?: string;
|
|
19
|
+
/** The thread role it composes as; absent: its worker's default. Not ownership. */
|
|
20
|
+
readonly role?: string;
|
|
21
|
+
/** The worker running its main session. Not ownership. */
|
|
22
|
+
readonly worker?: string;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
/** Copy a scope, dropping undefined fields so equality checks are stable. */
|
package/src/thread.ts
CHANGED
|
@@ -276,18 +276,15 @@ export interface ThreadMeta {
|
|
|
276
276
|
/** 1 for a child of a main thread. */
|
|
277
277
|
depth?: number;
|
|
278
278
|
|
|
279
|
-
/**
|
|
280
|
-
* Start-up of a thread whose Wardens are held in its own process (docs/THREAD-TREE.md spec C):
|
|
281
|
-
* turns typed meanwhile wait as `queued` until it is `ready` (or `degraded`).
|
|
282
|
-
*/
|
|
283
|
-
setup?: ThreadSetup;
|
|
284
|
-
|
|
285
279
|
/** Project that owns this thread. Scopes memory sources and tool calls. */
|
|
286
280
|
projectId?: string;
|
|
287
281
|
|
|
288
282
|
/** The harness running this thread's main session (`claude-code`, `codex`). Set at creation; never changed silently. */
|
|
289
283
|
worker?: string;
|
|
290
284
|
|
|
285
|
+
/** The thread role its instructions compose as (`coder`, `assistant`, …). Absent: its worker's default role. */
|
|
286
|
+
role?: string;
|
|
287
|
+
|
|
291
288
|
/** When this thread was created. */
|
|
292
289
|
readonly createdAt: number;
|
|
293
290
|
|
|
@@ -298,15 +295,6 @@ export interface ThreadMeta {
|
|
|
298
295
|
archivedAt?: number;
|
|
299
296
|
}
|
|
300
297
|
|
|
301
|
-
export interface ThreadSetup {
|
|
302
|
-
status: 'starting' | 'ready' | 'degraded';
|
|
303
|
-
startedAt: number;
|
|
304
|
-
readyAt?: number;
|
|
305
|
-
/** Every configured Warden, those holding a spare, and those that did not by the deadline. */
|
|
306
|
-
wardens: { expected: number; held: number; failed: string[] };
|
|
307
|
-
runtime: 'claude-in-session' | 'codex-in-process' | 'side-session';
|
|
308
|
-
}
|
|
309
|
-
|
|
310
298
|
export interface Dispatch<T = unknown> {
|
|
311
299
|
readonly agentId: string;
|
|
312
300
|
readonly timestamp: number;
|
|
@@ -403,6 +391,8 @@ export interface ThreadConfig {
|
|
|
403
391
|
projectId?: string;
|
|
404
392
|
/** The harness running this thread's main session. */
|
|
405
393
|
worker?: string;
|
|
394
|
+
/** The thread role its instructions compose as. */
|
|
395
|
+
role?: string;
|
|
406
396
|
/** Optional token tracker — auto-records costs for every dispatch. */
|
|
407
397
|
tokenTracker?: TokenTracker;
|
|
408
398
|
}
|
|
@@ -456,6 +446,7 @@ export class Thread {
|
|
|
456
446
|
...(opts?.forkedFrom ? { forkedFrom: opts.forkedFrom } : {}),
|
|
457
447
|
projectId: opts?.projectId,
|
|
458
448
|
...(opts?.worker ? { worker: opts.worker } : {}),
|
|
449
|
+
...(opts?.role ? { role: opts.role } : {}),
|
|
459
450
|
createdAt: now,
|
|
460
451
|
lastActiveAt: now,
|
|
461
452
|
};
|
package/src/token-tracker.ts
CHANGED
|
@@ -219,18 +219,19 @@ export interface UsageSummary {
|
|
|
219
219
|
|
|
220
220
|
// ---------------------------------------------------------------------------
|
|
221
221
|
// Default cost table. Anthropic: first-party API list prices per 1M tokens, as published at
|
|
222
|
-
// https://platform.claude.com/docs/en/about-claude/pricing (read 2026-11-
|
|
222
|
+
// https://platform.claude.com/docs/en/about-claude/pricing (read 2026-11-11; cache writes are the
|
|
223
223
|
// 5-minute rate, 1.25x input). Haiku 5.5 is its rate up to 100K-token prompts.
|
|
224
224
|
// ---------------------------------------------------------------------------
|
|
225
225
|
|
|
226
226
|
export const DEFAULT_COST_TABLE: CostTable = {
|
|
227
227
|
anthropic: {
|
|
228
|
+
'claude-fable-5-2': { inputPer1M: 8, outputPer1M: 40, cacheReadPer1M: 0.2 },
|
|
228
229
|
'claude-fable-5-1': { inputPer1M: 10, outputPer1M: 50, cacheReadPer1M: 0.25 },
|
|
229
230
|
'claude-fable-5': { inputPer1M: 10, outputPer1M: 50 },
|
|
230
231
|
'claude-opus-5-5': { inputPer1M: 4, outputPer1M: 20, cacheReadPer1M: 0.2 },
|
|
231
232
|
'claude-opus-5': { inputPer1M: 5, outputPer1M: 25 },
|
|
232
233
|
'claude-opus-4-8': { inputPer1M: 5, outputPer1M: 25 },
|
|
233
|
-
'claude-sonnet-5-5': { inputPer1M: 2, outputPer1M: 10, cacheReadPer1M: 0.
|
|
234
|
+
'claude-sonnet-5-5': { inputPer1M: 2, outputPer1M: 10, cacheReadPer1M: 0.1 },
|
|
234
235
|
'claude-sonnet-5': { inputPer1M: 2, outputPer1M: 10 },
|
|
235
236
|
'claude-haiku-5-5': { inputPer1M: 0.1, outputPer1M: 0.5 },
|
|
236
237
|
'claude-haiku-4-5': { inputPer1M: 1, outputPer1M: 5 },
|
package/src/types.ts
CHANGED
|
@@ -217,8 +217,6 @@ export interface LLMProvider {
|
|
|
217
217
|
* `threadPrefix` starts at once. `messages` end at those prefixes; no decision is made. Optional.
|
|
218
218
|
*/
|
|
219
219
|
prestart?(messages: LLMMessage[], opts: CompletionOpts): void;
|
|
220
|
-
/** A thread closed: release whatever the provider holds for it. Optional. */
|
|
221
|
-
releaseThread?(threadId: string): Promise<void>;
|
|
222
220
|
}
|
|
223
221
|
|
|
224
222
|
export interface EmbeddingProvider {
|