@inixiative/foundry-core 0.5.9 → 0.5.10
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 +4 -4
- package/src/action-prompt.ts +11 -2
- package/src/adapters/file-memory.ts +5 -0
- package/src/adapters/http-memory.ts +5 -0
- package/src/adapters/markdown-docs.ts +5 -0
- package/src/adapters/markdown-hierarchy.ts +7 -2
- package/src/adapters/sqlite-memory.ts +21 -6
- package/src/base-agent.ts +5 -0
- package/src/bounded-set.ts +5 -0
- package/src/cache-lifecycle.ts +5 -0
- package/src/capability.ts +5 -0
- package/src/clarifier.ts +5 -0
- package/src/classifier.ts +5 -0
- package/src/context-layer.ts +10 -0
- package/src/context-stack.ts +5 -0
- package/src/credentials.ts +5 -0
- package/src/decider.ts +5 -0
- package/src/delivery-evidence.ts +5 -0
- package/src/event-stream.ts +5 -0
- package/src/executor.ts +17 -4
- package/src/feedback/archive-turns.ts +5 -0
- package/src/feedback/correction.ts +13 -1
- package/src/feedback/experiment-gate.ts +5 -0
- package/src/feedback/review-findings.ts +5 -0
- package/src/feedback/warden-labels.ts +5 -0
- package/src/feedback/warden-prefilter.ts +5 -0
- package/src/feedback/warden-probes.ts +5 -0
- package/src/harness.ts +7 -2
- package/src/hydrator.ts +5 -0
- package/src/id.ts +5 -0
- package/src/index.ts +23 -11
- package/src/intervention.ts +5 -0
- package/src/messages.ts +5 -0
- package/src/middleware.ts +5 -0
- package/src/model-registry.ts +92 -2
- package/src/native-evidence.ts +18 -0
- package/src/native-tools.ts +5 -0
- package/src/retry.ts +5 -0
- package/src/router.ts +5 -0
- package/src/scope.ts +5 -0
- package/src/serialized-queue.ts +35 -0
- package/src/session-delivery.ts +5 -0
- package/src/signal.ts +5 -0
- package/src/thread.ts +196 -6
- package/src/token-counts.ts +5 -0
- package/src/token-tracker.ts +49 -24
- package/src/tools.ts +6 -1
- package/src/trace.ts +5 -0
- package/src/types.ts +5 -0
- package/src/hooks.ts +0 -133
package/src/token-tracker.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @atlas
|
|
3
|
+
* @kind service
|
|
4
|
+
* @partOf feature:usage
|
|
5
|
+
*/
|
|
1
6
|
import { sumTokenCounts, type TokenCounts, totalTokenCount } from './token-counts';
|
|
2
7
|
|
|
3
8
|
// ---------------------------------------------------------------------------
|
|
@@ -140,6 +145,10 @@ const attributionMatches = (
|
|
|
140
145
|
export interface ModelPricing {
|
|
141
146
|
readonly inputPer1M: number;
|
|
142
147
|
readonly outputPer1M: number;
|
|
148
|
+
/** Cache reads; default a tenth of input. */
|
|
149
|
+
readonly cacheReadPer1M?: number;
|
|
150
|
+
/** Cache writes (5-minute); default 1.25x input. */
|
|
151
|
+
readonly cacheWritePer1M?: number;
|
|
143
152
|
}
|
|
144
153
|
|
|
145
154
|
/**
|
|
@@ -209,11 +218,22 @@ export interface UsageSummary {
|
|
|
209
218
|
}
|
|
210
219
|
|
|
211
220
|
// ---------------------------------------------------------------------------
|
|
212
|
-
// Default cost table
|
|
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-01; cache writes are the
|
|
223
|
+
// 5-minute rate, 1.25x input). Haiku 5.5 is its rate up to 100K-token prompts.
|
|
213
224
|
// ---------------------------------------------------------------------------
|
|
214
225
|
|
|
215
226
|
export const DEFAULT_COST_TABLE: CostTable = {
|
|
216
227
|
anthropic: {
|
|
228
|
+
'claude-fable-5-1': { inputPer1M: 10, outputPer1M: 50, cacheReadPer1M: 0.25 },
|
|
229
|
+
'claude-fable-5': { inputPer1M: 10, outputPer1M: 50 },
|
|
230
|
+
'claude-opus-5-5': { inputPer1M: 4, outputPer1M: 20, cacheReadPer1M: 0.2 },
|
|
231
|
+
'claude-opus-5': { inputPer1M: 5, outputPer1M: 25 },
|
|
232
|
+
'claude-opus-4-8': { inputPer1M: 5, outputPer1M: 25 },
|
|
233
|
+
'claude-sonnet-5-5': { inputPer1M: 2, outputPer1M: 10, cacheReadPer1M: 0.2 },
|
|
234
|
+
'claude-sonnet-5': { inputPer1M: 2, outputPer1M: 10 },
|
|
235
|
+
'claude-haiku-5-5': { inputPer1M: 0.1, outputPer1M: 0.5 },
|
|
236
|
+
'claude-haiku-4-5': { inputPer1M: 1, outputPer1M: 5 },
|
|
217
237
|
'claude-opus-4-7': { inputPer1M: 5, outputPer1M: 25 },
|
|
218
238
|
'claude-opus-4-6': { inputPer1M: 5, outputPer1M: 25 },
|
|
219
239
|
'claude-sonnet-4-6': { inputPer1M: 3, outputPer1M: 15 },
|
|
@@ -239,10 +259,24 @@ export const DEFAULT_COST_TABLE: CostTable = {
|
|
|
239
259
|
'muse-spark-1.2-contributor': { inputPer1M: 0.1, outputPer1M: 0.2 },
|
|
240
260
|
'muse-spark-1.1': { inputPer1M: 1.25, outputPer1M: 4.25 },
|
|
241
261
|
},
|
|
242
|
-
// Claude Code provider tracks usage via session output, not cost table
|
|
243
|
-
'claude-code': {},
|
|
244
262
|
};
|
|
245
263
|
|
|
264
|
+
/**
|
|
265
|
+
* A model's price: its own provider's row, else the same model id under any provider. A Claude
|
|
266
|
+
* model run through the Claude Code login (provider `claude-code` or a decision host) is priced at
|
|
267
|
+
* its API list price: an API-equivalent cost, not what a subscription bills.
|
|
268
|
+
*/
|
|
269
|
+
export function modelPricing(
|
|
270
|
+
table: CostTable,
|
|
271
|
+
provider: string,
|
|
272
|
+
model: string,
|
|
273
|
+
): ModelPricing | undefined {
|
|
274
|
+
const own = table[provider]?.[model];
|
|
275
|
+
if (own) return own;
|
|
276
|
+
for (const rows of Object.values(table)) if (rows[model]) return rows[model];
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
246
280
|
// ---------------------------------------------------------------------------
|
|
247
281
|
// Token estimation
|
|
248
282
|
// ---------------------------------------------------------------------------
|
|
@@ -316,19 +350,14 @@ export class TokenTracker {
|
|
|
316
350
|
* Throws if budget.haltAt is exceeded (when configured).
|
|
317
351
|
*/
|
|
318
352
|
record(entry: Omit<UsageEntry, 'timestamp' | 'cost'>): UsageEntry {
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
entry.model,
|
|
322
|
-
entry.tokens.input,
|
|
323
|
-
entry.tokens.output,
|
|
324
|
-
entry.cached,
|
|
325
|
-
);
|
|
353
|
+
const pricing = modelPricing(this._costTable, entry.provider, entry.model);
|
|
354
|
+
const cost = pricing ? this._computeCost(pricing, entry.tokens, entry.cached) : 0;
|
|
326
355
|
|
|
327
356
|
const full: UsageEntry = {
|
|
328
357
|
...entry,
|
|
329
358
|
timestamp: Date.now(),
|
|
330
359
|
cost,
|
|
331
|
-
costKnown: !!
|
|
360
|
+
costKnown: !!pricing,
|
|
332
361
|
};
|
|
333
362
|
|
|
334
363
|
this._entries.push(full);
|
|
@@ -474,20 +503,16 @@ export class TokenTracker {
|
|
|
474
503
|
// Private helpers
|
|
475
504
|
// -----------------------------------------------------------------------
|
|
476
505
|
|
|
477
|
-
private _computeCost(
|
|
478
|
-
provider
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
cached?: boolean,
|
|
483
|
-
): number {
|
|
484
|
-
const pricing = this._costTable[provider]?.[model];
|
|
485
|
-
if (!pricing) return 0;
|
|
486
|
-
|
|
487
|
-
// Cached input tokens are typically free or heavily discounted
|
|
488
|
-
const effectiveInput = cached ? input * 0.1 : input;
|
|
506
|
+
private _computeCost(pricing: ModelPricing, tokens: TokenCounts, cached?: boolean): number {
|
|
507
|
+
// `cached` marks a whole call served from a provider cache that reports no split.
|
|
508
|
+
const input = cached ? tokens.input * 0.1 : tokens.input;
|
|
509
|
+
const perToken = (count: number | undefined, per1M: number) =>
|
|
510
|
+
((count ?? 0) / 1_000_000) * per1M;
|
|
489
511
|
return (
|
|
490
|
-
(
|
|
512
|
+
perToken(input, pricing.inputPer1M) +
|
|
513
|
+
perToken(tokens.output, pricing.outputPer1M) +
|
|
514
|
+
perToken(tokens.cacheRead, pricing.cacheReadPer1M ?? pricing.inputPer1M * 0.1) +
|
|
515
|
+
perToken(tokens.cacheWrite, pricing.cacheWritePer1M ?? pricing.inputPer1M * 1.25)
|
|
491
516
|
);
|
|
492
517
|
}
|
|
493
518
|
|
package/src/tools.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @atlas
|
|
3
|
+
* @kind type
|
|
4
|
+
* @partOf primitive:tools
|
|
5
|
+
*/
|
|
1
6
|
// ---------------------------------------------------------------------------
|
|
2
7
|
// Tools — typed interfaces for agent execution environments
|
|
3
8
|
// ---------------------------------------------------------------------------
|
|
@@ -721,7 +726,7 @@ export class ToolRegistry {
|
|
|
721
726
|
const api = tool.scoped ? tool.scoped(opts?.scope ?? {}) : tool;
|
|
722
727
|
return await api.request({
|
|
723
728
|
url: input.url as string,
|
|
724
|
-
method: (input.method as
|
|
729
|
+
method: (input.method as ApiRequest['method']) ?? 'GET',
|
|
725
730
|
body: input.body,
|
|
726
731
|
headers: input.headers as Record<string, string>,
|
|
727
732
|
});
|
package/src/trace.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @atlas
|
|
3
|
+
* @kind type
|
|
4
|
+
* @partOf primitive:agents
|
|
5
|
+
*/
|
|
1
6
|
import type { TokenCounts } from './token-counts';
|
|
2
7
|
// ---------------------------------------------------------------------------
|
|
3
8
|
// Shared types — used by harness for conditional invocation/activation
|
package/src/hooks.ts
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Types
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
export type HookPoint =
|
|
6
|
-
| 'pre:dispatch'
|
|
7
|
-
| 'post:dispatch'
|
|
8
|
-
| 'pre:classify'
|
|
9
|
-
| 'post:classify'
|
|
10
|
-
| 'pre:route'
|
|
11
|
-
| 'post:route'
|
|
12
|
-
| 'pre:compact'
|
|
13
|
-
| 'post:compact'
|
|
14
|
-
| 'session:create'
|
|
15
|
-
| 'session:archive'
|
|
16
|
-
| 'budget:warning'
|
|
17
|
-
| 'budget:exceeded'
|
|
18
|
-
| 'plan:enter'
|
|
19
|
-
| 'plan:exit'
|
|
20
|
-
| 'error:agent'
|
|
21
|
-
| 'error:provider';
|
|
22
|
-
|
|
23
|
-
export interface HookContext {
|
|
24
|
-
hookPoint: HookPoint;
|
|
25
|
-
threadId?: string;
|
|
26
|
-
agentId?: string;
|
|
27
|
-
payload?: unknown;
|
|
28
|
-
result?: unknown;
|
|
29
|
-
meta: Record<string, unknown>;
|
|
30
|
-
timestamp: number;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface HookResult {
|
|
34
|
-
action: 'continue' | 'skip' | 'abort' | 'redirect';
|
|
35
|
-
redirectTo?: string;
|
|
36
|
-
modified?: Partial<HookContext>;
|
|
37
|
-
annotations?: Record<string, unknown>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface HookHandler {
|
|
41
|
-
id: string;
|
|
42
|
-
points: HookPoint[];
|
|
43
|
-
priority?: number;
|
|
44
|
-
handler: (ctx: HookContext) => Promise<HookResult>;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface PlanModeTrigger {
|
|
48
|
-
kind: 'complexity' | 'newDomain' | 'largeDiff' | 'custom';
|
|
49
|
-
threshold?: number;
|
|
50
|
-
detect?: (ctx: HookContext) => boolean;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface PlanModeConfig {
|
|
54
|
-
triggers: PlanModeTrigger[];
|
|
55
|
-
plannerAgentId: string;
|
|
56
|
-
requireApproval?: boolean;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
// HookRegistry — engine mechanism, no built-in behaviors
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
|
-
|
|
63
|
-
const DEFAULT_PRIORITY = 100;
|
|
64
|
-
|
|
65
|
-
export class HookRegistry {
|
|
66
|
-
private _handlers: Map<string, HookHandler> = new Map();
|
|
67
|
-
|
|
68
|
-
register(handler: HookHandler): () => void {
|
|
69
|
-
this._handlers.set(handler.id, handler);
|
|
70
|
-
return () => {
|
|
71
|
-
this._handlers.delete(handler.id);
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
unregister(id: string): boolean {
|
|
76
|
-
return this._handlers.delete(id);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async execute(
|
|
80
|
-
point: HookPoint,
|
|
81
|
-
ctx: HookContext,
|
|
82
|
-
): Promise<HookContext & { action: HookResult['action'] }> {
|
|
83
|
-
const handlers = this.forPoint(point);
|
|
84
|
-
let current = { ...ctx };
|
|
85
|
-
let action: HookResult['action'] = 'continue';
|
|
86
|
-
|
|
87
|
-
for (const handler of handlers) {
|
|
88
|
-
const result = await handler.handler(current);
|
|
89
|
-
action = result.action;
|
|
90
|
-
|
|
91
|
-
if (result.modified) {
|
|
92
|
-
current = {
|
|
93
|
-
...current,
|
|
94
|
-
...result.modified,
|
|
95
|
-
meta: { ...current.meta, ...result.modified.meta },
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (result.annotations) {
|
|
100
|
-
current.meta = { ...current.meta, ...result.annotations };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (result.redirectTo) {
|
|
104
|
-
current.meta = { ...current.meta, redirectTo: result.redirectTo };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (action === 'abort' || action === 'skip' || action === 'redirect') {
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return { ...current, action };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
forPoint(point: HookPoint): ReadonlyArray<HookHandler> {
|
|
116
|
-
const matching: HookHandler[] = [];
|
|
117
|
-
for (const handler of this._handlers.values()) {
|
|
118
|
-
if (handler.points.includes(point)) {
|
|
119
|
-
matching.push(handler);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
matching.sort((a, b) => (a.priority ?? DEFAULT_PRIORITY) - (b.priority ?? DEFAULT_PRIORITY));
|
|
123
|
-
return matching;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
get size(): number {
|
|
127
|
-
return this._handlers.size;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
get handlers(): ReadonlyArray<HookHandler> {
|
|
131
|
-
return [...this._handlers.values()];
|
|
132
|
-
}
|
|
133
|
-
}
|