@leanandmean/coding-agent 0.74.1-scramjet.34 → 0.74.1-scramjet.39
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/CHANGELOG.md +7 -0
- package/README.md +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +1 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +2 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +15 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +90 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +6 -5
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +48 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +16 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/output-throughput-history.d.ts +25 -0
- package/dist/core/output-throughput-history.d.ts.map +1 -0
- package/dist/core/output-throughput-history.js +240 -0
- package/dist/core/output-throughput-history.js.map +1 -0
- package/dist/core/output-throughput.d.ts +43 -0
- package/dist/core/output-throughput.d.ts.map +1 -0
- package/dist/core/output-throughput.js +161 -0
- package/dist/core/output-throughput.js.map +1 -0
- package/dist/core/sdk.d.ts +2 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +11 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +6 -2
- package/dist/core/skills.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +6 -2
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +3 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +9 -3
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +3 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +8 -2
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +3 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +45 -7
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/extensions.md +24 -4
- package/docs/models.md +2 -0
- package/docs/session-format.md +2 -1
- package/docs/skills.md +2 -2
- package/docs/usage.md +3 -1
- package/package.json +8 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-throughput-history.js","sourceRoot":"","sources":["../../src/core/output-throughput-history.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdir, open, readFile, realpath, rename, unlink, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { OutputThroughputHistory, type OutputThroughputSample } from \"./output-throughput.js\";\n\n// SCRAMJET-DIVERGENCE: Persist bounded, profile-scoped provider throughput across concurrent processes (#476).\n\nconst HISTORY_VERSION = 1;\nconst MAX_SAMPLES_PER_MODEL = 20;\nconst MAX_HISTORY_SAMPLES = 20000;\nconst MAX_STRING_LENGTH = 512;\nconst queues = new Map<string, Promise<void>>();\n\nexport interface OutputThroughputHistoryDiagnostic {\n\toperation: \"refresh\" | \"lock\" | \"read\" | \"validate\" | \"write\" | \"rename\" | \"unlink\" | \"unlock\";\n\tpath: string;\n\tmessage: string;\n\tcause?: unknown;\n}\n\ninterface HistoryDocument {\n\tversion: 1;\n\tsamples: OutputThroughputSample[];\n}\n\nexport class OutputThroughputHistoryStore {\n\tprivate history = new OutputThroughputHistory();\n\tprivate queueKey?: Promise<string>;\n\tprivate pending: Promise<void> = Promise.resolve();\n\tprivate readonly reportedDiagnostics = new Set<string>();\n\n\tconstructor(\n\t\treadonly path: string,\n\t\tprivate readonly onDiagnostic?: (diagnostic: OutputThroughputHistoryDiagnostic) => void,\n\t) {}\n\n\tsnapshot(): OutputThroughputHistory {\n\t\treturn historyFromSamples(this.history.allSamples());\n\t}\n\n\tasync refresh(): Promise<OutputThroughputHistory> {\n\t\tawait this.enqueue(async () => {\n\t\t\tconst document = await this.readDocument(\"refresh\");\n\t\t\tif (document) this.history = historyFromSamples(document.samples);\n\t\t});\n\t\treturn this.snapshot();\n\t}\n\n\tsubmit(sample: OutputThroughputSample): void {\n\t\tif (!isValidSample(sample)) {\n\t\t\tthis.report(\"validate\", \"Invalid output-throughput sample was not persisted; existing history was preserved\");\n\t\t\treturn;\n\t\t}\n\t\tconst normalized = normalizeSample(sample);\n\t\tthis.pending = this.enqueue(() => this.update(normalized));\n\t}\n\n\tasync flush(): Promise<void> {\n\t\tawait this.pending;\n\t}\n\n\tprivate async update(sample: OutputThroughputSample): Promise<void> {\n\t\tawait mkdir(dirname(this.path), { recursive: true });\n\t\tconst anchorPath = join(dirname(this.path), `${basename(this.path, \".json\")}.lock`);\n\t\ttry {\n\t\t\tawait writeFile(anchorPath, \"\", { flag: \"a\" });\n\t\t} catch (cause) {\n\t\t\tthis.report(\"lock\", \"Could not prepare the throughput-history lock; existing history was preserved\", cause);\n\t\t\treturn;\n\t\t}\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\ttry {\n\t\t\trelease = await lockfile.lock(anchorPath, {\n\t\t\t\trealpath: false,\n\t\t\t\tretries: { retries: 8, factor: 2, minTimeout: 25, maxTimeout: 1000, randomize: true },\n\t\t\t\tstale: 30000,\n\t\t\t});\n\t\t\tconst current = await this.readDocument(\"read\");\n\t\t\tif (!current) return;\n\t\t\tconst samples = mergeSamples(current.samples, sample);\n\t\t\tconst document: HistoryDocument = { version: HISTORY_VERSION, samples };\n\t\t\tconst temporaryPath = join(dirname(this.path), `.${basename(this.path)}.${process.pid}.${randomUUID()}.tmp`);\n\t\t\tlet temporaryCreated = false;\n\t\t\ttry {\n\t\t\t\tconst handle = await open(temporaryPath, \"wx\", 0o600);\n\t\t\t\ttemporaryCreated = true;\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.writeFile(`${JSON.stringify(document, null, 2)}\\n`, \"utf8\");\n\t\t\t\t\tawait handle.sync();\n\t\t\t\t} finally {\n\t\t\t\t\tawait handle.close();\n\t\t\t\t}\n\t\t\t\tawait rename(temporaryPath, this.path);\n\t\t\t\ttemporaryCreated = false;\n\t\t\t\tthis.history = historyFromSamples(samples);\n\t\t\t} catch (cause) {\n\t\t\t\tthis.report(\"write\", \"Could not commit output-throughput history; existing history was preserved\", cause);\n\t\t\t} finally {\n\t\t\t\tif (temporaryCreated) {\n\t\t\t\t\tawait unlink(temporaryPath).catch((cause) => {\n\t\t\t\t\t\tthis.report(\"unlink\", \"Could not remove the temporary output-throughput history file\", cause);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (cause) {\n\t\t\tthis.report(\"lock\", \"Could not lock output-throughput history; existing history was preserved\", cause);\n\t\t} finally {\n\t\t\tawait release?.().catch((cause) => {\n\t\t\t\tthis.report(\"unlock\", \"Could not release the output-throughput history lock\", cause);\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async readDocument(operation: \"refresh\" | \"read\"): Promise<HistoryDocument | undefined> {\n\t\tlet raw: string;\n\t\ttry {\n\t\t\traw = await readFile(this.path, \"utf8\");\n\t\t} catch (cause) {\n\t\t\tif (isMissingFile(cause)) return { version: HISTORY_VERSION, samples: [] };\n\t\t\tthis.report(\n\t\t\t\toperation,\n\t\t\t\t\"Could not read output-throughput history; the last valid snapshot was preserved\",\n\t\t\t\tcause,\n\t\t\t);\n\t\t\treturn undefined;\n\t\t}\n\t\ttry {\n\t\t\tconst value: unknown = JSON.parse(raw);\n\t\t\tif (!isHistoryDocument(value)) throw new Error(\"unsupported version or invalid schema\");\n\t\t\treturn { version: HISTORY_VERSION, samples: value.samples.map(normalizeSample) };\n\t\t} catch (cause) {\n\t\t\tthis.report(\n\t\t\t\t\"validate\",\n\t\t\t\t\"Output-throughput history is malformed or unsupported; original bytes and the last valid snapshot were preserved\",\n\t\t\t\tcause,\n\t\t\t);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async enqueue(operation: () => Promise<void>): Promise<void> {\n\t\ttry {\n\t\t\tif (!this.queueKey) this.queueKey = canonicalPath(this.path);\n\t\t\tconst queueKey = this.queueKey;\n\t\t\tlet key: string;\n\t\t\ttry {\n\t\t\t\tkey = await queueKey;\n\t\t\t} catch (cause) {\n\t\t\t\tif (this.queueKey === queueKey) this.queueKey = undefined;\n\t\t\t\tthrow cause;\n\t\t\t}\n\t\t\tconst previous = queues.get(key) ?? Promise.resolve();\n\t\t\tconst current = previous.catch(() => undefined).then(operation);\n\t\t\tqueues.set(key, current);\n\t\t\ttry {\n\t\t\t\tawait current;\n\t\t\t} finally {\n\t\t\t\tif (queues.get(key) === current) queues.delete(key);\n\t\t\t}\n\t\t} catch (cause) {\n\t\t\tthis.report(\"write\", \"Output-throughput history operation failed; existing history was preserved\", cause);\n\t\t}\n\t}\n\n\tprivate report(operation: OutputThroughputHistoryDiagnostic[\"operation\"], message: string, cause?: unknown): void {\n\t\tconst normalizedCause = cause instanceof Error ? cause.message : cause === undefined ? \"\" : String(cause);\n\t\tconst key = `${resolve(this.path)}\\0${operation}\\0${normalizedCause}`;\n\t\tif (this.reportedDiagnostics.has(key)) return;\n\t\tthis.reportedDiagnostics.add(key);\n\t\tthis.onDiagnostic?.({ operation, path: this.path, message, cause });\n\t}\n}\n\nasync function canonicalPath(path: string): Promise<string> {\n\tawait mkdir(dirname(path), { recursive: true });\n\tconst parent = await realpath(dirname(path));\n\treturn join(parent, basename(path));\n}\n\nfunction isHistoryDocument(value: unknown): value is HistoryDocument {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst document = value as { version?: unknown; samples?: unknown };\n\treturn (\n\t\tdocument.version === HISTORY_VERSION &&\n\t\tArray.isArray(document.samples) &&\n\t\tdocument.samples.length <= MAX_HISTORY_SAMPLES &&\n\t\tdocument.samples.every(isValidSample)\n\t);\n}\n\nfunction isValidSample(value: unknown): value is OutputThroughputSample {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst sample = value as Partial<OutputThroughputSample>;\n\treturn (\n\t\tisBoundedString(sample.provider) &&\n\t\tisBoundedString(sample.model) &&\n\t\t(sample.responseModel === undefined || isBoundedString(sample.responseModel)) &&\n\t\tisPositiveFinite(sample.outputTokens) &&\n\t\tisPositiveFinite(sample.durationMs) &&\n\t\tNumber.isFinite(sample.observedAt) &&\n\t\t(sample.observedAt ?? -1) >= 0\n\t);\n}\n\nfunction normalizeSample(sample: OutputThroughputSample): OutputThroughputSample {\n\treturn {\n\t\tprovider: sample.provider,\n\t\tmodel: sample.model,\n\t\t...(sample.responseModel === undefined ? {} : { responseModel: sample.responseModel }),\n\t\toutputTokens: sample.outputTokens,\n\t\tdurationMs: sample.durationMs,\n\t\tobservedAt: sample.observedAt,\n\t};\n}\n\nfunction isBoundedString(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.length > 0 && value.length <= MAX_STRING_LENGTH;\n}\n\nfunction isPositiveFinite(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isFinite(value) && value > 0;\n}\n\nfunction mergeSamples(\n\tsamples: readonly OutputThroughputSample[],\n\tadded: OutputThroughputSample,\n): OutputThroughputSample[] {\n\tconst unique = new Map<string, OutputThroughputSample>();\n\tfor (const sample of [...samples, added]) unique.set(sampleIdentity(sample), normalizeSample(sample));\n\tconst byModel = new Map<string, OutputThroughputSample[]>();\n\tfor (const sample of unique.values()) {\n\t\tconst key = `${sample.provider.length}:${sample.provider}${sample.model}`;\n\t\tconst group = byModel.get(key) ?? [];\n\t\tgroup.push(sample);\n\t\tbyModel.set(key, group);\n\t}\n\treturn [...byModel.values()]\n\t\t.flatMap((group) => group.sort((left, right) => left.observedAt - right.observedAt).slice(-MAX_SAMPLES_PER_MODEL))\n\t\t.sort((left, right) => left.observedAt - right.observedAt)\n\t\t.slice(-MAX_HISTORY_SAMPLES);\n}\n\nfunction sampleIdentity(sample: OutputThroughputSample): string {\n\treturn JSON.stringify([\n\t\tsample.provider,\n\t\tsample.model,\n\t\tsample.responseModel ?? null,\n\t\tsample.outputTokens,\n\t\tsample.durationMs,\n\t\tsample.observedAt,\n\t]);\n}\n\nfunction historyFromSamples(samples: readonly OutputThroughputSample[]): OutputThroughputHistory {\n\tconst history = new OutputThroughputHistory();\n\tfor (const sample of samples) history.add(sample);\n\treturn history;\n}\n\nfunction isMissingFile(cause: unknown): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\";\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AssistantMessage, AssistantMessageEvent } from "@leanandmean/ai";
|
|
2
|
+
export interface OutputThroughputIdentity {
|
|
3
|
+
provider: string;
|
|
4
|
+
model: string;
|
|
5
|
+
}
|
|
6
|
+
export interface OutputThroughputSample extends OutputThroughputIdentity {
|
|
7
|
+
responseModel?: string;
|
|
8
|
+
outputTokens: number;
|
|
9
|
+
durationMs: number;
|
|
10
|
+
observedAt: number;
|
|
11
|
+
}
|
|
12
|
+
export interface OutputThroughputTrackerOptions {
|
|
13
|
+
monotonicNow?: () => number;
|
|
14
|
+
wallNow?: () => number;
|
|
15
|
+
}
|
|
16
|
+
export declare class OutputThroughputTracker {
|
|
17
|
+
private readonly monotonicNow;
|
|
18
|
+
private readonly wallNow;
|
|
19
|
+
private active?;
|
|
20
|
+
private nextGeneration;
|
|
21
|
+
constructor(options?: OutputThroughputTrackerOptions);
|
|
22
|
+
get generation(): number;
|
|
23
|
+
get outputActive(): boolean;
|
|
24
|
+
get activeIdentity(): OutputThroughputIdentity | undefined;
|
|
25
|
+
start(identity: OutputThroughputIdentity): number;
|
|
26
|
+
reset(): void;
|
|
27
|
+
observe(generation: number, event: AssistantMessageEvent): void;
|
|
28
|
+
liveRate(): number | undefined;
|
|
29
|
+
finalize(generation: number, message: AssistantMessage): OutputThroughputSample | undefined;
|
|
30
|
+
}
|
|
31
|
+
export declare class OutputThroughputHistory {
|
|
32
|
+
private readonly samplesByModel;
|
|
33
|
+
add(sample: OutputThroughputSample): void;
|
|
34
|
+
samples(provider: string, model: string): readonly OutputThroughputSample[];
|
|
35
|
+
allSamples(): readonly OutputThroughputSample[];
|
|
36
|
+
latestSample(provider: string, model: string): OutputThroughputSample | undefined;
|
|
37
|
+
latestRate(provider: string, model: string): number | undefined;
|
|
38
|
+
median(provider: string, model: string): number | undefined;
|
|
39
|
+
}
|
|
40
|
+
export declare function formatLiveOutputRate(rate: number): string | undefined;
|
|
41
|
+
export declare function formatCompletedOutputRate(rate: number): string | undefined;
|
|
42
|
+
export declare function formatHistoricalOutputRate(rate: number): string | undefined;
|
|
43
|
+
//# sourceMappingURL=output-throughput.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-throughput.d.ts","sourceRoot":"","sources":["../../src/core/output-throughput.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAS/E,MAAM,WAAW,wBAAwB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAuB,SAAQ,wBAAwB;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,8BAA8B;IAC9C,YAAY,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB;AAYD,qBAAa,uBAAuB;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,MAAM,CAAC,CAAoB;IACnC,OAAO,CAAC,cAAc,CAAK;IAE3B,YAAY,OAAO,GAAE,8BAAmC,EAGvD;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,IAAI,cAAc,IAAI,wBAAwB,GAAG,SAAS,CAEzD;IAED,KAAK,CAAC,QAAQ,EAAE,wBAAwB,GAAG,MAAM,CAIhD;IAED,KAAK,IAAI,IAAI,CAGZ;IAED,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,IAAI,CAK9D;IAED,QAAQ,IAAI,MAAM,GAAG,SAAS,CAa7B;IAED,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,sBAAsB,GAAG,SAAS,CAgC1F;CACD;AAED,qBAAa,uBAAuB;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+C;IAE9E,GAAG,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAOxC;IAED,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,sBAAsB,EAAE,CAE1E;IAED,UAAU,IAAI,SAAS,sBAAsB,EAAE,CAE9C;IAED,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAEhF;IAED,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK9D;IAED,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ1D;CACD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1E;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE3E","sourcesContent":["import type { AssistantMessage, AssistantMessageEvent } from \"@leanandmean/ai\";\n\n// SCRAMJET-DIVERGENCE: Measure and aggregate qualified provider output throughput by requested model (#476).\n\nconst LIVE_WINDOW_MS = 2000;\nconst BYTES_PER_TOKEN = 4;\nconst MAX_SAMPLES_PER_MODEL = 20;\nconst encoder = new TextEncoder();\n\nexport interface OutputThroughputIdentity {\n\tprovider: string;\n\tmodel: string;\n}\n\nexport interface OutputThroughputSample extends OutputThroughputIdentity {\n\tresponseModel?: string;\n\toutputTokens: number;\n\tdurationMs: number;\n\tobservedAt: number;\n}\n\nexport interface OutputThroughputTrackerOptions {\n\tmonotonicNow?: () => number;\n\twallNow?: () => number;\n}\n\ninterface Observation {\n\ttime: number;\n\ttokens: number;\n}\n\ninterface ActiveMeasurement extends OutputThroughputIdentity {\n\tgeneration: number;\n\tobservations: Observation[];\n}\n\nexport class OutputThroughputTracker {\n\tprivate readonly monotonicNow: () => number;\n\tprivate readonly wallNow: () => number;\n\tprivate active?: ActiveMeasurement;\n\tprivate nextGeneration = 0;\n\n\tconstructor(options: OutputThroughputTrackerOptions = {}) {\n\t\tthis.monotonicNow = options.monotonicNow ?? (() => performance.now());\n\t\tthis.wallNow = options.wallNow ?? (() => Date.now());\n\t}\n\n\tget generation(): number {\n\t\treturn this.nextGeneration;\n\t}\n\n\tget outputActive(): boolean {\n\t\treturn (this.active?.observations.length ?? 0) > 0;\n\t}\n\n\tget activeIdentity(): OutputThroughputIdentity | undefined {\n\t\treturn this.active ? { provider: this.active.provider, model: this.active.model } : undefined;\n\t}\n\n\tstart(identity: OutputThroughputIdentity): number {\n\t\tconst generation = ++this.nextGeneration;\n\t\tthis.active = { ...identity, generation, observations: [] };\n\t\treturn generation;\n\t}\n\n\treset(): void {\n\t\tthis.nextGeneration++;\n\t\tthis.active = undefined;\n\t}\n\n\tobserve(generation: number, event: AssistantMessageEvent): void {\n\t\tif (generation !== this.active?.generation || !isGeneratedDelta(event) || event.delta.length === 0) return;\n\t\tconst time = this.monotonicNow();\n\t\tif (!Number.isFinite(time)) return;\n\t\tthis.active.observations.push({ time, tokens: encoder.encode(event.delta).byteLength / BYTES_PER_TOKEN });\n\t}\n\n\tliveRate(): number | undefined {\n\t\tif (!this.active) return undefined;\n\t\tconst now = this.monotonicNow();\n\t\tif (!Number.isFinite(now)) return undefined;\n\t\tconst cutoff = now - LIVE_WINDOW_MS;\n\t\tconst retained = this.active.observations.filter((observation) => observation.time >= cutoff);\n\t\tconst first = retained[0];\n\t\tif (!first) return undefined;\n\t\tconst elapsedMs = now - first.time;\n\t\tif (!(elapsedMs > 0)) return undefined;\n\t\tconst tokens = retained.reduce((total, observation) => total + observation.tokens, 0);\n\t\tconst rate = (tokens * 1000) / elapsedMs;\n\t\treturn Number.isFinite(rate) && rate > 0 ? rate : undefined;\n\t}\n\n\tfinalize(generation: number, message: AssistantMessage): OutputThroughputSample | undefined {\n\t\tif (generation !== this.active?.generation) return undefined;\n\t\tconst active = this.active;\n\t\tthis.reset();\n\t\tconst first = active.observations[0];\n\t\tconst last = active.observations.at(-1);\n\t\tconst outputTokens = message.usage.output;\n\t\tif (\n\t\t\tmessage.origin !== \"provider\" ||\n\t\t\tmessage.provider !== active.provider ||\n\t\t\tmessage.model !== active.model ||\n\t\t\t!isSuccessfulStop(message.stopReason) ||\n\t\t\t!Number.isFinite(outputTokens) ||\n\t\t\toutputTokens <= 0 ||\n\t\t\tactive.observations.length < 2 ||\n\t\t\t!first ||\n\t\t\t!last\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst durationMs = last.time - first.time;\n\t\tif (!Number.isFinite(durationMs) || durationMs <= 0) return undefined;\n\t\tconst observedAt = this.wallNow();\n\t\tif (!Number.isFinite(observedAt)) return undefined;\n\t\treturn {\n\t\t\tprovider: active.provider,\n\t\t\tmodel: active.model,\n\t\t\t...(message.responseModel ? { responseModel: message.responseModel } : {}),\n\t\t\toutputTokens,\n\t\t\tdurationMs,\n\t\t\tobservedAt,\n\t\t};\n\t}\n}\n\nexport class OutputThroughputHistory {\n\tprivate readonly samplesByModel = new Map<string, OutputThroughputSample[]>();\n\n\tadd(sample: OutputThroughputSample): void {\n\t\tif (!isValidSample(sample)) return;\n\t\tconst key = modelKey(sample.provider, sample.model);\n\t\tconst samples = [...(this.samplesByModel.get(key) ?? []), { ...sample }]\n\t\t\t.sort((left, right) => left.observedAt - right.observedAt)\n\t\t\t.slice(-MAX_SAMPLES_PER_MODEL);\n\t\tthis.samplesByModel.set(key, samples);\n\t}\n\n\tsamples(provider: string, model: string): readonly OutputThroughputSample[] {\n\t\treturn (this.samplesByModel.get(modelKey(provider, model)) ?? []).map((sample) => ({ ...sample }));\n\t}\n\n\tallSamples(): readonly OutputThroughputSample[] {\n\t\treturn [...this.samplesByModel.values()].flat().map((sample) => ({ ...sample }));\n\t}\n\n\tlatestSample(provider: string, model: string): OutputThroughputSample | undefined {\n\t\treturn this.samples(provider, model).at(-1);\n\t}\n\n\tlatestRate(provider: string, model: string): number | undefined {\n\t\tconst sample = this.latestSample(provider, model);\n\t\tif (!sample) return undefined;\n\t\tconst rate = (sample.outputTokens * 1000) / sample.durationMs;\n\t\treturn Number.isFinite(rate) && rate > 0 ? rate : undefined;\n\t}\n\n\tmedian(provider: string, model: string): number | undefined {\n\t\tconst rates = this.samples(provider, model)\n\t\t\t.map((sample) => (sample.outputTokens * 1000) / sample.durationMs)\n\t\t\t.filter((rate) => Number.isFinite(rate) && rate > 0)\n\t\t\t.sort((left, right) => left - right);\n\t\tif (rates.length === 0) return undefined;\n\t\tconst middle = Math.floor(rates.length / 2);\n\t\treturn rates.length % 2 === 0 ? ((rates[middle - 1] ?? 0) + (rates[middle] ?? 0)) / 2 : rates[middle];\n\t}\n}\n\nexport function formatLiveOutputRate(rate: number): string | undefined {\n\treturn formatCompletedOutputRate(rate);\n}\n\nexport function formatCompletedOutputRate(rate: number): string | undefined {\n\tif (!Number.isFinite(rate) || rate <= 0) return undefined;\n\treturn `${Math.round(rate)}tok/s`;\n}\n\nexport function formatHistoricalOutputRate(rate: number): string | undefined {\n\treturn formatCompletedOutputRate(rate);\n}\n\nfunction isGeneratedDelta(\n\tevent: AssistantMessageEvent,\n): event is Extract<AssistantMessageEvent, { type: \"text_delta\" | \"thinking_delta\" | \"toolcall_delta\" }> {\n\treturn event.type === \"text_delta\" || event.type === \"thinking_delta\" || event.type === \"toolcall_delta\";\n}\n\nfunction isSuccessfulStop(stopReason: AssistantMessage[\"stopReason\"]): boolean {\n\treturn stopReason === \"stop\" || stopReason === \"length\" || stopReason === \"toolUse\";\n}\n\nfunction isValidSample(sample: OutputThroughputSample): boolean {\n\treturn (\n\t\tsample.provider.length > 0 &&\n\t\tsample.model.length > 0 &&\n\t\tNumber.isFinite(sample.outputTokens) &&\n\t\tsample.outputTokens > 0 &&\n\t\tNumber.isFinite(sample.durationMs) &&\n\t\tsample.durationMs > 0 &&\n\t\tNumber.isFinite(sample.observedAt)\n\t);\n}\n\nfunction modelKey(provider: string, model: string): string {\n\treturn `${provider.length}:${provider}${model}`;\n}\n"]}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// SCRAMJET-DIVERGENCE: Measure and aggregate qualified provider output throughput by requested model (#476).
|
|
2
|
+
const LIVE_WINDOW_MS = 2000;
|
|
3
|
+
const BYTES_PER_TOKEN = 4;
|
|
4
|
+
const MAX_SAMPLES_PER_MODEL = 20;
|
|
5
|
+
const encoder = new TextEncoder();
|
|
6
|
+
export class OutputThroughputTracker {
|
|
7
|
+
monotonicNow;
|
|
8
|
+
wallNow;
|
|
9
|
+
active;
|
|
10
|
+
nextGeneration = 0;
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.monotonicNow = options.monotonicNow ?? (() => performance.now());
|
|
13
|
+
this.wallNow = options.wallNow ?? (() => Date.now());
|
|
14
|
+
}
|
|
15
|
+
get generation() {
|
|
16
|
+
return this.nextGeneration;
|
|
17
|
+
}
|
|
18
|
+
get outputActive() {
|
|
19
|
+
return (this.active?.observations.length ?? 0) > 0;
|
|
20
|
+
}
|
|
21
|
+
get activeIdentity() {
|
|
22
|
+
return this.active ? { provider: this.active.provider, model: this.active.model } : undefined;
|
|
23
|
+
}
|
|
24
|
+
start(identity) {
|
|
25
|
+
const generation = ++this.nextGeneration;
|
|
26
|
+
this.active = { ...identity, generation, observations: [] };
|
|
27
|
+
return generation;
|
|
28
|
+
}
|
|
29
|
+
reset() {
|
|
30
|
+
this.nextGeneration++;
|
|
31
|
+
this.active = undefined;
|
|
32
|
+
}
|
|
33
|
+
observe(generation, event) {
|
|
34
|
+
if (generation !== this.active?.generation || !isGeneratedDelta(event) || event.delta.length === 0)
|
|
35
|
+
return;
|
|
36
|
+
const time = this.monotonicNow();
|
|
37
|
+
if (!Number.isFinite(time))
|
|
38
|
+
return;
|
|
39
|
+
this.active.observations.push({ time, tokens: encoder.encode(event.delta).byteLength / BYTES_PER_TOKEN });
|
|
40
|
+
}
|
|
41
|
+
liveRate() {
|
|
42
|
+
if (!this.active)
|
|
43
|
+
return undefined;
|
|
44
|
+
const now = this.monotonicNow();
|
|
45
|
+
if (!Number.isFinite(now))
|
|
46
|
+
return undefined;
|
|
47
|
+
const cutoff = now - LIVE_WINDOW_MS;
|
|
48
|
+
const retained = this.active.observations.filter((observation) => observation.time >= cutoff);
|
|
49
|
+
const first = retained[0];
|
|
50
|
+
if (!first)
|
|
51
|
+
return undefined;
|
|
52
|
+
const elapsedMs = now - first.time;
|
|
53
|
+
if (!(elapsedMs > 0))
|
|
54
|
+
return undefined;
|
|
55
|
+
const tokens = retained.reduce((total, observation) => total + observation.tokens, 0);
|
|
56
|
+
const rate = (tokens * 1000) / elapsedMs;
|
|
57
|
+
return Number.isFinite(rate) && rate > 0 ? rate : undefined;
|
|
58
|
+
}
|
|
59
|
+
finalize(generation, message) {
|
|
60
|
+
if (generation !== this.active?.generation)
|
|
61
|
+
return undefined;
|
|
62
|
+
const active = this.active;
|
|
63
|
+
this.reset();
|
|
64
|
+
const first = active.observations[0];
|
|
65
|
+
const last = active.observations.at(-1);
|
|
66
|
+
const outputTokens = message.usage.output;
|
|
67
|
+
if (message.origin !== "provider" ||
|
|
68
|
+
message.provider !== active.provider ||
|
|
69
|
+
message.model !== active.model ||
|
|
70
|
+
!isSuccessfulStop(message.stopReason) ||
|
|
71
|
+
!Number.isFinite(outputTokens) ||
|
|
72
|
+
outputTokens <= 0 ||
|
|
73
|
+
active.observations.length < 2 ||
|
|
74
|
+
!first ||
|
|
75
|
+
!last) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
const durationMs = last.time - first.time;
|
|
79
|
+
if (!Number.isFinite(durationMs) || durationMs <= 0)
|
|
80
|
+
return undefined;
|
|
81
|
+
const observedAt = this.wallNow();
|
|
82
|
+
if (!Number.isFinite(observedAt))
|
|
83
|
+
return undefined;
|
|
84
|
+
return {
|
|
85
|
+
provider: active.provider,
|
|
86
|
+
model: active.model,
|
|
87
|
+
...(message.responseModel ? { responseModel: message.responseModel } : {}),
|
|
88
|
+
outputTokens,
|
|
89
|
+
durationMs,
|
|
90
|
+
observedAt,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export class OutputThroughputHistory {
|
|
95
|
+
samplesByModel = new Map();
|
|
96
|
+
add(sample) {
|
|
97
|
+
if (!isValidSample(sample))
|
|
98
|
+
return;
|
|
99
|
+
const key = modelKey(sample.provider, sample.model);
|
|
100
|
+
const samples = [...(this.samplesByModel.get(key) ?? []), { ...sample }]
|
|
101
|
+
.sort((left, right) => left.observedAt - right.observedAt)
|
|
102
|
+
.slice(-MAX_SAMPLES_PER_MODEL);
|
|
103
|
+
this.samplesByModel.set(key, samples);
|
|
104
|
+
}
|
|
105
|
+
samples(provider, model) {
|
|
106
|
+
return (this.samplesByModel.get(modelKey(provider, model)) ?? []).map((sample) => ({ ...sample }));
|
|
107
|
+
}
|
|
108
|
+
allSamples() {
|
|
109
|
+
return [...this.samplesByModel.values()].flat().map((sample) => ({ ...sample }));
|
|
110
|
+
}
|
|
111
|
+
latestSample(provider, model) {
|
|
112
|
+
return this.samples(provider, model).at(-1);
|
|
113
|
+
}
|
|
114
|
+
latestRate(provider, model) {
|
|
115
|
+
const sample = this.latestSample(provider, model);
|
|
116
|
+
if (!sample)
|
|
117
|
+
return undefined;
|
|
118
|
+
const rate = (sample.outputTokens * 1000) / sample.durationMs;
|
|
119
|
+
return Number.isFinite(rate) && rate > 0 ? rate : undefined;
|
|
120
|
+
}
|
|
121
|
+
median(provider, model) {
|
|
122
|
+
const rates = this.samples(provider, model)
|
|
123
|
+
.map((sample) => (sample.outputTokens * 1000) / sample.durationMs)
|
|
124
|
+
.filter((rate) => Number.isFinite(rate) && rate > 0)
|
|
125
|
+
.sort((left, right) => left - right);
|
|
126
|
+
if (rates.length === 0)
|
|
127
|
+
return undefined;
|
|
128
|
+
const middle = Math.floor(rates.length / 2);
|
|
129
|
+
return rates.length % 2 === 0 ? ((rates[middle - 1] ?? 0) + (rates[middle] ?? 0)) / 2 : rates[middle];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export function formatLiveOutputRate(rate) {
|
|
133
|
+
return formatCompletedOutputRate(rate);
|
|
134
|
+
}
|
|
135
|
+
export function formatCompletedOutputRate(rate) {
|
|
136
|
+
if (!Number.isFinite(rate) || rate <= 0)
|
|
137
|
+
return undefined;
|
|
138
|
+
return `${Math.round(rate)}tok/s`;
|
|
139
|
+
}
|
|
140
|
+
export function formatHistoricalOutputRate(rate) {
|
|
141
|
+
return formatCompletedOutputRate(rate);
|
|
142
|
+
}
|
|
143
|
+
function isGeneratedDelta(event) {
|
|
144
|
+
return event.type === "text_delta" || event.type === "thinking_delta" || event.type === "toolcall_delta";
|
|
145
|
+
}
|
|
146
|
+
function isSuccessfulStop(stopReason) {
|
|
147
|
+
return stopReason === "stop" || stopReason === "length" || stopReason === "toolUse";
|
|
148
|
+
}
|
|
149
|
+
function isValidSample(sample) {
|
|
150
|
+
return (sample.provider.length > 0 &&
|
|
151
|
+
sample.model.length > 0 &&
|
|
152
|
+
Number.isFinite(sample.outputTokens) &&
|
|
153
|
+
sample.outputTokens > 0 &&
|
|
154
|
+
Number.isFinite(sample.durationMs) &&
|
|
155
|
+
sample.durationMs > 0 &&
|
|
156
|
+
Number.isFinite(sample.observedAt));
|
|
157
|
+
}
|
|
158
|
+
function modelKey(provider, model) {
|
|
159
|
+
return `${provider.length}:${provider}${model}`;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=output-throughput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-throughput.js","sourceRoot":"","sources":["../../src/core/output-throughput.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage, AssistantMessageEvent } from \"@leanandmean/ai\";\n\n// SCRAMJET-DIVERGENCE: Measure and aggregate qualified provider output throughput by requested model (#476).\n\nconst LIVE_WINDOW_MS = 2000;\nconst BYTES_PER_TOKEN = 4;\nconst MAX_SAMPLES_PER_MODEL = 20;\nconst encoder = new TextEncoder();\n\nexport interface OutputThroughputIdentity {\n\tprovider: string;\n\tmodel: string;\n}\n\nexport interface OutputThroughputSample extends OutputThroughputIdentity {\n\tresponseModel?: string;\n\toutputTokens: number;\n\tdurationMs: number;\n\tobservedAt: number;\n}\n\nexport interface OutputThroughputTrackerOptions {\n\tmonotonicNow?: () => number;\n\twallNow?: () => number;\n}\n\ninterface Observation {\n\ttime: number;\n\ttokens: number;\n}\n\ninterface ActiveMeasurement extends OutputThroughputIdentity {\n\tgeneration: number;\n\tobservations: Observation[];\n}\n\nexport class OutputThroughputTracker {\n\tprivate readonly monotonicNow: () => number;\n\tprivate readonly wallNow: () => number;\n\tprivate active?: ActiveMeasurement;\n\tprivate nextGeneration = 0;\n\n\tconstructor(options: OutputThroughputTrackerOptions = {}) {\n\t\tthis.monotonicNow = options.monotonicNow ?? (() => performance.now());\n\t\tthis.wallNow = options.wallNow ?? (() => Date.now());\n\t}\n\n\tget generation(): number {\n\t\treturn this.nextGeneration;\n\t}\n\n\tget outputActive(): boolean {\n\t\treturn (this.active?.observations.length ?? 0) > 0;\n\t}\n\n\tget activeIdentity(): OutputThroughputIdentity | undefined {\n\t\treturn this.active ? { provider: this.active.provider, model: this.active.model } : undefined;\n\t}\n\n\tstart(identity: OutputThroughputIdentity): number {\n\t\tconst generation = ++this.nextGeneration;\n\t\tthis.active = { ...identity, generation, observations: [] };\n\t\treturn generation;\n\t}\n\n\treset(): void {\n\t\tthis.nextGeneration++;\n\t\tthis.active = undefined;\n\t}\n\n\tobserve(generation: number, event: AssistantMessageEvent): void {\n\t\tif (generation !== this.active?.generation || !isGeneratedDelta(event) || event.delta.length === 0) return;\n\t\tconst time = this.monotonicNow();\n\t\tif (!Number.isFinite(time)) return;\n\t\tthis.active.observations.push({ time, tokens: encoder.encode(event.delta).byteLength / BYTES_PER_TOKEN });\n\t}\n\n\tliveRate(): number | undefined {\n\t\tif (!this.active) return undefined;\n\t\tconst now = this.monotonicNow();\n\t\tif (!Number.isFinite(now)) return undefined;\n\t\tconst cutoff = now - LIVE_WINDOW_MS;\n\t\tconst retained = this.active.observations.filter((observation) => observation.time >= cutoff);\n\t\tconst first = retained[0];\n\t\tif (!first) return undefined;\n\t\tconst elapsedMs = now - first.time;\n\t\tif (!(elapsedMs > 0)) return undefined;\n\t\tconst tokens = retained.reduce((total, observation) => total + observation.tokens, 0);\n\t\tconst rate = (tokens * 1000) / elapsedMs;\n\t\treturn Number.isFinite(rate) && rate > 0 ? rate : undefined;\n\t}\n\n\tfinalize(generation: number, message: AssistantMessage): OutputThroughputSample | undefined {\n\t\tif (generation !== this.active?.generation) return undefined;\n\t\tconst active = this.active;\n\t\tthis.reset();\n\t\tconst first = active.observations[0];\n\t\tconst last = active.observations.at(-1);\n\t\tconst outputTokens = message.usage.output;\n\t\tif (\n\t\t\tmessage.origin !== \"provider\" ||\n\t\t\tmessage.provider !== active.provider ||\n\t\t\tmessage.model !== active.model ||\n\t\t\t!isSuccessfulStop(message.stopReason) ||\n\t\t\t!Number.isFinite(outputTokens) ||\n\t\t\toutputTokens <= 0 ||\n\t\t\tactive.observations.length < 2 ||\n\t\t\t!first ||\n\t\t\t!last\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst durationMs = last.time - first.time;\n\t\tif (!Number.isFinite(durationMs) || durationMs <= 0) return undefined;\n\t\tconst observedAt = this.wallNow();\n\t\tif (!Number.isFinite(observedAt)) return undefined;\n\t\treturn {\n\t\t\tprovider: active.provider,\n\t\t\tmodel: active.model,\n\t\t\t...(message.responseModel ? { responseModel: message.responseModel } : {}),\n\t\t\toutputTokens,\n\t\t\tdurationMs,\n\t\t\tobservedAt,\n\t\t};\n\t}\n}\n\nexport class OutputThroughputHistory {\n\tprivate readonly samplesByModel = new Map<string, OutputThroughputSample[]>();\n\n\tadd(sample: OutputThroughputSample): void {\n\t\tif (!isValidSample(sample)) return;\n\t\tconst key = modelKey(sample.provider, sample.model);\n\t\tconst samples = [...(this.samplesByModel.get(key) ?? []), { ...sample }]\n\t\t\t.sort((left, right) => left.observedAt - right.observedAt)\n\t\t\t.slice(-MAX_SAMPLES_PER_MODEL);\n\t\tthis.samplesByModel.set(key, samples);\n\t}\n\n\tsamples(provider: string, model: string): readonly OutputThroughputSample[] {\n\t\treturn (this.samplesByModel.get(modelKey(provider, model)) ?? []).map((sample) => ({ ...sample }));\n\t}\n\n\tallSamples(): readonly OutputThroughputSample[] {\n\t\treturn [...this.samplesByModel.values()].flat().map((sample) => ({ ...sample }));\n\t}\n\n\tlatestSample(provider: string, model: string): OutputThroughputSample | undefined {\n\t\treturn this.samples(provider, model).at(-1);\n\t}\n\n\tlatestRate(provider: string, model: string): number | undefined {\n\t\tconst sample = this.latestSample(provider, model);\n\t\tif (!sample) return undefined;\n\t\tconst rate = (sample.outputTokens * 1000) / sample.durationMs;\n\t\treturn Number.isFinite(rate) && rate > 0 ? rate : undefined;\n\t}\n\n\tmedian(provider: string, model: string): number | undefined {\n\t\tconst rates = this.samples(provider, model)\n\t\t\t.map((sample) => (sample.outputTokens * 1000) / sample.durationMs)\n\t\t\t.filter((rate) => Number.isFinite(rate) && rate > 0)\n\t\t\t.sort((left, right) => left - right);\n\t\tif (rates.length === 0) return undefined;\n\t\tconst middle = Math.floor(rates.length / 2);\n\t\treturn rates.length % 2 === 0 ? ((rates[middle - 1] ?? 0) + (rates[middle] ?? 0)) / 2 : rates[middle];\n\t}\n}\n\nexport function formatLiveOutputRate(rate: number): string | undefined {\n\treturn formatCompletedOutputRate(rate);\n}\n\nexport function formatCompletedOutputRate(rate: number): string | undefined {\n\tif (!Number.isFinite(rate) || rate <= 0) return undefined;\n\treturn `${Math.round(rate)}tok/s`;\n}\n\nexport function formatHistoricalOutputRate(rate: number): string | undefined {\n\treturn formatCompletedOutputRate(rate);\n}\n\nfunction isGeneratedDelta(\n\tevent: AssistantMessageEvent,\n): event is Extract<AssistantMessageEvent, { type: \"text_delta\" | \"thinking_delta\" | \"toolcall_delta\" }> {\n\treturn event.type === \"text_delta\" || event.type === \"thinking_delta\" || event.type === \"toolcall_delta\";\n}\n\nfunction isSuccessfulStop(stopReason: AssistantMessage[\"stopReason\"]): boolean {\n\treturn stopReason === \"stop\" || stopReason === \"length\" || stopReason === \"toolUse\";\n}\n\nfunction isValidSample(sample: OutputThroughputSample): boolean {\n\treturn (\n\t\tsample.provider.length > 0 &&\n\t\tsample.model.length > 0 &&\n\t\tNumber.isFinite(sample.outputTokens) &&\n\t\tsample.outputTokens > 0 &&\n\t\tNumber.isFinite(sample.durationMs) &&\n\t\tsample.durationMs > 0 &&\n\t\tNumber.isFinite(sample.observedAt)\n\t);\n}\n\nfunction modelKey(provider: string, model: string): string {\n\treturn `${provider.length}:${provider}${model}`;\n}\n"]}
|
package/dist/core/sdk.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export interface CreateAgentSessionOptions {
|
|
|
13
13
|
cwd?: string;
|
|
14
14
|
/** Global config directory. Default: ~/.scramjet/agent */
|
|
15
15
|
agentDir?: string;
|
|
16
|
+
/** Output-throughput history path resolved by runtime services. */
|
|
17
|
+
outputThroughputHistoryPath?: string;
|
|
16
18
|
/** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */
|
|
17
19
|
authStorage?: AuthStorage;
|
|
18
20
|
/** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */
|
package/dist/core/sdk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport { Agent, type AgentMessage, type ThinkingLevel } from \"@leanandmean/agent\";\nimport { type CacheRetention, clampThinkingLevel, type Message, type Model, streamSimple } from \"@leanandmean/ai\";\nimport { getAgentDir } from \"../config.js\";\nimport { AgentSession } from \"./agent-session.js\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.js\";\nimport { AuthStorage } from \"./auth-storage.js\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.js\";\nimport type { ExtensionRunner, LoadExtensionsResult, SessionStartEvent, ToolDefinition } from \"./extensions/index.js\";\nimport { convertToLlm } from \"./messages.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { findInitialModel } from \"./model-resolver.js\";\nimport type { ResourceLoader } from \"./resource-loader.js\";\nimport { DefaultResourceLoader } from \"./resource-loader.js\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.js\";\nimport { SettingsManager } from \"./settings-manager.js\";\nimport { isInstallTelemetryEnabled } from \"./telemetry.js\";\nimport { time } from \"./timings.js\";\nimport {\n\tcreateBashTool,\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateWriteTool,\n\ttype ToolName,\n\twithFileMutationQueue,\n} from \"./tools/index.js\";\n\nexport interface CreateAgentSessionOptions {\n\t/** Working directory for project-local discovery. Default: process.cwd() */\n\tcwd?: string;\n\t/** Global config directory. Default: ~/.scramjet/agent */\n\tagentDir?: string;\n\n\t/** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n\tauthStorage?: AuthStorage;\n\t/** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n\tmodelRegistry?: ModelRegistry;\n\n\t/** Model to use. Default: from settings, else first available */\n\tmodel?: Model<any>;\n\t/** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n\tthinkingLevel?: ThinkingLevel;\n\t/** Models available for cycling (Ctrl+P in interactive mode) */\n\tscopedModels?: Array<{ model: Model<any>; thinkingLevel?: ThinkingLevel }>;\n\t/**\n\t * Prompt-cache retention for provider requests.\n\t *\n\t * Precedence: this option, then the PI_CACHE_RETENTION environment variable,\n\t * then \"long\". Pass \"short\" for short-lived sessions such as subagents.\n\t */\n\tcacheRetention?: CacheRetention;\n\n\t/**\n\t * Optional default tool suppression mode when no explicit allowlist is provided.\n\t *\n\t * - \"all\": start with no tools enabled\n\t * - \"builtin\": disable the default built-in tools (read, bash, edit, write)\n\t * but keep extension/custom tools enabled\n\t */\n\tnoTools?: \"all\" | \"builtin\";\n\t/**\n\t * Optional allowlist of tool names.\n\t *\n\t * When omitted, pi enables the default built-in tools (read, bash, edit, write)\n\t * and leaves extension/custom tools enabled unless `noTools` changes that default.\n\t * When provided, only the listed tool names are enabled.\n\t */\n\ttools?: string[];\n\t/** Custom tools to register (in addition to built-in tools). */\n\tcustomTools?: ToolDefinition[];\n\n\t/** Resource loader. When omitted, DefaultResourceLoader is used. */\n\tresourceLoader?: ResourceLoader;\n\n\t/** Session manager. Default: SessionManager.create(cwd) */\n\tsessionManager?: SessionManager;\n\n\t/** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n\tsettingsManager?: SettingsManager;\n\t/** Session start event metadata for extension runtime startup. */\n\tsessionStartEvent?: SessionStartEvent;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n\t/** The created session */\n\tsession: AgentSession;\n\t/** Extensions result (for UI context setup in interactive mode) */\n\textensionsResult: LoadExtensionsResult;\n\t/** Warning if session was restored with a different model than saved */\n\tmodelFallbackMessage?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.js\";\nexport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tExtensionFactory,\n\tSlashCommandInfo,\n\tSlashCommandSource,\n\tToolDefinition,\n} from \"./extensions/index.js\";\nexport type { PromptTemplate } from \"./prompt-templates.js\";\nexport type { Skill } from \"./skills.js\";\nexport type { Tool } from \"./tools/index.js\";\n\nexport {\n\twithFileMutationQueue,\n\t// Tool factories (for custom cwd)\n\tcreateCodingTools,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateBashTool,\n\tcreateEditTool,\n\tcreateWriteTool,\n\tcreateGrepTool,\n\tcreateFindTool,\n\tcreateLsTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n\treturn getAgentDir();\n}\n\nfunction isCacheRetention(value: string): value is CacheRetention {\n\treturn value === \"none\" || value === \"short\" || value === \"long\";\n}\n\n// resolveCacheRetention runs on every createAgentSession call (including /new),\n// so warn about a bad env var only once per process to keep it out of the TUI.\n//\n// Defaults to \"long\" (1h tier). NOTE: the Anthropic adapter has a same-named\n// resolveCacheRetention (packages/ai/src/providers/anthropic.ts) that defaults\n// to \"short\" for backward compatibility. The divergence is intentional: this\n// SDK resolver runs first and passes a concrete value down, so the adapter's\n// \"short\" default is effectively shadowed in the coding-agent path. Keep both\n// in mind when changing either default.\nlet warnedInvalidCacheRetention = false;\n\nfunction resolveCacheRetention(option: CacheRetention | undefined): CacheRetention {\n\tif (option) {\n\t\treturn option;\n\t}\n\t// SCRAMJET-DIVERGENCE: prefer SCRAMJET_CACHE_RETENTION, fall back to PI_CACHE_RETENTION.\n\tconst env = process.env.SCRAMJET_CACHE_RETENTION || process.env.PI_CACHE_RETENTION;\n\tif (env) {\n\t\tif (isCacheRetention(env)) {\n\t\t\treturn env;\n\t\t}\n\t\tif (!warnedInvalidCacheRetention) {\n\t\t\twarnedInvalidCacheRetention = true;\n\t\t\tconsole.warn(`Invalid SCRAMJET_CACHE_RETENTION \"${env}\". Valid values: none, short, long. Using \"long\".`);\n\t\t}\n\t}\n\treturn \"long\";\n}\n\nfunction getAttributionHeaders(\n\tmodel: Model<any>,\n\tsettingsManager: SettingsManager,\n): Record<string, string> | undefined {\n\tif (!isInstallTelemetryEnabled(settingsManager)) {\n\t\treturn undefined;\n\t}\n\n\tif (model.provider === \"openrouter\" || model.baseUrl.includes(\"openrouter.ai\")) {\n\t\t// SCRAMJET-DIVERGENCE: Identify as Scramjet to OpenRouter\n\t\treturn {\n\t\t\t\"HTTP-Referer\": \"https://github.com/LeanAndMean/scramjet\",\n\t\t\t\"X-OpenRouter-Title\": \"scramjet\",\n\t\t\t\"X-OpenRouter-Categories\": \"cli-agent\",\n\t\t};\n\t}\n\n\tif (\n\t\tmodel.provider === \"cloudflare-workers-ai\" ||\n\t\tmodel.provider === \"cloudflare-ai-gateway\" ||\n\t\tmodel.baseUrl.includes(\"api.cloudflare.com\") ||\n\t\tmodel.baseUrl.includes(\"gateway.ai.cloudflare.com\")\n\t) {\n\t\t// SCRAMJET-DIVERGENCE: Identify as Scramjet to Cloudflare\n\t\treturn {\n\t\t\t\"User-Agent\": \"scramjet-coding-agent\",\n\t\t};\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@leanandmean/ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {\n\tconst cwd = options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd();\n\tconst agentDir = options.agentDir ?? getDefaultAgentDir();\n\tconst cacheRetention = resolveCacheRetention(options.cacheRetention);\n\tlet resourceLoader = options.resourceLoader;\n\n\t// Use provided or create AuthStorage and ModelRegistry\n\tconst authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n\tconst modelsPath = options.agentDir ? join(agentDir, \"models.json\") : undefined;\n\tconst authStorage = options.authStorage ?? AuthStorage.create(authPath);\n\tconst modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);\n\n\tconst settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n\tconst sessionManager = options.sessionManager ?? SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n\tif (!resourceLoader) {\n\t\tresourceLoader = new DefaultResourceLoader({ cwd, agentDir, settingsManager });\n\t\tawait resourceLoader.reload();\n\t\ttime(\"resourceLoader.reload\");\n\t}\n\n\t// Check if session has existing data to restore\n\tconst existingSession = sessionManager.buildSessionContext();\n\tconst hasExistingSession = existingSession.messages.length > 0;\n\tconst hasThinkingEntry = sessionManager.getBranch().some((entry) => entry.type === \"thinking_level_change\");\n\n\tlet model = options.model;\n\tlet modelFallbackMessage: string | undefined;\n\n\t// If session has data, try to restore model from it\n\tif (!model && hasExistingSession && existingSession.model) {\n\t\tconst restoredModel = modelRegistry.find(existingSession.model.provider, existingSession.model.modelId);\n\t\tif (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n\t\t\tmodel = restoredModel;\n\t\t}\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n\t\t}\n\t}\n\n\t// If still no model, use findInitialModel (checks settings default, then provider defaults)\n\tif (!model) {\n\t\tconst result = await findInitialModel({\n\t\t\tscopedModels: [],\n\t\t\tisContinuing: hasExistingSession,\n\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\tmodelRegistry,\n\t\t});\n\t\tmodel = result.model;\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = formatNoModelsAvailableMessage();\n\t\t} else if (modelFallbackMessage) {\n\t\t\tmodelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n\t\t}\n\t}\n\n\tlet thinkingLevel = options.thinkingLevel;\n\n\t// If session has data, restore thinking level from it\n\tif (thinkingLevel === undefined && hasExistingSession) {\n\t\tthinkingLevel = hasThinkingEntry\n\t\t\t? (existingSession.thinkingLevel as ThinkingLevel)\n\t\t\t: (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n\t}\n\n\t// Fall back to settings default\n\tif (thinkingLevel === undefined) {\n\t\tthinkingLevel = settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n\t}\n\n\t// Clamp to model capabilities\n\tif (!model) {\n\t\tthinkingLevel = \"off\";\n\t} else {\n\t\tthinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n\t}\n\n\tconst defaultActiveToolNames: ToolName[] = [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst allowedToolNames = options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n\tconst initialActiveToolNames: string[] = options.tools\n\t\t? [...options.tools]\n\t\t: options.noTools\n\t\t\t? []\n\t\t\t: defaultActiveToolNames;\n\n\tlet agent: Agent;\n\n\t// Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n\tconst convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n\t\tconst converted = convertToLlm(messages);\n\t\t// Check setting dynamically so mid-session changes take effect\n\t\tif (!settingsManager.getBlockImages()) {\n\t\t\treturn converted;\n\t\t}\n\t\t// Filter out ImageContent from all messages, replacing with text placeholder\n\t\treturn converted.map((msg) => {\n\t\t\tif (msg.role === \"user\" || msg.role === \"toolResult\") {\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content)) {\n\t\t\t\t\tconst hasImages = content.some((c) => c.type === \"image\");\n\t\t\t\t\tif (hasImages) {\n\t\t\t\t\t\tconst filteredContent = content\n\t\t\t\t\t\t\t.map((c) =>\n\t\t\t\t\t\t\t\tc.type === \"image\" ? { type: \"text\" as const, text: \"Image reading is disabled.\" } : c,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t\t(c, i, arr) =>\n\t\t\t\t\t\t\t\t\t// Dedupe consecutive \"Image reading is disabled.\" texts\n\t\t\t\t\t\t\t\t\t!(\n\t\t\t\t\t\t\t\t\t\tc.type === \"text\" &&\n\t\t\t\t\t\t\t\t\t\tc.text === \"Image reading is disabled.\" &&\n\t\t\t\t\t\t\t\t\t\ti > 0 &&\n\t\t\t\t\t\t\t\t\t\tarr[i - 1].type === \"text\" &&\n\t\t\t\t\t\t\t\t\t\t(arr[i - 1] as { type: \"text\"; text: string }).text === \"Image reading is disabled.\"\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn { ...msg, content: filteredContent };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn msg;\n\t\t});\n\t};\n\n\tconst extensionRunnerRef: { current?: ExtensionRunner } = {};\n\n\tagent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt: \"\",\n\t\t\tmodel,\n\t\t\tthinkingLevel,\n\t\t\ttools: [],\n\t\t},\n\t\tconvertToLlm: convertToLlmWithBlockImages,\n\t\tstreamFn: async (model, context, options) => {\n\t\t\tconst auth = await modelRegistry.getApiKeyAndHeaders(model);\n\t\t\tif (!auth.ok) {\n\t\t\t\tthrow new Error(auth.error);\n\t\t\t}\n\t\t\tconst providerRetrySettings = settingsManager.getProviderRetrySettings();\n\t\t\tconst attributionHeaders = getAttributionHeaders(model, settingsManager);\n\t\t\treturn streamSimple(model, context, {\n\t\t\t\t...options,\n\t\t\t\tapiKey: auth.apiKey,\n\t\t\t\tcacheRetention: options?.cacheRetention ?? cacheRetention,\n\t\t\t\ttimeoutMs: options?.timeoutMs ?? providerRetrySettings.timeoutMs,\n\t\t\t\tmaxRetries: options?.maxRetries ?? providerRetrySettings.maxRetries,\n\t\t\t\tmaxRetryDelayMs: options?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n\t\t\t\theaders:\n\t\t\t\t\tattributionHeaders || auth.headers || options?.headers\n\t\t\t\t\t\t? { ...attributionHeaders, ...auth.headers, ...options?.headers }\n\t\t\t\t\t\t: undefined,\n\t\t\t});\n\t\t},\n\t\tonPayload: async (payload, _model) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner?.hasHandlers(\"before_provider_request\")) {\n\t\t\t\treturn payload;\n\t\t\t}\n\t\t\treturn runner.emitBeforeProviderRequest(payload);\n\t\t},\n\t\tonResponse: async (response, _model) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner?.hasHandlers(\"after_provider_response\")) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait runner.emit({\n\t\t\t\ttype: \"after_provider_response\",\n\t\t\t\tstatus: response.status,\n\t\t\t\theaders: response.headers,\n\t\t\t});\n\t\t},\n\t\tsessionId: sessionManager.getSessionId(),\n\t\ttransformContext: async (messages) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner) return messages;\n\t\t\treturn runner.emitContext(messages);\n\t\t},\n\t\tsteeringMode: settingsManager.getSteeringMode(),\n\t\tfollowUpMode: settingsManager.getFollowUpMode(),\n\t\ttransport: settingsManager.getTransport(),\n\t\tthinkingBudgets: settingsManager.getThinkingBudgets(),\n\t\tmaxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n\t});\n\n\t// Restore messages if session has existing data\n\tif (hasExistingSession) {\n\t\tagent.state.messages = existingSession.messages;\n\t\tif (!hasThinkingEntry) {\n\t\t\tsessionManager.appendThinkingLevelChange(thinkingLevel);\n\t\t}\n\t} else {\n\t\t// Save initial model and thinking level for new sessions so they can be restored on resume\n\t\tif (model) {\n\t\t\tsessionManager.appendModelChange(model.provider, model.id);\n\t\t}\n\t\tsessionManager.appendThinkingLevelChange(thinkingLevel);\n\t}\n\n\tconst session = new AgentSession({\n\t\tagent,\n\t\tsessionManager,\n\t\tsettingsManager,\n\t\tcwd,\n\t\tscopedModels: options.scopedModels,\n\t\tresourceLoader,\n\t\tcustomTools: options.customTools,\n\t\tmodelRegistry,\n\t\tinitialActiveToolNames,\n\t\tallowedToolNames,\n\t\textensionRunnerRef,\n\t\tsessionStartEvent: options.sessionStartEvent,\n\t});\n\tconst extensionsResult = resourceLoader.getExtensions();\n\n\treturn {\n\t\tsession,\n\t\textensionsResult,\n\t\tmodelFallbackMessage,\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,KAAK,cAAc,EAAoC,KAAK,KAAK,EAAgB,MAAM,iBAAiB,CAAC;AAElH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAmB,oBAAoB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEtH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAAwB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,EACN,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,eAAe,EAEf,qBAAqB,EACrB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,yBAAyB;IACzC,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,2BAA2B,CAAC,EAAE,MAAM,CAAC;IAErC,oFAAoF;IACpF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,uFAAuF;IACvF,aAAa,CAAC,EAAE,aAAa,CAAC;IAE9B,iEAAiE;IACjE,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,4FAA4F;IAC5F,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,gEAAgE;IAChE,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QAAC,aAAa,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;IAC3E;;;;;OAKG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,gEAAgE;IAChE,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;IAE/B,oEAAoE;IACpE,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC,2DAA2D;IAC3D,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC,uEAAuE;IACvE,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,kEAAkE;IAClE,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACtC;AAED,qCAAqC;AACrC,MAAM,WAAW,wBAAwB;IACxC,0BAA0B;IAC1B,OAAO,EAAE,YAAY,CAAC;IACtB,mEAAmE;IACnE,gBAAgB,EAAE,oBAAoB,CAAC;IACvC,wEAAwE;IACxE,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAID,cAAc,4BAA4B,CAAC;AAC3C,YAAY,EACX,YAAY,EACZ,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,GACd,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EACN,qBAAqB,EAErB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,GACZ,CAAC;AAyEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAuOnH","sourcesContent":["import { join } from \"node:path\";\nimport { Agent, type AgentMessage, type ThinkingLevel } from \"@leanandmean/agent\";\nimport { type CacheRetention, clampThinkingLevel, type Message, type Model, streamSimple } from \"@leanandmean/ai\";\nimport { getAgentDir } from \"../config.js\";\nimport { AgentSession } from \"./agent-session.js\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.js\";\nimport { AuthStorage } from \"./auth-storage.js\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.js\";\nimport type { ExtensionRunner, LoadExtensionsResult, SessionStartEvent, ToolDefinition } from \"./extensions/index.js\";\nimport { convertToLlm } from \"./messages.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { findInitialModel } from \"./model-resolver.js\";\nimport type { ResourceLoader } from \"./resource-loader.js\";\nimport { DefaultResourceLoader } from \"./resource-loader.js\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.js\";\nimport { SettingsManager } from \"./settings-manager.js\";\nimport { isInstallTelemetryEnabled } from \"./telemetry.js\";\nimport { time } from \"./timings.js\";\nimport {\n\tcreateBashTool,\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateWriteTool,\n\ttype ToolName,\n\twithFileMutationQueue,\n} from \"./tools/index.js\";\n\nexport interface CreateAgentSessionOptions {\n\t/** Working directory for project-local discovery. Default: process.cwd() */\n\tcwd?: string;\n\t/** Global config directory. Default: ~/.scramjet/agent */\n\tagentDir?: string;\n\t/** Output-throughput history path resolved by runtime services. */\n\toutputThroughputHistoryPath?: string;\n\n\t/** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n\tauthStorage?: AuthStorage;\n\t/** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n\tmodelRegistry?: ModelRegistry;\n\n\t/** Model to use. Default: from settings, else first available */\n\tmodel?: Model<any>;\n\t/** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n\tthinkingLevel?: ThinkingLevel;\n\t/** Models available for cycling (Ctrl+P in interactive mode) */\n\tscopedModels?: Array<{ model: Model<any>; thinkingLevel?: ThinkingLevel }>;\n\t/**\n\t * Prompt-cache retention for provider requests.\n\t *\n\t * Precedence: this option, then the PI_CACHE_RETENTION environment variable,\n\t * then \"long\". Pass \"short\" for short-lived sessions such as subagents.\n\t */\n\tcacheRetention?: CacheRetention;\n\n\t/**\n\t * Optional default tool suppression mode when no explicit allowlist is provided.\n\t *\n\t * - \"all\": start with no tools enabled\n\t * - \"builtin\": disable the default built-in tools (read, bash, edit, write)\n\t * but keep extension/custom tools enabled\n\t */\n\tnoTools?: \"all\" | \"builtin\";\n\t/**\n\t * Optional allowlist of tool names.\n\t *\n\t * When omitted, pi enables the default built-in tools (read, bash, edit, write)\n\t * and leaves extension/custom tools enabled unless `noTools` changes that default.\n\t * When provided, only the listed tool names are enabled.\n\t */\n\ttools?: string[];\n\t/** Custom tools to register (in addition to built-in tools). */\n\tcustomTools?: ToolDefinition[];\n\n\t/** Resource loader. When omitted, DefaultResourceLoader is used. */\n\tresourceLoader?: ResourceLoader;\n\n\t/** Session manager. Default: SessionManager.create(cwd) */\n\tsessionManager?: SessionManager;\n\n\t/** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n\tsettingsManager?: SettingsManager;\n\t/** Session start event metadata for extension runtime startup. */\n\tsessionStartEvent?: SessionStartEvent;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n\t/** The created session */\n\tsession: AgentSession;\n\t/** Extensions result (for UI context setup in interactive mode) */\n\textensionsResult: LoadExtensionsResult;\n\t/** Warning if session was restored with a different model than saved */\n\tmodelFallbackMessage?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.js\";\nexport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tExtensionFactory,\n\tSlashCommandInfo,\n\tSlashCommandSource,\n\tToolDefinition,\n} from \"./extensions/index.js\";\nexport type { PromptTemplate } from \"./prompt-templates.js\";\nexport type { Skill } from \"./skills.js\";\nexport type { Tool } from \"./tools/index.js\";\n\nexport {\n\twithFileMutationQueue,\n\t// Tool factories (for custom cwd)\n\tcreateCodingTools,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateBashTool,\n\tcreateEditTool,\n\tcreateWriteTool,\n\tcreateGrepTool,\n\tcreateFindTool,\n\tcreateLsTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n\treturn getAgentDir();\n}\n\nfunction isCacheRetention(value: string): value is CacheRetention {\n\treturn value === \"none\" || value === \"short\" || value === \"long\";\n}\n\n// resolveCacheRetention runs on every createAgentSession call (including /new),\n// so warn about a bad env var only once per process to keep it out of the TUI.\n//\n// Defaults to \"long\" (1h tier). NOTE: the Anthropic adapter has a same-named\n// resolveCacheRetention (packages/ai/src/providers/anthropic.ts) that defaults\n// to \"short\" for backward compatibility. The divergence is intentional: this\n// SDK resolver runs first and passes a concrete value down, so the adapter's\n// \"short\" default is effectively shadowed in the coding-agent path. Keep both\n// in mind when changing either default.\nlet warnedInvalidCacheRetention = false;\n\nfunction resolveCacheRetention(option: CacheRetention | undefined): CacheRetention {\n\tif (option) {\n\t\treturn option;\n\t}\n\t// SCRAMJET-DIVERGENCE: prefer SCRAMJET_CACHE_RETENTION, fall back to PI_CACHE_RETENTION.\n\tconst env = process.env.SCRAMJET_CACHE_RETENTION || process.env.PI_CACHE_RETENTION;\n\tif (env) {\n\t\tif (isCacheRetention(env)) {\n\t\t\treturn env;\n\t\t}\n\t\tif (!warnedInvalidCacheRetention) {\n\t\t\twarnedInvalidCacheRetention = true;\n\t\t\tconsole.warn(`Invalid SCRAMJET_CACHE_RETENTION \"${env}\". Valid values: none, short, long. Using \"long\".`);\n\t\t}\n\t}\n\treturn \"long\";\n}\n\nfunction getAttributionHeaders(\n\tmodel: Model<any>,\n\tsettingsManager: SettingsManager,\n): Record<string, string> | undefined {\n\tif (!isInstallTelemetryEnabled(settingsManager)) {\n\t\treturn undefined;\n\t}\n\n\tif (model.provider === \"openrouter\" || model.baseUrl.includes(\"openrouter.ai\")) {\n\t\t// SCRAMJET-DIVERGENCE: Identify as Scramjet to OpenRouter\n\t\treturn {\n\t\t\t\"HTTP-Referer\": \"https://github.com/LeanAndMean/scramjet\",\n\t\t\t\"X-OpenRouter-Title\": \"scramjet\",\n\t\t\t\"X-OpenRouter-Categories\": \"cli-agent\",\n\t\t};\n\t}\n\n\tif (\n\t\tmodel.provider === \"cloudflare-workers-ai\" ||\n\t\tmodel.provider === \"cloudflare-ai-gateway\" ||\n\t\tmodel.baseUrl.includes(\"api.cloudflare.com\") ||\n\t\tmodel.baseUrl.includes(\"gateway.ai.cloudflare.com\")\n\t) {\n\t\t// SCRAMJET-DIVERGENCE: Identify as Scramjet to Cloudflare\n\t\treturn {\n\t\t\t\"User-Agent\": \"scramjet-coding-agent\",\n\t\t};\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@leanandmean/ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {\n\tconst cwd = options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd();\n\tconst agentDir = options.agentDir ?? getDefaultAgentDir();\n\tconst outputThroughputHistoryPath =\n\t\toptions.outputThroughputHistoryPath ?? join(agentDir, \"output-throughput-history.json\");\n\tconst cacheRetention = resolveCacheRetention(options.cacheRetention);\n\tlet resourceLoader = options.resourceLoader;\n\n\t// Use provided or create AuthStorage and ModelRegistry\n\tconst authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n\tconst modelsPath = options.agentDir ? join(agentDir, \"models.json\") : undefined;\n\tconst authStorage = options.authStorage ?? AuthStorage.create(authPath);\n\tconst modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);\n\n\tconst settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n\tconst sessionManager = options.sessionManager ?? SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n\tif (!resourceLoader) {\n\t\tresourceLoader = new DefaultResourceLoader({ cwd, agentDir, settingsManager });\n\t\tawait resourceLoader.reload();\n\t\ttime(\"resourceLoader.reload\");\n\t}\n\n\t// Check if session has existing data to restore\n\tconst existingSession = sessionManager.buildSessionContext();\n\tconst hasExistingSession = existingSession.messages.length > 0;\n\tconst hasThinkingEntry = sessionManager.getBranch().some((entry) => entry.type === \"thinking_level_change\");\n\n\tlet model = options.model;\n\tlet modelFallbackMessage: string | undefined;\n\n\t// If session has data, try to restore model from it\n\tif (!model && hasExistingSession && existingSession.model) {\n\t\tconst restoredModel = modelRegistry.find(existingSession.model.provider, existingSession.model.modelId);\n\t\tif (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n\t\t\tmodel = restoredModel;\n\t\t}\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n\t\t}\n\t}\n\n\t// If still no model, use findInitialModel (checks settings default, then provider defaults)\n\tif (!model) {\n\t\tconst result = await findInitialModel({\n\t\t\tscopedModels: [],\n\t\t\tisContinuing: hasExistingSession,\n\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\tmodelRegistry,\n\t\t});\n\t\tmodel = result.model;\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = formatNoModelsAvailableMessage();\n\t\t} else if (modelFallbackMessage) {\n\t\t\tmodelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n\t\t}\n\t}\n\n\tlet thinkingLevel = options.thinkingLevel;\n\n\t// If session has data, restore thinking level from it\n\tif (thinkingLevel === undefined && hasExistingSession) {\n\t\tthinkingLevel = hasThinkingEntry\n\t\t\t? (existingSession.thinkingLevel as ThinkingLevel)\n\t\t\t: (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n\t}\n\n\t// Fall back to settings default\n\tif (thinkingLevel === undefined) {\n\t\tthinkingLevel = settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n\t}\n\n\t// Clamp to model capabilities\n\tif (!model) {\n\t\tthinkingLevel = \"off\";\n\t} else {\n\t\tthinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n\t}\n\n\tconst defaultActiveToolNames: ToolName[] = [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst allowedToolNames = options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n\tconst initialActiveToolNames: string[] = options.tools\n\t\t? [...options.tools]\n\t\t: options.noTools\n\t\t\t? []\n\t\t\t: defaultActiveToolNames;\n\n\tlet agent: Agent;\n\n\t// Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n\tconst convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n\t\tconst converted = convertToLlm(messages);\n\t\t// Check setting dynamically so mid-session changes take effect\n\t\tif (!settingsManager.getBlockImages()) {\n\t\t\treturn converted;\n\t\t}\n\t\t// Filter out ImageContent from all messages, replacing with text placeholder\n\t\treturn converted.map((msg) => {\n\t\t\tif (msg.role === \"user\" || msg.role === \"toolResult\") {\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content)) {\n\t\t\t\t\tconst hasImages = content.some((c) => c.type === \"image\");\n\t\t\t\t\tif (hasImages) {\n\t\t\t\t\t\tconst filteredContent = content\n\t\t\t\t\t\t\t.map((c) =>\n\t\t\t\t\t\t\t\tc.type === \"image\" ? { type: \"text\" as const, text: \"Image reading is disabled.\" } : c,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t\t(c, i, arr) =>\n\t\t\t\t\t\t\t\t\t// Dedupe consecutive \"Image reading is disabled.\" texts\n\t\t\t\t\t\t\t\t\t!(\n\t\t\t\t\t\t\t\t\t\tc.type === \"text\" &&\n\t\t\t\t\t\t\t\t\t\tc.text === \"Image reading is disabled.\" &&\n\t\t\t\t\t\t\t\t\t\ti > 0 &&\n\t\t\t\t\t\t\t\t\t\tarr[i - 1].type === \"text\" &&\n\t\t\t\t\t\t\t\t\t\t(arr[i - 1] as { type: \"text\"; text: string }).text === \"Image reading is disabled.\"\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn { ...msg, content: filteredContent };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn msg;\n\t\t});\n\t};\n\n\tconst extensionRunnerRef: { current?: ExtensionRunner } = {};\n\n\tagent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt: \"\",\n\t\t\tmodel,\n\t\t\tthinkingLevel,\n\t\t\ttools: [],\n\t\t},\n\t\tconvertToLlm: convertToLlmWithBlockImages,\n\t\tstreamFn: async (model, context, options) => {\n\t\t\tconst auth = await modelRegistry.getApiKeyAndHeaders(model);\n\t\t\tif (!auth.ok) {\n\t\t\t\tthrow new Error(auth.error);\n\t\t\t}\n\t\t\tconst providerRetrySettings = settingsManager.getProviderRetrySettings();\n\t\t\tconst attributionHeaders = getAttributionHeaders(model, settingsManager);\n\t\t\treturn streamSimple(model, context, {\n\t\t\t\t...options,\n\t\t\t\tapiKey: auth.apiKey,\n\t\t\t\tcacheRetention: options?.cacheRetention ?? cacheRetention,\n\t\t\t\ttimeoutMs: options?.timeoutMs ?? providerRetrySettings.timeoutMs,\n\t\t\t\tmaxRetries: options?.maxRetries ?? providerRetrySettings.maxRetries,\n\t\t\t\tmaxRetryDelayMs: options?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n\t\t\t\theaders:\n\t\t\t\t\tattributionHeaders || auth.headers || options?.headers\n\t\t\t\t\t\t? { ...attributionHeaders, ...auth.headers, ...options?.headers }\n\t\t\t\t\t\t: undefined,\n\t\t\t});\n\t\t},\n\t\tbeforeProviderCall: async (context, model) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner?.hasHandlers(\"before_provider_call\")) return context;\n\t\t\treturn runner.emitBeforeProviderCall(context, model);\n\t\t},\n\t\tonPayload: async (payload, model) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner?.hasHandlers(\"before_provider_request\")) {\n\t\t\t\treturn payload;\n\t\t\t}\n\t\t\treturn runner.emitBeforeProviderRequest(payload, model);\n\t\t},\n\t\tonResponse: async (response, _model) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner?.hasHandlers(\"after_provider_response\")) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait runner.emit({\n\t\t\t\ttype: \"after_provider_response\",\n\t\t\t\tstatus: response.status,\n\t\t\t\theaders: response.headers,\n\t\t\t});\n\t\t},\n\t\tsessionId: sessionManager.getSessionId(),\n\t\ttransformContext: async (messages) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner) return messages;\n\t\t\treturn runner.emitContext(messages);\n\t\t},\n\t\tsteeringMode: settingsManager.getSteeringMode(),\n\t\tfollowUpMode: settingsManager.getFollowUpMode(),\n\t\ttransport: settingsManager.getTransport(),\n\t\tthinkingBudgets: settingsManager.getThinkingBudgets(),\n\t\tmaxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n\t});\n\n\t// Restore messages if session has existing data\n\tif (hasExistingSession) {\n\t\tagent.state.messages = existingSession.messages;\n\t\tif (!hasThinkingEntry) {\n\t\t\tsessionManager.appendThinkingLevelChange(thinkingLevel);\n\t\t}\n\t} else {\n\t\t// Save initial model and thinking level for new sessions so they can be restored on resume\n\t\tif (model) {\n\t\t\tsessionManager.appendModelChange(model.provider, model.id);\n\t\t}\n\t\tsessionManager.appendThinkingLevelChange(thinkingLevel);\n\t}\n\n\tconst session = new AgentSession({\n\t\tagent,\n\t\tsessionManager,\n\t\tsettingsManager,\n\t\tcwd,\n\t\toutputThroughputHistoryPath,\n\t\tscopedModels: options.scopedModels,\n\t\tresourceLoader,\n\t\tcustomTools: options.customTools,\n\t\tmodelRegistry,\n\t\tinitialActiveToolNames,\n\t\tallowedToolNames,\n\t\textensionRunnerRef,\n\t\tsessionStartEvent: options.sessionStartEvent,\n\t});\n\tawait session.refreshOutputThroughputHistory();\n\tconst extensionsResult = resourceLoader.getExtensions();\n\n\treturn {\n\t\tsession,\n\t\textensionsResult,\n\t\tmodelFallbackMessage,\n\t};\n}\n"]}
|
package/dist/core/sdk.js
CHANGED
|
@@ -115,6 +115,7 @@ function getAttributionHeaders(model, settingsManager) {
|
|
|
115
115
|
export async function createAgentSession(options = {}) {
|
|
116
116
|
const cwd = options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd();
|
|
117
117
|
const agentDir = options.agentDir ?? getDefaultAgentDir();
|
|
118
|
+
const outputThroughputHistoryPath = options.outputThroughputHistoryPath ?? join(agentDir, "output-throughput-history.json");
|
|
118
119
|
const cacheRetention = resolveCacheRetention(options.cacheRetention);
|
|
119
120
|
let resourceLoader = options.resourceLoader;
|
|
120
121
|
// Use provided or create AuthStorage and ModelRegistry
|
|
@@ -247,12 +248,18 @@ export async function createAgentSession(options = {}) {
|
|
|
247
248
|
: undefined,
|
|
248
249
|
});
|
|
249
250
|
},
|
|
250
|
-
|
|
251
|
+
beforeProviderCall: async (context, model) => {
|
|
252
|
+
const runner = extensionRunnerRef.current;
|
|
253
|
+
if (!runner?.hasHandlers("before_provider_call"))
|
|
254
|
+
return context;
|
|
255
|
+
return runner.emitBeforeProviderCall(context, model);
|
|
256
|
+
},
|
|
257
|
+
onPayload: async (payload, model) => {
|
|
251
258
|
const runner = extensionRunnerRef.current;
|
|
252
259
|
if (!runner?.hasHandlers("before_provider_request")) {
|
|
253
260
|
return payload;
|
|
254
261
|
}
|
|
255
|
-
return runner.emitBeforeProviderRequest(payload);
|
|
262
|
+
return runner.emitBeforeProviderRequest(payload, model);
|
|
256
263
|
},
|
|
257
264
|
onResponse: async (response, _model) => {
|
|
258
265
|
const runner = extensionRunnerRef.current;
|
|
@@ -297,6 +304,7 @@ export async function createAgentSession(options = {}) {
|
|
|
297
304
|
sessionManager,
|
|
298
305
|
settingsManager,
|
|
299
306
|
cwd,
|
|
307
|
+
outputThroughputHistoryPath,
|
|
300
308
|
scopedModels: options.scopedModels,
|
|
301
309
|
resourceLoader,
|
|
302
310
|
customTools: options.customTools,
|
|
@@ -306,6 +314,7 @@ export async function createAgentSession(options = {}) {
|
|
|
306
314
|
extensionRunnerRef,
|
|
307
315
|
sessionStartEvent: options.sessionStartEvent,
|
|
308
316
|
});
|
|
317
|
+
await session.refreshOutputThroughputHistory();
|
|
309
318
|
const extensionsResult = resourceLoader.getExtensions();
|
|
310
319
|
return {
|
|
311
320
|
session,
|
package/dist/core/sdk.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.js","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport { Agent, type AgentMessage, type ThinkingLevel } from \"@leanandmean/agent\";\nimport { type CacheRetention, clampThinkingLevel, type Message, type Model, streamSimple } from \"@leanandmean/ai\";\nimport { getAgentDir } from \"../config.js\";\nimport { AgentSession } from \"./agent-session.js\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.js\";\nimport { AuthStorage } from \"./auth-storage.js\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.js\";\nimport type { ExtensionRunner, LoadExtensionsResult, SessionStartEvent, ToolDefinition } from \"./extensions/index.js\";\nimport { convertToLlm } from \"./messages.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { findInitialModel } from \"./model-resolver.js\";\nimport type { ResourceLoader } from \"./resource-loader.js\";\nimport { DefaultResourceLoader } from \"./resource-loader.js\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.js\";\nimport { SettingsManager } from \"./settings-manager.js\";\nimport { isInstallTelemetryEnabled } from \"./telemetry.js\";\nimport { time } from \"./timings.js\";\nimport {\n\tcreateBashTool,\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateWriteTool,\n\ttype ToolName,\n\twithFileMutationQueue,\n} from \"./tools/index.js\";\n\nexport interface CreateAgentSessionOptions {\n\t/** Working directory for project-local discovery. Default: process.cwd() */\n\tcwd?: string;\n\t/** Global config directory. Default: ~/.scramjet/agent */\n\tagentDir?: string;\n\n\t/** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n\tauthStorage?: AuthStorage;\n\t/** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n\tmodelRegistry?: ModelRegistry;\n\n\t/** Model to use. Default: from settings, else first available */\n\tmodel?: Model<any>;\n\t/** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n\tthinkingLevel?: ThinkingLevel;\n\t/** Models available for cycling (Ctrl+P in interactive mode) */\n\tscopedModels?: Array<{ model: Model<any>; thinkingLevel?: ThinkingLevel }>;\n\t/**\n\t * Prompt-cache retention for provider requests.\n\t *\n\t * Precedence: this option, then the PI_CACHE_RETENTION environment variable,\n\t * then \"long\". Pass \"short\" for short-lived sessions such as subagents.\n\t */\n\tcacheRetention?: CacheRetention;\n\n\t/**\n\t * Optional default tool suppression mode when no explicit allowlist is provided.\n\t *\n\t * - \"all\": start with no tools enabled\n\t * - \"builtin\": disable the default built-in tools (read, bash, edit, write)\n\t * but keep extension/custom tools enabled\n\t */\n\tnoTools?: \"all\" | \"builtin\";\n\t/**\n\t * Optional allowlist of tool names.\n\t *\n\t * When omitted, pi enables the default built-in tools (read, bash, edit, write)\n\t * and leaves extension/custom tools enabled unless `noTools` changes that default.\n\t * When provided, only the listed tool names are enabled.\n\t */\n\ttools?: string[];\n\t/** Custom tools to register (in addition to built-in tools). */\n\tcustomTools?: ToolDefinition[];\n\n\t/** Resource loader. When omitted, DefaultResourceLoader is used. */\n\tresourceLoader?: ResourceLoader;\n\n\t/** Session manager. Default: SessionManager.create(cwd) */\n\tsessionManager?: SessionManager;\n\n\t/** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n\tsettingsManager?: SettingsManager;\n\t/** Session start event metadata for extension runtime startup. */\n\tsessionStartEvent?: SessionStartEvent;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n\t/** The created session */\n\tsession: AgentSession;\n\t/** Extensions result (for UI context setup in interactive mode) */\n\textensionsResult: LoadExtensionsResult;\n\t/** Warning if session was restored with a different model than saved */\n\tmodelFallbackMessage?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.js\";\nexport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tExtensionFactory,\n\tSlashCommandInfo,\n\tSlashCommandSource,\n\tToolDefinition,\n} from \"./extensions/index.js\";\nexport type { PromptTemplate } from \"./prompt-templates.js\";\nexport type { Skill } from \"./skills.js\";\nexport type { Tool } from \"./tools/index.js\";\n\nexport {\n\twithFileMutationQueue,\n\t// Tool factories (for custom cwd)\n\tcreateCodingTools,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateBashTool,\n\tcreateEditTool,\n\tcreateWriteTool,\n\tcreateGrepTool,\n\tcreateFindTool,\n\tcreateLsTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n\treturn getAgentDir();\n}\n\nfunction isCacheRetention(value: string): value is CacheRetention {\n\treturn value === \"none\" || value === \"short\" || value === \"long\";\n}\n\n// resolveCacheRetention runs on every createAgentSession call (including /new),\n// so warn about a bad env var only once per process to keep it out of the TUI.\n//\n// Defaults to \"long\" (1h tier). NOTE: the Anthropic adapter has a same-named\n// resolveCacheRetention (packages/ai/src/providers/anthropic.ts) that defaults\n// to \"short\" for backward compatibility. The divergence is intentional: this\n// SDK resolver runs first and passes a concrete value down, so the adapter's\n// \"short\" default is effectively shadowed in the coding-agent path. Keep both\n// in mind when changing either default.\nlet warnedInvalidCacheRetention = false;\n\nfunction resolveCacheRetention(option: CacheRetention | undefined): CacheRetention {\n\tif (option) {\n\t\treturn option;\n\t}\n\t// SCRAMJET-DIVERGENCE: prefer SCRAMJET_CACHE_RETENTION, fall back to PI_CACHE_RETENTION.\n\tconst env = process.env.SCRAMJET_CACHE_RETENTION || process.env.PI_CACHE_RETENTION;\n\tif (env) {\n\t\tif (isCacheRetention(env)) {\n\t\t\treturn env;\n\t\t}\n\t\tif (!warnedInvalidCacheRetention) {\n\t\t\twarnedInvalidCacheRetention = true;\n\t\t\tconsole.warn(`Invalid SCRAMJET_CACHE_RETENTION \"${env}\". Valid values: none, short, long. Using \"long\".`);\n\t\t}\n\t}\n\treturn \"long\";\n}\n\nfunction getAttributionHeaders(\n\tmodel: Model<any>,\n\tsettingsManager: SettingsManager,\n): Record<string, string> | undefined {\n\tif (!isInstallTelemetryEnabled(settingsManager)) {\n\t\treturn undefined;\n\t}\n\n\tif (model.provider === \"openrouter\" || model.baseUrl.includes(\"openrouter.ai\")) {\n\t\t// SCRAMJET-DIVERGENCE: Identify as Scramjet to OpenRouter\n\t\treturn {\n\t\t\t\"HTTP-Referer\": \"https://github.com/LeanAndMean/scramjet\",\n\t\t\t\"X-OpenRouter-Title\": \"scramjet\",\n\t\t\t\"X-OpenRouter-Categories\": \"cli-agent\",\n\t\t};\n\t}\n\n\tif (\n\t\tmodel.provider === \"cloudflare-workers-ai\" ||\n\t\tmodel.provider === \"cloudflare-ai-gateway\" ||\n\t\tmodel.baseUrl.includes(\"api.cloudflare.com\") ||\n\t\tmodel.baseUrl.includes(\"gateway.ai.cloudflare.com\")\n\t) {\n\t\t// SCRAMJET-DIVERGENCE: Identify as Scramjet to Cloudflare\n\t\treturn {\n\t\t\t\"User-Agent\": \"scramjet-coding-agent\",\n\t\t};\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@leanandmean/ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {\n\tconst cwd = options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd();\n\tconst agentDir = options.agentDir ?? getDefaultAgentDir();\n\tconst cacheRetention = resolveCacheRetention(options.cacheRetention);\n\tlet resourceLoader = options.resourceLoader;\n\n\t// Use provided or create AuthStorage and ModelRegistry\n\tconst authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n\tconst modelsPath = options.agentDir ? join(agentDir, \"models.json\") : undefined;\n\tconst authStorage = options.authStorage ?? AuthStorage.create(authPath);\n\tconst modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);\n\n\tconst settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n\tconst sessionManager = options.sessionManager ?? SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n\tif (!resourceLoader) {\n\t\tresourceLoader = new DefaultResourceLoader({ cwd, agentDir, settingsManager });\n\t\tawait resourceLoader.reload();\n\t\ttime(\"resourceLoader.reload\");\n\t}\n\n\t// Check if session has existing data to restore\n\tconst existingSession = sessionManager.buildSessionContext();\n\tconst hasExistingSession = existingSession.messages.length > 0;\n\tconst hasThinkingEntry = sessionManager.getBranch().some((entry) => entry.type === \"thinking_level_change\");\n\n\tlet model = options.model;\n\tlet modelFallbackMessage: string | undefined;\n\n\t// If session has data, try to restore model from it\n\tif (!model && hasExistingSession && existingSession.model) {\n\t\tconst restoredModel = modelRegistry.find(existingSession.model.provider, existingSession.model.modelId);\n\t\tif (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n\t\t\tmodel = restoredModel;\n\t\t}\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n\t\t}\n\t}\n\n\t// If still no model, use findInitialModel (checks settings default, then provider defaults)\n\tif (!model) {\n\t\tconst result = await findInitialModel({\n\t\t\tscopedModels: [],\n\t\t\tisContinuing: hasExistingSession,\n\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\tmodelRegistry,\n\t\t});\n\t\tmodel = result.model;\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = formatNoModelsAvailableMessage();\n\t\t} else if (modelFallbackMessage) {\n\t\t\tmodelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n\t\t}\n\t}\n\n\tlet thinkingLevel = options.thinkingLevel;\n\n\t// If session has data, restore thinking level from it\n\tif (thinkingLevel === undefined && hasExistingSession) {\n\t\tthinkingLevel = hasThinkingEntry\n\t\t\t? (existingSession.thinkingLevel as ThinkingLevel)\n\t\t\t: (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n\t}\n\n\t// Fall back to settings default\n\tif (thinkingLevel === undefined) {\n\t\tthinkingLevel = settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n\t}\n\n\t// Clamp to model capabilities\n\tif (!model) {\n\t\tthinkingLevel = \"off\";\n\t} else {\n\t\tthinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n\t}\n\n\tconst defaultActiveToolNames: ToolName[] = [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst allowedToolNames = options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n\tconst initialActiveToolNames: string[] = options.tools\n\t\t? [...options.tools]\n\t\t: options.noTools\n\t\t\t? []\n\t\t\t: defaultActiveToolNames;\n\n\tlet agent: Agent;\n\n\t// Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n\tconst convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n\t\tconst converted = convertToLlm(messages);\n\t\t// Check setting dynamically so mid-session changes take effect\n\t\tif (!settingsManager.getBlockImages()) {\n\t\t\treturn converted;\n\t\t}\n\t\t// Filter out ImageContent from all messages, replacing with text placeholder\n\t\treturn converted.map((msg) => {\n\t\t\tif (msg.role === \"user\" || msg.role === \"toolResult\") {\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content)) {\n\t\t\t\t\tconst hasImages = content.some((c) => c.type === \"image\");\n\t\t\t\t\tif (hasImages) {\n\t\t\t\t\t\tconst filteredContent = content\n\t\t\t\t\t\t\t.map((c) =>\n\t\t\t\t\t\t\t\tc.type === \"image\" ? { type: \"text\" as const, text: \"Image reading is disabled.\" } : c,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t\t(c, i, arr) =>\n\t\t\t\t\t\t\t\t\t// Dedupe consecutive \"Image reading is disabled.\" texts\n\t\t\t\t\t\t\t\t\t!(\n\t\t\t\t\t\t\t\t\t\tc.type === \"text\" &&\n\t\t\t\t\t\t\t\t\t\tc.text === \"Image reading is disabled.\" &&\n\t\t\t\t\t\t\t\t\t\ti > 0 &&\n\t\t\t\t\t\t\t\t\t\tarr[i - 1].type === \"text\" &&\n\t\t\t\t\t\t\t\t\t\t(arr[i - 1] as { type: \"text\"; text: string }).text === \"Image reading is disabled.\"\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn { ...msg, content: filteredContent };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn msg;\n\t\t});\n\t};\n\n\tconst extensionRunnerRef: { current?: ExtensionRunner } = {};\n\n\tagent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt: \"\",\n\t\t\tmodel,\n\t\t\tthinkingLevel,\n\t\t\ttools: [],\n\t\t},\n\t\tconvertToLlm: convertToLlmWithBlockImages,\n\t\tstreamFn: async (model, context, options) => {\n\t\t\tconst auth = await modelRegistry.getApiKeyAndHeaders(model);\n\t\t\tif (!auth.ok) {\n\t\t\t\tthrow new Error(auth.error);\n\t\t\t}\n\t\t\tconst providerRetrySettings = settingsManager.getProviderRetrySettings();\n\t\t\tconst attributionHeaders = getAttributionHeaders(model, settingsManager);\n\t\t\treturn streamSimple(model, context, {\n\t\t\t\t...options,\n\t\t\t\tapiKey: auth.apiKey,\n\t\t\t\tcacheRetention: options?.cacheRetention ?? cacheRetention,\n\t\t\t\ttimeoutMs: options?.timeoutMs ?? providerRetrySettings.timeoutMs,\n\t\t\t\tmaxRetries: options?.maxRetries ?? providerRetrySettings.maxRetries,\n\t\t\t\tmaxRetryDelayMs: options?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n\t\t\t\theaders:\n\t\t\t\t\tattributionHeaders || auth.headers || options?.headers\n\t\t\t\t\t\t? { ...attributionHeaders, ...auth.headers, ...options?.headers }\n\t\t\t\t\t\t: undefined,\n\t\t\t});\n\t\t},\n\t\tonPayload: async (payload, _model) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner?.hasHandlers(\"before_provider_request\")) {\n\t\t\t\treturn payload;\n\t\t\t}\n\t\t\treturn runner.emitBeforeProviderRequest(payload);\n\t\t},\n\t\tonResponse: async (response, _model) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner?.hasHandlers(\"after_provider_response\")) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait runner.emit({\n\t\t\t\ttype: \"after_provider_response\",\n\t\t\t\tstatus: response.status,\n\t\t\t\theaders: response.headers,\n\t\t\t});\n\t\t},\n\t\tsessionId: sessionManager.getSessionId(),\n\t\ttransformContext: async (messages) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner) return messages;\n\t\t\treturn runner.emitContext(messages);\n\t\t},\n\t\tsteeringMode: settingsManager.getSteeringMode(),\n\t\tfollowUpMode: settingsManager.getFollowUpMode(),\n\t\ttransport: settingsManager.getTransport(),\n\t\tthinkingBudgets: settingsManager.getThinkingBudgets(),\n\t\tmaxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n\t});\n\n\t// Restore messages if session has existing data\n\tif (hasExistingSession) {\n\t\tagent.state.messages = existingSession.messages;\n\t\tif (!hasThinkingEntry) {\n\t\t\tsessionManager.appendThinkingLevelChange(thinkingLevel);\n\t\t}\n\t} else {\n\t\t// Save initial model and thinking level for new sessions so they can be restored on resume\n\t\tif (model) {\n\t\t\tsessionManager.appendModelChange(model.provider, model.id);\n\t\t}\n\t\tsessionManager.appendThinkingLevelChange(thinkingLevel);\n\t}\n\n\tconst session = new AgentSession({\n\t\tagent,\n\t\tsessionManager,\n\t\tsettingsManager,\n\t\tcwd,\n\t\tscopedModels: options.scopedModels,\n\t\tresourceLoader,\n\t\tcustomTools: options.customTools,\n\t\tmodelRegistry,\n\t\tinitialActiveToolNames,\n\t\tallowedToolNames,\n\t\textensionRunnerRef,\n\t\tsessionStartEvent: options.sessionStartEvent,\n\t});\n\tconst extensionsResult = resourceLoader.getExtensions();\n\n\treturn {\n\t\tsession,\n\t\textensionsResult,\n\t\tmodelFallbackMessage,\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sdk.js","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport { Agent, type AgentMessage, type ThinkingLevel } from \"@leanandmean/agent\";\nimport { type CacheRetention, clampThinkingLevel, type Message, type Model, streamSimple } from \"@leanandmean/ai\";\nimport { getAgentDir } from \"../config.js\";\nimport { AgentSession } from \"./agent-session.js\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.js\";\nimport { AuthStorage } from \"./auth-storage.js\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.js\";\nimport type { ExtensionRunner, LoadExtensionsResult, SessionStartEvent, ToolDefinition } from \"./extensions/index.js\";\nimport { convertToLlm } from \"./messages.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { findInitialModel } from \"./model-resolver.js\";\nimport type { ResourceLoader } from \"./resource-loader.js\";\nimport { DefaultResourceLoader } from \"./resource-loader.js\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.js\";\nimport { SettingsManager } from \"./settings-manager.js\";\nimport { isInstallTelemetryEnabled } from \"./telemetry.js\";\nimport { time } from \"./timings.js\";\nimport {\n\tcreateBashTool,\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateWriteTool,\n\ttype ToolName,\n\twithFileMutationQueue,\n} from \"./tools/index.js\";\n\nexport interface CreateAgentSessionOptions {\n\t/** Working directory for project-local discovery. Default: process.cwd() */\n\tcwd?: string;\n\t/** Global config directory. Default: ~/.scramjet/agent */\n\tagentDir?: string;\n\t/** Output-throughput history path resolved by runtime services. */\n\toutputThroughputHistoryPath?: string;\n\n\t/** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n\tauthStorage?: AuthStorage;\n\t/** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n\tmodelRegistry?: ModelRegistry;\n\n\t/** Model to use. Default: from settings, else first available */\n\tmodel?: Model<any>;\n\t/** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n\tthinkingLevel?: ThinkingLevel;\n\t/** Models available for cycling (Ctrl+P in interactive mode) */\n\tscopedModels?: Array<{ model: Model<any>; thinkingLevel?: ThinkingLevel }>;\n\t/**\n\t * Prompt-cache retention for provider requests.\n\t *\n\t * Precedence: this option, then the PI_CACHE_RETENTION environment variable,\n\t * then \"long\". Pass \"short\" for short-lived sessions such as subagents.\n\t */\n\tcacheRetention?: CacheRetention;\n\n\t/**\n\t * Optional default tool suppression mode when no explicit allowlist is provided.\n\t *\n\t * - \"all\": start with no tools enabled\n\t * - \"builtin\": disable the default built-in tools (read, bash, edit, write)\n\t * but keep extension/custom tools enabled\n\t */\n\tnoTools?: \"all\" | \"builtin\";\n\t/**\n\t * Optional allowlist of tool names.\n\t *\n\t * When omitted, pi enables the default built-in tools (read, bash, edit, write)\n\t * and leaves extension/custom tools enabled unless `noTools` changes that default.\n\t * When provided, only the listed tool names are enabled.\n\t */\n\ttools?: string[];\n\t/** Custom tools to register (in addition to built-in tools). */\n\tcustomTools?: ToolDefinition[];\n\n\t/** Resource loader. When omitted, DefaultResourceLoader is used. */\n\tresourceLoader?: ResourceLoader;\n\n\t/** Session manager. Default: SessionManager.create(cwd) */\n\tsessionManager?: SessionManager;\n\n\t/** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n\tsettingsManager?: SettingsManager;\n\t/** Session start event metadata for extension runtime startup. */\n\tsessionStartEvent?: SessionStartEvent;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n\t/** The created session */\n\tsession: AgentSession;\n\t/** Extensions result (for UI context setup in interactive mode) */\n\textensionsResult: LoadExtensionsResult;\n\t/** Warning if session was restored with a different model than saved */\n\tmodelFallbackMessage?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.js\";\nexport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tExtensionFactory,\n\tSlashCommandInfo,\n\tSlashCommandSource,\n\tToolDefinition,\n} from \"./extensions/index.js\";\nexport type { PromptTemplate } from \"./prompt-templates.js\";\nexport type { Skill } from \"./skills.js\";\nexport type { Tool } from \"./tools/index.js\";\n\nexport {\n\twithFileMutationQueue,\n\t// Tool factories (for custom cwd)\n\tcreateCodingTools,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateBashTool,\n\tcreateEditTool,\n\tcreateWriteTool,\n\tcreateGrepTool,\n\tcreateFindTool,\n\tcreateLsTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n\treturn getAgentDir();\n}\n\nfunction isCacheRetention(value: string): value is CacheRetention {\n\treturn value === \"none\" || value === \"short\" || value === \"long\";\n}\n\n// resolveCacheRetention runs on every createAgentSession call (including /new),\n// so warn about a bad env var only once per process to keep it out of the TUI.\n//\n// Defaults to \"long\" (1h tier). NOTE: the Anthropic adapter has a same-named\n// resolveCacheRetention (packages/ai/src/providers/anthropic.ts) that defaults\n// to \"short\" for backward compatibility. The divergence is intentional: this\n// SDK resolver runs first and passes a concrete value down, so the adapter's\n// \"short\" default is effectively shadowed in the coding-agent path. Keep both\n// in mind when changing either default.\nlet warnedInvalidCacheRetention = false;\n\nfunction resolveCacheRetention(option: CacheRetention | undefined): CacheRetention {\n\tif (option) {\n\t\treturn option;\n\t}\n\t// SCRAMJET-DIVERGENCE: prefer SCRAMJET_CACHE_RETENTION, fall back to PI_CACHE_RETENTION.\n\tconst env = process.env.SCRAMJET_CACHE_RETENTION || process.env.PI_CACHE_RETENTION;\n\tif (env) {\n\t\tif (isCacheRetention(env)) {\n\t\t\treturn env;\n\t\t}\n\t\tif (!warnedInvalidCacheRetention) {\n\t\t\twarnedInvalidCacheRetention = true;\n\t\t\tconsole.warn(`Invalid SCRAMJET_CACHE_RETENTION \"${env}\". Valid values: none, short, long. Using \"long\".`);\n\t\t}\n\t}\n\treturn \"long\";\n}\n\nfunction getAttributionHeaders(\n\tmodel: Model<any>,\n\tsettingsManager: SettingsManager,\n): Record<string, string> | undefined {\n\tif (!isInstallTelemetryEnabled(settingsManager)) {\n\t\treturn undefined;\n\t}\n\n\tif (model.provider === \"openrouter\" || model.baseUrl.includes(\"openrouter.ai\")) {\n\t\t// SCRAMJET-DIVERGENCE: Identify as Scramjet to OpenRouter\n\t\treturn {\n\t\t\t\"HTTP-Referer\": \"https://github.com/LeanAndMean/scramjet\",\n\t\t\t\"X-OpenRouter-Title\": \"scramjet\",\n\t\t\t\"X-OpenRouter-Categories\": \"cli-agent\",\n\t\t};\n\t}\n\n\tif (\n\t\tmodel.provider === \"cloudflare-workers-ai\" ||\n\t\tmodel.provider === \"cloudflare-ai-gateway\" ||\n\t\tmodel.baseUrl.includes(\"api.cloudflare.com\") ||\n\t\tmodel.baseUrl.includes(\"gateway.ai.cloudflare.com\")\n\t) {\n\t\t// SCRAMJET-DIVERGENCE: Identify as Scramjet to Cloudflare\n\t\treturn {\n\t\t\t\"User-Agent\": \"scramjet-coding-agent\",\n\t\t};\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@leanandmean/ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {\n\tconst cwd = options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd();\n\tconst agentDir = options.agentDir ?? getDefaultAgentDir();\n\tconst outputThroughputHistoryPath =\n\t\toptions.outputThroughputHistoryPath ?? join(agentDir, \"output-throughput-history.json\");\n\tconst cacheRetention = resolveCacheRetention(options.cacheRetention);\n\tlet resourceLoader = options.resourceLoader;\n\n\t// Use provided or create AuthStorage and ModelRegistry\n\tconst authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n\tconst modelsPath = options.agentDir ? join(agentDir, \"models.json\") : undefined;\n\tconst authStorage = options.authStorage ?? AuthStorage.create(authPath);\n\tconst modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);\n\n\tconst settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n\tconst sessionManager = options.sessionManager ?? SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n\tif (!resourceLoader) {\n\t\tresourceLoader = new DefaultResourceLoader({ cwd, agentDir, settingsManager });\n\t\tawait resourceLoader.reload();\n\t\ttime(\"resourceLoader.reload\");\n\t}\n\n\t// Check if session has existing data to restore\n\tconst existingSession = sessionManager.buildSessionContext();\n\tconst hasExistingSession = existingSession.messages.length > 0;\n\tconst hasThinkingEntry = sessionManager.getBranch().some((entry) => entry.type === \"thinking_level_change\");\n\n\tlet model = options.model;\n\tlet modelFallbackMessage: string | undefined;\n\n\t// If session has data, try to restore model from it\n\tif (!model && hasExistingSession && existingSession.model) {\n\t\tconst restoredModel = modelRegistry.find(existingSession.model.provider, existingSession.model.modelId);\n\t\tif (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n\t\t\tmodel = restoredModel;\n\t\t}\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n\t\t}\n\t}\n\n\t// If still no model, use findInitialModel (checks settings default, then provider defaults)\n\tif (!model) {\n\t\tconst result = await findInitialModel({\n\t\t\tscopedModels: [],\n\t\t\tisContinuing: hasExistingSession,\n\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\tmodelRegistry,\n\t\t});\n\t\tmodel = result.model;\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = formatNoModelsAvailableMessage();\n\t\t} else if (modelFallbackMessage) {\n\t\t\tmodelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n\t\t}\n\t}\n\n\tlet thinkingLevel = options.thinkingLevel;\n\n\t// If session has data, restore thinking level from it\n\tif (thinkingLevel === undefined && hasExistingSession) {\n\t\tthinkingLevel = hasThinkingEntry\n\t\t\t? (existingSession.thinkingLevel as ThinkingLevel)\n\t\t\t: (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n\t}\n\n\t// Fall back to settings default\n\tif (thinkingLevel === undefined) {\n\t\tthinkingLevel = settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n\t}\n\n\t// Clamp to model capabilities\n\tif (!model) {\n\t\tthinkingLevel = \"off\";\n\t} else {\n\t\tthinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n\t}\n\n\tconst defaultActiveToolNames: ToolName[] = [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst allowedToolNames = options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n\tconst initialActiveToolNames: string[] = options.tools\n\t\t? [...options.tools]\n\t\t: options.noTools\n\t\t\t? []\n\t\t\t: defaultActiveToolNames;\n\n\tlet agent: Agent;\n\n\t// Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n\tconst convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n\t\tconst converted = convertToLlm(messages);\n\t\t// Check setting dynamically so mid-session changes take effect\n\t\tif (!settingsManager.getBlockImages()) {\n\t\t\treturn converted;\n\t\t}\n\t\t// Filter out ImageContent from all messages, replacing with text placeholder\n\t\treturn converted.map((msg) => {\n\t\t\tif (msg.role === \"user\" || msg.role === \"toolResult\") {\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content)) {\n\t\t\t\t\tconst hasImages = content.some((c) => c.type === \"image\");\n\t\t\t\t\tif (hasImages) {\n\t\t\t\t\t\tconst filteredContent = content\n\t\t\t\t\t\t\t.map((c) =>\n\t\t\t\t\t\t\t\tc.type === \"image\" ? { type: \"text\" as const, text: \"Image reading is disabled.\" } : c,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t\t(c, i, arr) =>\n\t\t\t\t\t\t\t\t\t// Dedupe consecutive \"Image reading is disabled.\" texts\n\t\t\t\t\t\t\t\t\t!(\n\t\t\t\t\t\t\t\t\t\tc.type === \"text\" &&\n\t\t\t\t\t\t\t\t\t\tc.text === \"Image reading is disabled.\" &&\n\t\t\t\t\t\t\t\t\t\ti > 0 &&\n\t\t\t\t\t\t\t\t\t\tarr[i - 1].type === \"text\" &&\n\t\t\t\t\t\t\t\t\t\t(arr[i - 1] as { type: \"text\"; text: string }).text === \"Image reading is disabled.\"\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn { ...msg, content: filteredContent };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn msg;\n\t\t});\n\t};\n\n\tconst extensionRunnerRef: { current?: ExtensionRunner } = {};\n\n\tagent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt: \"\",\n\t\t\tmodel,\n\t\t\tthinkingLevel,\n\t\t\ttools: [],\n\t\t},\n\t\tconvertToLlm: convertToLlmWithBlockImages,\n\t\tstreamFn: async (model, context, options) => {\n\t\t\tconst auth = await modelRegistry.getApiKeyAndHeaders(model);\n\t\t\tif (!auth.ok) {\n\t\t\t\tthrow new Error(auth.error);\n\t\t\t}\n\t\t\tconst providerRetrySettings = settingsManager.getProviderRetrySettings();\n\t\t\tconst attributionHeaders = getAttributionHeaders(model, settingsManager);\n\t\t\treturn streamSimple(model, context, {\n\t\t\t\t...options,\n\t\t\t\tapiKey: auth.apiKey,\n\t\t\t\tcacheRetention: options?.cacheRetention ?? cacheRetention,\n\t\t\t\ttimeoutMs: options?.timeoutMs ?? providerRetrySettings.timeoutMs,\n\t\t\t\tmaxRetries: options?.maxRetries ?? providerRetrySettings.maxRetries,\n\t\t\t\tmaxRetryDelayMs: options?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n\t\t\t\theaders:\n\t\t\t\t\tattributionHeaders || auth.headers || options?.headers\n\t\t\t\t\t\t? { ...attributionHeaders, ...auth.headers, ...options?.headers }\n\t\t\t\t\t\t: undefined,\n\t\t\t});\n\t\t},\n\t\tbeforeProviderCall: async (context, model) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner?.hasHandlers(\"before_provider_call\")) return context;\n\t\t\treturn runner.emitBeforeProviderCall(context, model);\n\t\t},\n\t\tonPayload: async (payload, model) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner?.hasHandlers(\"before_provider_request\")) {\n\t\t\t\treturn payload;\n\t\t\t}\n\t\t\treturn runner.emitBeforeProviderRequest(payload, model);\n\t\t},\n\t\tonResponse: async (response, _model) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner?.hasHandlers(\"after_provider_response\")) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait runner.emit({\n\t\t\t\ttype: \"after_provider_response\",\n\t\t\t\tstatus: response.status,\n\t\t\t\theaders: response.headers,\n\t\t\t});\n\t\t},\n\t\tsessionId: sessionManager.getSessionId(),\n\t\ttransformContext: async (messages) => {\n\t\t\tconst runner = extensionRunnerRef.current;\n\t\t\tif (!runner) return messages;\n\t\t\treturn runner.emitContext(messages);\n\t\t},\n\t\tsteeringMode: settingsManager.getSteeringMode(),\n\t\tfollowUpMode: settingsManager.getFollowUpMode(),\n\t\ttransport: settingsManager.getTransport(),\n\t\tthinkingBudgets: settingsManager.getThinkingBudgets(),\n\t\tmaxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n\t});\n\n\t// Restore messages if session has existing data\n\tif (hasExistingSession) {\n\t\tagent.state.messages = existingSession.messages;\n\t\tif (!hasThinkingEntry) {\n\t\t\tsessionManager.appendThinkingLevelChange(thinkingLevel);\n\t\t}\n\t} else {\n\t\t// Save initial model and thinking level for new sessions so they can be restored on resume\n\t\tif (model) {\n\t\t\tsessionManager.appendModelChange(model.provider, model.id);\n\t\t}\n\t\tsessionManager.appendThinkingLevelChange(thinkingLevel);\n\t}\n\n\tconst session = new AgentSession({\n\t\tagent,\n\t\tsessionManager,\n\t\tsettingsManager,\n\t\tcwd,\n\t\toutputThroughputHistoryPath,\n\t\tscopedModels: options.scopedModels,\n\t\tresourceLoader,\n\t\tcustomTools: options.customTools,\n\t\tmodelRegistry,\n\t\tinitialActiveToolNames,\n\t\tallowedToolNames,\n\t\textensionRunnerRef,\n\t\tsessionStartEvent: options.sessionStartEvent,\n\t});\n\tawait session.refreshOutputThroughputHistory();\n\tconst extensionsResult = resourceLoader.getExtensions();\n\n\treturn {\n\t\tsession,\n\t\textensionsResult,\n\t\tmodelFallbackMessage,\n\t};\n}\n"]}
|