@toddzheng024/dscode-bundle 0.7.2 → 0.7.4
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/THIRD_PARTY_NOTICES.md +3 -0
- package/cordis.patch.yml +4 -0
- package/package.json +4 -2
- package/plugins/auto-review/index.mjs +2 -1
- package/plugins/code-review/git.mjs +24 -2
- package/plugins/code-review/index.mjs +13 -6
- package/plugins/credentials/index.mjs +7 -5
- package/plugins/exec/cli.mjs +63 -0
- package/plugins/exec/index.mjs +1 -1
- package/plugins/memory/index.mjs +17 -12
- package/plugins/providers/catalog.mjs +134 -0
- package/plugins/session-cards/index.mjs +11 -8
- package/plugins/session-cards/manager.mjs +1 -1
- package/plugins/session-metrics/attribution.mjs +19 -0
- package/plugins/session-metrics/balance.mjs +41 -22
- package/plugins/session-metrics/index.mjs +26 -14
- package/plugins/session-metrics/pricing.mjs +24 -4
- package/plugins/session-metrics/view.mjs +5 -3
- package/plugins/tui-tools/doctor.mjs +9 -6
- package/plugins/tui-tools/index.mjs +2 -2
- package/plugins/ultra/policy.mjs +16 -0
- package/vendor/pi-ai/LICENSE +21 -0
- package/vendor/pi-ai/index.js +2702 -0
- package/vendor/pi-ai/types/adapter.d.ts +105 -0
- package/vendor/pi-ai/types/auth.d.ts +60 -0
- package/vendor/pi-ai/types/catalog.d.ts +355 -0
- package/vendor/pi-ai/types/config.d.ts +208 -0
- package/vendor/pi-ai/types/context.d.ts +42 -0
- package/vendor/pi-ai/types/discovery.d.ts +43 -0
- package/vendor/pi-ai/types/index.d.ts +69 -0
- package/vendor/pi-ai/types/login.d.ts +21 -0
- package/vendor/pi-ai/types/provider.d.ts +59 -0
- package/vendor/pi-ai/types/replay.d.ts +63 -0
- package/vendor/pi-ai/types/stream.d.ts +43 -0
- package/vendor/tui/dscode-providers/catalog.mjs +134 -0
- package/vendor/tui/index.mjs +146 -59
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
// Remaining
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
1
|
+
// Remaining provider balance, refreshed on a long cache. DeepSeek's response
|
|
2
|
+
// also carries the trusted clock: its `Date` header anchors peak/off-peak pricing
|
|
3
|
+
// without a second network call. OpenRouter has no peak window, so only its
|
|
4
|
+
// remaining credits are read.
|
|
5
|
+
const SOURCES = {
|
|
6
|
+
'deepseek-official': { url: 'https://api.deepseek.com/user/balance', env: 'DEEPSEEK_API_KEY', parse: body => parseBalance(body), clock: true },
|
|
7
|
+
openrouter: { url: 'https://openrouter.ai/api/v1/credits', env: 'OPENROUTER_API_KEY', parse: body => parseOpenRouterCredits(body), clock: false },
|
|
8
|
+
};
|
|
9
|
+
export const BALANCE_PROVIDERS = Object.freeze(Object.keys(SOURCES));
|
|
5
10
|
const CACHE_MS = 5 * 60 * 1000;
|
|
6
11
|
const RETRY_MS = 60 * 1000;
|
|
7
|
-
|
|
12
|
+
const snapshots = new Map();
|
|
13
|
+
let clock = { anchor: null, skewMs: 0 };
|
|
14
|
+
const snapshotOf = provider => snapshots.get(provider) ?? { balance: null, fetchedAt: 0, pending: false };
|
|
8
15
|
|
|
9
16
|
/** Best-effort positive USD balance from a `/user/balance` body. */
|
|
10
17
|
export function parseBalance(body) {
|
|
@@ -15,38 +22,50 @@ export function parseBalance(body) {
|
|
|
15
22
|
return body?.is_available === false ? null : total;
|
|
16
23
|
}
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
/** Remaining USD credits from an OpenRouter `/credits` body: purchased minus used. */
|
|
26
|
+
export function parseOpenRouterCredits(body) {
|
|
27
|
+
const credits = Number(body?.data?.total_credits), used = Number(body?.data?.total_usage);
|
|
28
|
+
if (body?.data?.total_credits == null || body?.data?.total_usage == null) return null;
|
|
29
|
+
if (!Number.isFinite(credits) || !Number.isFinite(used) || credits < 0 || used < 0) return null;
|
|
30
|
+
return Math.max(0, credits - used);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function balanceNow(provider = 'deepseek-official') { return snapshotOf(provider).balance; }
|
|
34
|
+
/** Clock anchored to the last DeepSeek balance response's `Date` header, else the local one. */
|
|
20
35
|
export function trustedNow() {
|
|
21
36
|
const local = Date.now();
|
|
22
|
-
return
|
|
37
|
+
return clock.anchor === null ? local : local + clock.skewMs;
|
|
23
38
|
}
|
|
24
39
|
|
|
25
|
-
/** Refresh at most once per cache window; never throws into the render path. */
|
|
40
|
+
/** Refresh one provider at most once per cache window; never throws into the render path. */
|
|
26
41
|
export async function refreshBalance(options = {}) {
|
|
42
|
+
const provider = options.provider ?? 'deepseek-official';
|
|
43
|
+
const source = SOURCES[provider];
|
|
44
|
+
if (!source) return null;
|
|
45
|
+
const snapshot = snapshotOf(provider);
|
|
27
46
|
const now = Date.now();
|
|
28
47
|
if (snapshot.pending || now - snapshot.fetchedAt < CACHE_MS) return snapshot.balance;
|
|
29
|
-
const
|
|
30
|
-
|
|
48
|
+
const retrySoon = () => Date.now() - (CACHE_MS - RETRY_MS);
|
|
49
|
+
const key = options.key ?? process.env[source.env];
|
|
50
|
+
if (!key) { snapshots.set(provider, { ...snapshot, fetchedAt: retrySoon(), pending: false }); return snapshot.balance; }
|
|
31
51
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
32
52
|
if (typeof fetchImpl !== 'function') return snapshot.balance;
|
|
33
|
-
snapshot
|
|
53
|
+
snapshots.set(provider, { ...snapshot, pending: true });
|
|
34
54
|
try {
|
|
35
|
-
const response = await fetchImpl(
|
|
36
|
-
const header = response.headers?.get?.('date');
|
|
55
|
+
const response = await fetchImpl(source.url, { headers: { Authorization: `Bearer ${key}`, Accept: 'application/json' } });
|
|
56
|
+
const header = source.clock ? response.headers?.get?.('date') : null;
|
|
37
57
|
const anchor = header ? Date.parse(header) : NaN;
|
|
38
58
|
const body = await response.json();
|
|
39
|
-
const parsed = response.ok ?
|
|
40
|
-
|
|
59
|
+
const parsed = response.ok ? source.parse(body) : null;
|
|
60
|
+
if (Number.isFinite(anchor)) clock = { anchor, skewMs: anchor - Date.now() };
|
|
61
|
+
snapshots.set(provider, {
|
|
41
62
|
balance: parsed === null && response.ok ? null : parsed ?? snapshot.balance,
|
|
42
|
-
fetchedAt: parsed === null ?
|
|
43
|
-
clock: Number.isFinite(anchor) ? anchor : snapshot.clock,
|
|
44
|
-
skewMs: Number.isFinite(anchor) ? anchor - Date.now() : snapshot.skewMs,
|
|
63
|
+
fetchedAt: parsed === null ? retrySoon() : Date.now(),
|
|
45
64
|
pending: false,
|
|
46
|
-
};
|
|
65
|
+
});
|
|
47
66
|
} catch {
|
|
48
67
|
// A transient failure keeps the last known balance and retries sooner.
|
|
49
|
-
|
|
68
|
+
snapshots.set(provider, { ...snapshot, fetchedAt: retrySoon(), pending: false });
|
|
50
69
|
}
|
|
51
|
-
return
|
|
70
|
+
return snapshotOf(provider).balance;
|
|
52
71
|
}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { appendMetric } from './store.mjs';
|
|
3
|
-
import { estimateCost,
|
|
3
|
+
import { estimateCost, priceVersionFor } from './pricing.mjs';
|
|
4
4
|
import { setMetricSource } from './view.mjs';
|
|
5
|
-
import { refreshBalance } from './balance.mjs';
|
|
5
|
+
import { BALANCE_PROVIDERS, refreshBalance } from './balance.mjs';
|
|
6
|
+
import { providerSpec } from '../providers/catalog.mjs';
|
|
6
7
|
import { createWindowRate } from './rate.mjs';
|
|
8
|
+
import { currentCharge } from './attribution.mjs';
|
|
9
|
+
|
|
10
|
+
// Chunks that carry generated output; the first one marks time to first token.
|
|
11
|
+
const OUTPUT_CHUNKS = new Set(['text-delta', 'reasoning-delta', 'tool-call-delta']);
|
|
7
12
|
export const name = 'dscode-session-metrics';
|
|
8
13
|
export const inject = ['llm', 'agents', 'tokenMeter', 'sessionProjections'];
|
|
9
14
|
export function apply(ctx) {
|
|
@@ -26,42 +31,49 @@ export function apply(ctx) {
|
|
|
26
31
|
// inject the credentials service, so a missing one cannot fail startup), keep
|
|
27
32
|
// the request on a five-minute cache, and never let it reach the render path.
|
|
28
33
|
const credentials = ctx.get?.('credentials');
|
|
29
|
-
const refresh =
|
|
34
|
+
const refresh = () => Promise.all(BALANCE_PROVIDERS.map(async provider => {
|
|
30
35
|
try {
|
|
31
|
-
const
|
|
36
|
+
const ref = providerSpec(provider).credentialRef;
|
|
37
|
+
const resolved = await credentials?.resolve?.(ref);
|
|
32
38
|
const key = typeof resolved === 'string' ? resolved : resolved?.value;
|
|
33
|
-
await refreshBalance({ key: key ?? process.env
|
|
39
|
+
await refreshBalance({ provider, key: key ?? process.env[ref] });
|
|
34
40
|
} catch {
|
|
35
41
|
/* balance stays unknown */
|
|
36
42
|
}
|
|
37
|
-
};
|
|
43
|
+
}));
|
|
38
44
|
refresh();
|
|
39
45
|
const balanceTimer = setInterval(refresh, 5 * 60 * 1000);
|
|
40
46
|
if (typeof balanceTimer.unref === 'function') balanceTimer.unref();
|
|
41
47
|
ctx.effect(() => () => clearInterval(balanceTimer));
|
|
42
48
|
const record = (id, entry) => { try { appendMetric(home, id, entry); } catch { ctx.logger.warn('Session cost telemetry could not be saved.'); } };
|
|
43
49
|
ctx.on('llm/stream', async function* (options, next) {
|
|
44
|
-
|
|
50
|
+
// Plugin calls made for a session carry no sessionId on the wire; they are charged through the async context.
|
|
51
|
+
const charge = options.sessionId ? undefined : currentCharge();
|
|
52
|
+
const sessionId = options.sessionId ?? charge?.sessionId;
|
|
53
|
+
if (!sessionId || !home) { yield* next(); return; }
|
|
45
54
|
const id = randomUUID(), time = Date.now();
|
|
46
|
-
const
|
|
47
|
-
|
|
55
|
+
const purpose = options.purpose ?? charge?.purpose ?? 'agent';
|
|
56
|
+
const recipients = new Set([sessionId]);
|
|
57
|
+
let child = ctx.agents.get(sessionId);
|
|
48
58
|
while (child?.session.header.origin === 'subagent' && child.session.header.parentSession && !recipients.has(child.session.header.parentSession)) {
|
|
49
59
|
recipients.add(child.session.header.parentSession);
|
|
50
60
|
child = ctx.agents.get(child.session.header.parentSession);
|
|
51
61
|
}
|
|
52
|
-
const save = entry => { for (const recipient of recipients) record(recipient, { ...entry, sessionId
|
|
53
|
-
save(
|
|
54
|
-
let usage;
|
|
55
|
-
const liveSession =
|
|
62
|
+
const save = entry => { for (const recipient of recipients) record(recipient, { ...entry, sessionId }); };
|
|
63
|
+
save({ kind: 'start', id, time, provider: options.provider, model: options.model, purpose });
|
|
64
|
+
let usage, firstTokenTime;
|
|
65
|
+
const liveSession = purpose === 'agent' ? ctx.agents.get(sessionId)?.session : undefined;
|
|
56
66
|
try {
|
|
57
67
|
for await (const chunk of next()) {
|
|
58
68
|
if (chunk.type === 'usage') usage = chunk.usage;
|
|
69
|
+
else if (firstTokenTime === undefined && OUTPUT_CHUNKS.has(chunk.type)) firstTokenTime = Date.now();
|
|
59
70
|
if (liveSession) liveRate.add(liveSession, chunk);
|
|
60
71
|
yield chunk;
|
|
61
72
|
}
|
|
62
73
|
} finally {
|
|
63
74
|
if (liveSession) liveRate.calibrate(liveSession, usage?.outputTokens);
|
|
64
|
-
|
|
75
|
+
// `time` stays the start (it prices the call); `endTime` and `firstTokenTime` time it.
|
|
76
|
+
save({ kind: 'end', id, time, endTime: Date.now(), ...(firstTokenTime === undefined ? {} : { firstTokenTime }), usage: usage ?? null, cost: estimateCost(options.provider, options.model, usage, time), priceVersion: priceVersionFor(options.provider) });
|
|
65
77
|
}
|
|
66
78
|
});
|
|
67
79
|
}
|
|
@@ -2,18 +2,38 @@
|
|
|
2
2
|
// https://api-docs.deepseek.com/quick_start/pricing/
|
|
3
3
|
export const PRICE_SOURCE = 'https://api-docs.deepseek.com/quick_start/pricing/';
|
|
4
4
|
export const PRICE_VERSION = 'deepseek-2026-09-11';
|
|
5
|
+
// OpenRouter list prices for the DeepSeek models `/provider openrouter` declares,
|
|
6
|
+
// from the pinned pi-ai 0.85.1 catalog (its OpenRouter /models snapshot).
|
|
7
|
+
// OpenRouter bills no peak window. [cache read, input, output].
|
|
8
|
+
export const OPENROUTER_PRICE_VERSION = 'openrouter-pi-ai-0.85.1';
|
|
9
|
+
const OPENROUTER_PRICES = {
|
|
10
|
+
'deepseek/deepseek-v4-flash': [0.017052, 0.08526, 0.17052],
|
|
11
|
+
'deepseek/deepseek-v4-pro': [0.074196, 0.890358, 1.780716],
|
|
12
|
+
'deepseek/deepseek-v4-flash-vision-exp': [0.007, 0.22, 0.66],
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** The price table a provider's ledger entries are estimated with. */
|
|
16
|
+
export function priceVersionFor(provider) {
|
|
17
|
+
return provider === 'openrouter' ? OPENROUTER_PRICE_VERSION : PRICE_VERSION;
|
|
18
|
+
}
|
|
19
|
+
|
|
5
20
|
export function estimateCost(provider, model, usage, time) {
|
|
6
|
-
if (
|
|
21
|
+
if (!usage || !Number.isFinite(time)) return null;
|
|
22
|
+
if (provider === 'openrouter') return OPENROUTER_PRICES[model] ? charge(usage, OPENROUTER_PRICES[model]) : null;
|
|
23
|
+
if (provider !== 'deepseek-official') return null;
|
|
7
24
|
// Earlier requests require an older price table; never back-price them at today's rate.
|
|
8
25
|
if (time < Date.UTC(2026, 8, 11)) return null;
|
|
9
26
|
const flash = ['deepseek-flash', 'deepseek-v4-flash', 'deepseek-v4-flash-vision-exp'].includes(model)
|
|
10
27
|
|| model === 'deepseek-v4-pro' && time >= Date.UTC(2026, 8, 14, 4);
|
|
11
28
|
if (!flash && model !== 'deepseek-v4-pro') return null;
|
|
12
|
-
const
|
|
13
|
-
|
|
29
|
+
const cost = charge(usage, flash ? [0.003, 0.15, 0.6] : [0.022, 0.66, 1.98]);
|
|
30
|
+
return cost === null ? null : cost * (isPeak(time) ? 2 : 1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function charge(usage, [read, input, output]) {
|
|
14
34
|
const values = [usage.inputTokens, usage.outputTokens, usage.cacheReadTokens ?? 0, usage.cacheWriteTokens ?? 0];
|
|
15
35
|
if (!values.every(n => Number.isFinite(n) && n >= 0) || values[3] !== 0) return null;
|
|
16
|
-
return (values[0] * input + values[1] * output + values[2] * read)
|
|
36
|
+
return (values[0] * input + values[1] * output + values[2] * read) / 1e6;
|
|
17
37
|
}
|
|
18
38
|
|
|
19
39
|
/**
|
|
@@ -3,6 +3,7 @@ import { t } from '../i18n/messages.mjs';
|
|
|
3
3
|
import { estimateCost, peakEmoji } from './pricing.mjs';
|
|
4
4
|
import { balanceNow, trustedNow } from './balance.mjs';
|
|
5
5
|
import { sessionAverageTps } from './rate.mjs';
|
|
6
|
+
import { providerOfHeader } from '../providers/catalog.mjs';
|
|
6
7
|
let source;
|
|
7
8
|
export function setMetricSource(next) { source = next; return () => { if (source === next) source = undefined; }; }
|
|
8
9
|
export function summarize(rows, events = [], corrupt = false) {
|
|
@@ -44,13 +45,14 @@ export function displayWidth(text) {
|
|
|
44
45
|
for (const char of text) width += /[\u1100-\u115f\u2e80-\ua4cf\uac00-\ud7a3\uf900-\ufaff\ufe30-\ufe4f\uff00-\uff60\uffe0-\uffe6\u2600-\u27bf\u{1f300}-\u{1faff}]/u.test(char) ? 2 : 1;
|
|
45
46
|
return width;
|
|
46
47
|
}
|
|
47
|
-
export function formatFooter(metrics, context, columns = 80, rates, locale = 'en', header = '') {
|
|
48
|
+
export function formatFooter(metrics, context, columns = 80, rates, locale = 'en', header = '', provider = providerOfHeader(header) ?? 'deepseek-official') {
|
|
48
49
|
const label = key => t(locale, key);
|
|
49
50
|
const ctx = Number.isFinite(context) ? `${Math.round(context)}%` : '--';
|
|
50
51
|
const cache = metrics.cache === null ? '--' : `${metrics.cache.toFixed(1)}%`;
|
|
51
|
-
|
|
52
|
+
// The balance belongs to the provider the header names; only DeepSeek's official route bills by a peak window.
|
|
53
|
+
const balance = balanceNow(provider);
|
|
52
54
|
const spend = metrics.unknown && metrics.cost === 0 ? '--' : `$${metrics.cost.toFixed(2)}${metrics.unknown ? '+' : ''}${metrics.pending ? '…' : ''}`;
|
|
53
|
-
const dollars = `${spend} / ${balance === null ? '$--' : '$' + balance.toFixed(2)}
|
|
55
|
+
const dollars = `${spend} / ${balance === null ? '$--' : '$' + balance.toFixed(2)}${provider === 'deepseek-official' ? ' ' + peakEmoji(trustedNow()) : ''}`;
|
|
54
56
|
const base = rates ? [
|
|
55
57
|
`${label('footer.current')}: ${Number.isFinite(rates.current) ? '~' + rates.current.toFixed(1) : '--'} tps`,
|
|
56
58
|
`${label('footer.average')}: ${Number.isFinite(rates.average) ? rates.average.toFixed(1) : '--'} tps`,
|
|
@@ -4,6 +4,7 @@ import { Logger } from '@deepseek-ai/cordis';
|
|
|
4
4
|
import { BlockAssembler, createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
5
5
|
import { redact } from '../auto-review/policy.mjs';
|
|
6
6
|
import { t, readLanguage } from '../i18n/messages.mjs';
|
|
7
|
+
import { chargeTo } from '../session-metrics/attribution.mjs';
|
|
7
8
|
const L = (key, params) => t(readLanguage(), key, params);
|
|
8
9
|
|
|
9
10
|
const MAX_LOG_BYTES = 1024 * 1024;
|
|
@@ -118,7 +119,7 @@ export function localDoctorReport(evidence) {
|
|
|
118
119
|
return `${L('doctor.evidence', { traces: evidence.traces.length, logs: evidence.logs.length })}${evidence.logs.length ? '' : evidence.logCoverage ?? ''}\n${local.length ? local.slice(-8).join('\n') : L('doctor.noFindings')}\n${near300.length >= 2 ? `${L('doctor.nearTimeout', { count: near300.length })}\n` : ''}${evidence.logs.slice(-5).map(l => `${time(l.time)} ${l.level} ${l.source}: ${l.detail}`).join('\n')}`;
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
export async function analyzeDoctorEvidence(ctx, evidence, route, signal, { model = true } = {}) {
|
|
122
|
+
export async function analyzeDoctorEvidence(ctx, evidence, route, signal, { model = true, sessionId } = {}) {
|
|
122
123
|
const fallback = localDoctorReport(evidence);
|
|
123
124
|
if (!model) return fallback;
|
|
124
125
|
if (!route?.provider || !route?.model) return `${fallback}\n${L('doctor.noRoute')}`;
|
|
@@ -126,11 +127,13 @@ export async function analyzeDoctorEvidence(ctx, evidence, route, signal, { mode
|
|
|
126
127
|
const deadline = AbortSignal.any([signal ?? new AbortController().signal, AbortSignal.timeout(45000)]);
|
|
127
128
|
try {
|
|
128
129
|
let finished = false;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
await chargeTo(sessionId, 'doctor', async () => {
|
|
131
|
+
for await (const chunk of ctx.llm.stream({ provider: route.provider, model: route.model, reasoningEffort: 'low', maxTokens: 4096, system: SYSTEM,
|
|
132
|
+
messages: [createUserMessage({ content: [{ type: 'text', text: JSON.stringify(evidence) }], source: { kind: 'plugin', plugin: 'dscode-doctor' } })], signal: deadline })) {
|
|
133
|
+
deadline.throwIfAborted(); assembler.push(chunk);
|
|
134
|
+
if (chunk.type === 'finish') finished = true;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
134
137
|
if (!finished || assembler.finish.kind !== 'stop') throw Error(`model response was incomplete (${safe(JSON.stringify(assembler.finish ?? { kind: 'no finish' }))})`);
|
|
135
138
|
const blocks = assembler.blocks();
|
|
136
139
|
if (blocks.some(b => !['text', 'reasoning'].includes(b.type))) throw Error('model returned unexpected tool output');
|
|
@@ -92,7 +92,7 @@ export function apply(ctx) {
|
|
|
92
92
|
`Session: ${session.id}`, `Workspace: ${session.header.cwd ?? process.cwd()}`,
|
|
93
93
|
`Agent: ${agent.status} | preset: ${session.header.agentPreset ?? 'standard'}`,
|
|
94
94
|
`Model: ${route?.provider ?? 'default'} / ${route?.model ?? 'default'}`,
|
|
95
|
-
`Effort: ${route?.reasoningEffort ?? 'model default'}${route?.reasoningEffort === 'ultra' ? '
|
|
95
|
+
`Effort: ${route?.reasoningEffort ?? 'model default'}${route?.reasoningEffort === 'ultra' ? ` (${route.provider === 'openrouter' ? 'OpenRouter wire: xhigh' : 'DeepSeek wire: max'}; collaboration enabled)` : ''}`,
|
|
96
96
|
`Permission: ${show(ctx.permissionPresets.current(session))}`,
|
|
97
97
|
`Tokens: ${show(usage ?? 'no provider usage yet')}`,
|
|
98
98
|
`Context: ${pressure?.pressureTokens ?? pressure?.surfaceTokens ?? '?'} / ${pressure?.contextWindow ?? '?'} tokens`,
|
|
@@ -120,7 +120,7 @@ export function apply(ctx) {
|
|
|
120
120
|
const evidence = await collectDoctorEvidence(ctx, { agent, signal });
|
|
121
121
|
if (action === 'preview') return ok(JSON.stringify(evidence, null, 2));
|
|
122
122
|
const route = agent.session.requestHeader()?.config ?? agent.options;
|
|
123
|
-
return ok(`${health}\n\n${await analyzeDoctorEvidence(ctx, evidence, route, signal, { model: action !== 'local' })}`);
|
|
123
|
+
return ok(`${health}\n\n${await analyzeDoctorEvidence(ctx, evidence, route, signal, { model: action !== 'local', sessionId: agent.session.id })}`);
|
|
124
124
|
});
|
|
125
125
|
register('mcp', 'MCP list, tools <id>, enable/disable/reconnect <id>', async ({ agent, rawInput }) => {
|
|
126
126
|
const [action = 'list', id, extra] = rawInput.trim().split(/\s+/).filter(Boolean);
|
package/plugins/ultra/policy.mjs
CHANGED
|
@@ -16,6 +16,22 @@ export function flashRequest(options, messages) {
|
|
|
16
16
|
return copy;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* The same shaping for pi-ai routes (OpenRouter), on harness-format options:
|
|
21
|
+
* Ultra sends max and adds the collaboration policy; below Ultra the delegation
|
|
22
|
+
* tools are not offered. Returns a copy; the logged input is never mutated.
|
|
23
|
+
*/
|
|
24
|
+
export function piAiRequest(options) {
|
|
25
|
+
const ultra = options.reasoningEffort === 'ultra';
|
|
26
|
+
const hidden = ultra ? ['workflow', 'ralph'] : ['subagent', 'subagent_fork', 'workflow', 'ralph'];
|
|
27
|
+
const next = { ...options, ...(options.tools ? { tools: options.tools.filter(tool => !hidden.includes(tool.name)) } : {}), ...(ultra ? { reasoningEffort: 'max' } : {}) };
|
|
28
|
+
if (!ultra || options.purpose || !options.tools?.some(t => t.name === 'subagent' || t.name === 'subagent_fork')) return next;
|
|
29
|
+
if (typeof next.system === 'string') return { ...next, system: next.system + '\n\n' + ULTRA_POLICY };
|
|
30
|
+
const [first, ...rest] = next.messages ?? [];
|
|
31
|
+
if (first?.role === 'system' && Array.isArray(first.content)) return { ...next, messages: [{ ...first, content: [...first.content, { type: 'text', text: '\n\n' + ULTRA_POLICY }] }, ...rest] };
|
|
32
|
+
return { ...next, messages: [{ role: 'system', content: [{ type: 'text', text: ULTRA_POLICY }] }, ...(next.messages ?? [])] };
|
|
33
|
+
}
|
|
34
|
+
|
|
19
35
|
export function ultraRequest(options, messages) {
|
|
20
36
|
if (options.reasoningEffort !== 'ultra' || options.purpose || !options.tools?.some(t => t.name === 'subagent' || t.name === 'subagent_fork')) return messages;
|
|
21
37
|
const copy = messages.map(m => ({ ...m }));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DeepSeek
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|