@dreb/coding-agent 2.47.0 → 2.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/core/agent-session.d.ts +22 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +90 -12
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/k3-context-tier.d.ts +67 -0
- package/dist/core/k3-context-tier.d.ts.map +1 -0
- package/dist/core/k3-context-tier.js +76 -0
- package/dist/core/k3-context-tier.js.map +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +5 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/tools/ask-user.d.ts.map +1 -1
- package/dist/core/tools/ask-user.js +1 -1
- package/dist/core/tools/ask-user.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/dashboard.md +8 -5
- package/docs/providers.md +1 -0
- package/docs/rpc.md +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi K3 auto context tier.
|
|
3
|
+
*
|
|
4
|
+
* The Kimi coding endpoint serves K3 under two model IDs:
|
|
5
|
+
* - `k3-256k`: 256k context window (cheaper)
|
|
6
|
+
* - `k3`: 1M context window (stated to consume 2x the quota)
|
|
7
|
+
*
|
|
8
|
+
* The pricing/quota terms are as stated by the Kimi team and not
|
|
9
|
+
* independently verified, but they are the rationale for auto-switching:
|
|
10
|
+
* 256k is still large enough for most tasks, so the first 256k of every
|
|
11
|
+
* session can run on the cheaper model and only sessions that genuinely
|
|
12
|
+
* outgrow it pay the 1M premium.
|
|
13
|
+
*
|
|
14
|
+
* `k3-256k` is exclusive to the Kimi for Coding OAuth endpoint — the
|
|
15
|
+
* pay-per-token Moonshot AI Platform does not expose the cheaper variant —
|
|
16
|
+
* so this applies solely to the `kimi-coding-oauth` provider.
|
|
17
|
+
*
|
|
18
|
+
* Per the Kimi backend team, switching from `k3-256k` to `k3` does not
|
|
19
|
+
* invalidate the prompt cache — the cache seamlessly upgrades from 256k to
|
|
20
|
+
* 1M. dreb therefore exposes a single user-selectable `k3` model and
|
|
21
|
+
* automatically upgrades the wire model ID once the session context grows
|
|
22
|
+
* past the 256k cutoff, avoiding context compaction on long-horizon tasks.
|
|
23
|
+
*
|
|
24
|
+
* The upgrade cutoff is the 256k window minus the DEFAULT compaction reserve,
|
|
25
|
+
* i.e. the point where auto-compaction would trigger under default settings
|
|
26
|
+
* for a 256k-window model. Users who lower their compaction threshold compact
|
|
27
|
+
* before the cutoff is ever reached, which effectively disables the upgrade.
|
|
28
|
+
*/
|
|
29
|
+
import type { Api, Model } from "@dreb/ai";
|
|
30
|
+
/** Provider and user-facing model ID the tier logic applies to. */
|
|
31
|
+
export declare const K3_PROVIDER = "kimi-coding-oauth";
|
|
32
|
+
export declare const K3_MODEL_ID = "k3";
|
|
33
|
+
/** Wire model ID sent while in the cheaper 256k tier. */
|
|
34
|
+
export declare const K3_256K_WIRE_MODEL_ID = "k3-256k";
|
|
35
|
+
/** Context window of the 256k tier. */
|
|
36
|
+
export declare const K3_256K_CONTEXT_WINDOW = 262144;
|
|
37
|
+
/** Context window of the 1M tier. */
|
|
38
|
+
export declare const K3_1M_CONTEXT_WINDOW = 1048576;
|
|
39
|
+
/**
|
|
40
|
+
* Context token count at which the wire model ID upgrades from `k3-256k` to
|
|
41
|
+
* `k3`: the 256k window minus the default compaction reserve.
|
|
42
|
+
*/
|
|
43
|
+
export declare const K3_UPGRADE_CUTOFF_TOKENS: number;
|
|
44
|
+
/** Whether the model is the user-facing Kimi K3 model subject to auto context tiers. */
|
|
45
|
+
export declare function isK3Model(model: Model<any> | null | undefined): boolean;
|
|
46
|
+
/** Whether the model is currently in the 256k tier (wire model ID `k3-256k`). */
|
|
47
|
+
export declare function isK3256kTier(model: Model<any> | null | undefined): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Derive the effective model for the current context size.
|
|
50
|
+
*
|
|
51
|
+
* At or below the cutoff: 256k context window with wire model ID `k3-256k`.
|
|
52
|
+
* Above the cutoff: 1M context window with the registry model ID `k3` sent
|
|
53
|
+
* on the wire (the Kimi backend upgrades the cache seamlessly).
|
|
54
|
+
*
|
|
55
|
+
* Returns the input unchanged for non-K3 models and is idempotent for
|
|
56
|
+
* already-derived models. A K3 model whose context window was customized
|
|
57
|
+
* (e.g. a models.json override) is also returned unchanged — automatic
|
|
58
|
+
* tiering never silently replaces user-configured limits.
|
|
59
|
+
*/
|
|
60
|
+
export declare function deriveK3ContextTierModel<TApi extends Api>(model: Model<TApi>, contextTokens: number): Model<TApi>;
|
|
61
|
+
export declare function deriveK3ContextTierModel<TApi extends Api>(model: Model<TApi> | undefined, contextTokens: number): Model<TApi> | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Whether the session should upgrade from the 256k tier to the 1M tier.
|
|
64
|
+
* Only true while in the 256k tier and past the cutoff.
|
|
65
|
+
*/
|
|
66
|
+
export declare function shouldUpgradeK3Tier(model: Model<any> | null | undefined, contextTokens: number): boolean;
|
|
67
|
+
//# sourceMappingURL=k3-context-tier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"k3-context-tier.d.ts","sourceRoot":"","sources":["../../src/core/k3-context-tier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAG3C,mEAAmE;AACnE,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAC/C,eAAO,MAAM,WAAW,OAAO,CAAC;AAEhC,yDAAyD;AACzD,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C,uCAAuC;AACvC,eAAO,MAAM,sBAAsB,SAAS,CAAC;AAE7C,qCAAqC;AACrC,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,wBAAwB,QAAqE,CAAC;AAE3G,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEvE;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;AACnH,wBAAgB,wBAAwB,CAAC,IAAI,SAAS,GAAG,EACxD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,SAAS,EAC9B,aAAa,EAAE,MAAM,GACnB,KAAK,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;AAkB3B;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAExG","sourcesContent":["/**\n * Kimi K3 auto context tier.\n *\n * The Kimi coding endpoint serves K3 under two model IDs:\n * - `k3-256k`: 256k context window (cheaper)\n * - `k3`: 1M context window (stated to consume 2x the quota)\n *\n * The pricing/quota terms are as stated by the Kimi team and not\n * independently verified, but they are the rationale for auto-switching:\n * 256k is still large enough for most tasks, so the first 256k of every\n * session can run on the cheaper model and only sessions that genuinely\n * outgrow it pay the 1M premium.\n *\n * `k3-256k` is exclusive to the Kimi for Coding OAuth endpoint — the\n * pay-per-token Moonshot AI Platform does not expose the cheaper variant —\n * so this applies solely to the `kimi-coding-oauth` provider.\n *\n * Per the Kimi backend team, switching from `k3-256k` to `k3` does not\n * invalidate the prompt cache — the cache seamlessly upgrades from 256k to\n * 1M. dreb therefore exposes a single user-selectable `k3` model and\n * automatically upgrades the wire model ID once the session context grows\n * past the 256k cutoff, avoiding context compaction on long-horizon tasks.\n *\n * The upgrade cutoff is the 256k window minus the DEFAULT compaction reserve,\n * i.e. the point where auto-compaction would trigger under default settings\n * for a 256k-window model. Users who lower their compaction threshold compact\n * before the cutoff is ever reached, which effectively disables the upgrade.\n */\n\nimport type { Api, Model } from \"@dreb/ai\";\nimport { DEFAULT_COMPACTION_SETTINGS } from \"./compaction/compaction.js\";\n\n/** Provider and user-facing model ID the tier logic applies to. */\nexport const K3_PROVIDER = \"kimi-coding-oauth\";\nexport const K3_MODEL_ID = \"k3\";\n\n/** Wire model ID sent while in the cheaper 256k tier. */\nexport const K3_256K_WIRE_MODEL_ID = \"k3-256k\";\n\n/** Context window of the 256k tier. */\nexport const K3_256K_CONTEXT_WINDOW = 262144;\n\n/** Context window of the 1M tier. */\nexport const K3_1M_CONTEXT_WINDOW = 1048576;\n\n/**\n * Context token count at which the wire model ID upgrades from `k3-256k` to\n * `k3`: the 256k window minus the default compaction reserve.\n */\nexport const K3_UPGRADE_CUTOFF_TOKENS = K3_256K_CONTEXT_WINDOW - DEFAULT_COMPACTION_SETTINGS.reserveTokens;\n\n/** Whether the model is the user-facing Kimi K3 model subject to auto context tiers. */\nexport function isK3Model(model: Model<any> | null | undefined): boolean {\n\treturn model?.provider === K3_PROVIDER && model?.id === K3_MODEL_ID;\n}\n\n/** Whether the model is currently in the 256k tier (wire model ID `k3-256k`). */\nexport function isK3256kTier(model: Model<any> | null | undefined): boolean {\n\treturn isK3Model(model) && model?.wireModelId === K3_256K_WIRE_MODEL_ID;\n}\n\n/**\n * Derive the effective model for the current context size.\n *\n * At or below the cutoff: 256k context window with wire model ID `k3-256k`.\n * Above the cutoff: 1M context window with the registry model ID `k3` sent\n * on the wire (the Kimi backend upgrades the cache seamlessly).\n *\n * Returns the input unchanged for non-K3 models and is idempotent for\n * already-derived models. A K3 model whose context window was customized\n * (e.g. a models.json override) is also returned unchanged — automatic\n * tiering never silently replaces user-configured limits.\n */\nexport function deriveK3ContextTierModel<TApi extends Api>(model: Model<TApi>, contextTokens: number): Model<TApi>;\nexport function deriveK3ContextTierModel<TApi extends Api>(\n\tmodel: Model<TApi> | undefined,\n\tcontextTokens: number,\n): Model<TApi> | undefined;\nexport function deriveK3ContextTierModel<TApi extends Api>(\n\tmodel: Model<TApi> | undefined,\n\tcontextTokens: number,\n): Model<TApi> | undefined {\n\tif (!model || !isK3Model(model)) return model;\n\tconst isStock = model.contextWindow === K3_1M_CONTEXT_WINDOW && model.wireModelId === undefined;\n\tconst isDerived256k = model.contextWindow === K3_256K_CONTEXT_WINDOW && model.wireModelId === K3_256K_WIRE_MODEL_ID;\n\tif (!isStock && !isDerived256k) return model;\n\tif (contextTokens > K3_UPGRADE_CUTOFF_TOKENS) {\n\t\tif (isStock) return model;\n\t\tconst { wireModelId: _droppedWireModelId, ...rest } = model;\n\t\treturn { ...rest, contextWindow: K3_1M_CONTEXT_WINDOW };\n\t}\n\tif (isDerived256k) return model;\n\treturn { ...model, contextWindow: K3_256K_CONTEXT_WINDOW, wireModelId: K3_256K_WIRE_MODEL_ID };\n}\n\n/**\n * Whether the session should upgrade from the 256k tier to the 1M tier.\n * Only true while in the 256k tier and past the cutoff.\n */\nexport function shouldUpgradeK3Tier(model: Model<any> | null | undefined, contextTokens: number): boolean {\n\treturn isK3256kTier(model) && contextTokens > K3_UPGRADE_CUTOFF_TOKENS;\n}\n"]}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi K3 auto context tier.
|
|
3
|
+
*
|
|
4
|
+
* The Kimi coding endpoint serves K3 under two model IDs:
|
|
5
|
+
* - `k3-256k`: 256k context window (cheaper)
|
|
6
|
+
* - `k3`: 1M context window (stated to consume 2x the quota)
|
|
7
|
+
*
|
|
8
|
+
* The pricing/quota terms are as stated by the Kimi team and not
|
|
9
|
+
* independently verified, but they are the rationale for auto-switching:
|
|
10
|
+
* 256k is still large enough for most tasks, so the first 256k of every
|
|
11
|
+
* session can run on the cheaper model and only sessions that genuinely
|
|
12
|
+
* outgrow it pay the 1M premium.
|
|
13
|
+
*
|
|
14
|
+
* `k3-256k` is exclusive to the Kimi for Coding OAuth endpoint — the
|
|
15
|
+
* pay-per-token Moonshot AI Platform does not expose the cheaper variant —
|
|
16
|
+
* so this applies solely to the `kimi-coding-oauth` provider.
|
|
17
|
+
*
|
|
18
|
+
* Per the Kimi backend team, switching from `k3-256k` to `k3` does not
|
|
19
|
+
* invalidate the prompt cache — the cache seamlessly upgrades from 256k to
|
|
20
|
+
* 1M. dreb therefore exposes a single user-selectable `k3` model and
|
|
21
|
+
* automatically upgrades the wire model ID once the session context grows
|
|
22
|
+
* past the 256k cutoff, avoiding context compaction on long-horizon tasks.
|
|
23
|
+
*
|
|
24
|
+
* The upgrade cutoff is the 256k window minus the DEFAULT compaction reserve,
|
|
25
|
+
* i.e. the point where auto-compaction would trigger under default settings
|
|
26
|
+
* for a 256k-window model. Users who lower their compaction threshold compact
|
|
27
|
+
* before the cutoff is ever reached, which effectively disables the upgrade.
|
|
28
|
+
*/
|
|
29
|
+
import { DEFAULT_COMPACTION_SETTINGS } from "./compaction/compaction.js";
|
|
30
|
+
/** Provider and user-facing model ID the tier logic applies to. */
|
|
31
|
+
export const K3_PROVIDER = "kimi-coding-oauth";
|
|
32
|
+
export const K3_MODEL_ID = "k3";
|
|
33
|
+
/** Wire model ID sent while in the cheaper 256k tier. */
|
|
34
|
+
export const K3_256K_WIRE_MODEL_ID = "k3-256k";
|
|
35
|
+
/** Context window of the 256k tier. */
|
|
36
|
+
export const K3_256K_CONTEXT_WINDOW = 262144;
|
|
37
|
+
/** Context window of the 1M tier. */
|
|
38
|
+
export const K3_1M_CONTEXT_WINDOW = 1048576;
|
|
39
|
+
/**
|
|
40
|
+
* Context token count at which the wire model ID upgrades from `k3-256k` to
|
|
41
|
+
* `k3`: the 256k window minus the default compaction reserve.
|
|
42
|
+
*/
|
|
43
|
+
export const K3_UPGRADE_CUTOFF_TOKENS = K3_256K_CONTEXT_WINDOW - DEFAULT_COMPACTION_SETTINGS.reserveTokens;
|
|
44
|
+
/** Whether the model is the user-facing Kimi K3 model subject to auto context tiers. */
|
|
45
|
+
export function isK3Model(model) {
|
|
46
|
+
return model?.provider === K3_PROVIDER && model?.id === K3_MODEL_ID;
|
|
47
|
+
}
|
|
48
|
+
/** Whether the model is currently in the 256k tier (wire model ID `k3-256k`). */
|
|
49
|
+
export function isK3256kTier(model) {
|
|
50
|
+
return isK3Model(model) && model?.wireModelId === K3_256K_WIRE_MODEL_ID;
|
|
51
|
+
}
|
|
52
|
+
export function deriveK3ContextTierModel(model, contextTokens) {
|
|
53
|
+
if (!model || !isK3Model(model))
|
|
54
|
+
return model;
|
|
55
|
+
const isStock = model.contextWindow === K3_1M_CONTEXT_WINDOW && model.wireModelId === undefined;
|
|
56
|
+
const isDerived256k = model.contextWindow === K3_256K_CONTEXT_WINDOW && model.wireModelId === K3_256K_WIRE_MODEL_ID;
|
|
57
|
+
if (!isStock && !isDerived256k)
|
|
58
|
+
return model;
|
|
59
|
+
if (contextTokens > K3_UPGRADE_CUTOFF_TOKENS) {
|
|
60
|
+
if (isStock)
|
|
61
|
+
return model;
|
|
62
|
+
const { wireModelId: _droppedWireModelId, ...rest } = model;
|
|
63
|
+
return { ...rest, contextWindow: K3_1M_CONTEXT_WINDOW };
|
|
64
|
+
}
|
|
65
|
+
if (isDerived256k)
|
|
66
|
+
return model;
|
|
67
|
+
return { ...model, contextWindow: K3_256K_CONTEXT_WINDOW, wireModelId: K3_256K_WIRE_MODEL_ID };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Whether the session should upgrade from the 256k tier to the 1M tier.
|
|
71
|
+
* Only true while in the 256k tier and past the cutoff.
|
|
72
|
+
*/
|
|
73
|
+
export function shouldUpgradeK3Tier(model, contextTokens) {
|
|
74
|
+
return isK3256kTier(model) && contextTokens > K3_UPGRADE_CUTOFF_TOKENS;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=k3-context-tier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"k3-context-tier.js","sourceRoot":"","sources":["../../src/core/k3-context-tier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAEzE,mEAAmE;AACnE,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAC;AAC/C,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC;AAEhC,yDAAyD;AACzD,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAE/C,uCAAuC;AACvC,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAE7C,qCAAqC;AACrC,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC;AAE5C;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,sBAAsB,GAAG,2BAA2B,CAAC,aAAa,CAAC;AAE3G,wFAAwF;AACxF,MAAM,UAAU,SAAS,CAAC,KAAoC,EAAW;IACxE,OAAO,KAAK,EAAE,QAAQ,KAAK,WAAW,IAAI,KAAK,EAAE,EAAE,KAAK,WAAW,CAAC;AAAA,CACpE;AAED,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAAC,KAAoC,EAAW;IAC3E,OAAO,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,EAAE,WAAW,KAAK,qBAAqB,CAAC;AAAA,CACxE;AAmBD,MAAM,UAAU,wBAAwB,CACvC,KAA8B,EAC9B,aAAqB,EACK;IAC1B,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,aAAa,KAAK,oBAAoB,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC;IAChG,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,KAAK,sBAAsB,IAAI,KAAK,CAAC,WAAW,KAAK,qBAAqB,CAAC;IACpH,IAAI,CAAC,OAAO,IAAI,CAAC,aAAa;QAAE,OAAO,KAAK,CAAC;IAC7C,IAAI,aAAa,GAAG,wBAAwB,EAAE,CAAC;QAC9C,IAAI,OAAO;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,EAAE,WAAW,EAAE,mBAAmB,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;QAC5D,OAAO,EAAE,GAAG,IAAI,EAAE,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACzD,CAAC;IACD,IAAI,aAAa;QAAE,OAAO,KAAK,CAAC;IAChC,OAAO,EAAE,GAAG,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC;AAAA,CAC/F;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAoC,EAAE,aAAqB,EAAW;IACzG,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,aAAa,GAAG,wBAAwB,CAAC;AAAA,CACvE","sourcesContent":["/**\n * Kimi K3 auto context tier.\n *\n * The Kimi coding endpoint serves K3 under two model IDs:\n * - `k3-256k`: 256k context window (cheaper)\n * - `k3`: 1M context window (stated to consume 2x the quota)\n *\n * The pricing/quota terms are as stated by the Kimi team and not\n * independently verified, but they are the rationale for auto-switching:\n * 256k is still large enough for most tasks, so the first 256k of every\n * session can run on the cheaper model and only sessions that genuinely\n * outgrow it pay the 1M premium.\n *\n * `k3-256k` is exclusive to the Kimi for Coding OAuth endpoint — the\n * pay-per-token Moonshot AI Platform does not expose the cheaper variant —\n * so this applies solely to the `kimi-coding-oauth` provider.\n *\n * Per the Kimi backend team, switching from `k3-256k` to `k3` does not\n * invalidate the prompt cache — the cache seamlessly upgrades from 256k to\n * 1M. dreb therefore exposes a single user-selectable `k3` model and\n * automatically upgrades the wire model ID once the session context grows\n * past the 256k cutoff, avoiding context compaction on long-horizon tasks.\n *\n * The upgrade cutoff is the 256k window minus the DEFAULT compaction reserve,\n * i.e. the point where auto-compaction would trigger under default settings\n * for a 256k-window model. Users who lower their compaction threshold compact\n * before the cutoff is ever reached, which effectively disables the upgrade.\n */\n\nimport type { Api, Model } from \"@dreb/ai\";\nimport { DEFAULT_COMPACTION_SETTINGS } from \"./compaction/compaction.js\";\n\n/** Provider and user-facing model ID the tier logic applies to. */\nexport const K3_PROVIDER = \"kimi-coding-oauth\";\nexport const K3_MODEL_ID = \"k3\";\n\n/** Wire model ID sent while in the cheaper 256k tier. */\nexport const K3_256K_WIRE_MODEL_ID = \"k3-256k\";\n\n/** Context window of the 256k tier. */\nexport const K3_256K_CONTEXT_WINDOW = 262144;\n\n/** Context window of the 1M tier. */\nexport const K3_1M_CONTEXT_WINDOW = 1048576;\n\n/**\n * Context token count at which the wire model ID upgrades from `k3-256k` to\n * `k3`: the 256k window minus the default compaction reserve.\n */\nexport const K3_UPGRADE_CUTOFF_TOKENS = K3_256K_CONTEXT_WINDOW - DEFAULT_COMPACTION_SETTINGS.reserveTokens;\n\n/** Whether the model is the user-facing Kimi K3 model subject to auto context tiers. */\nexport function isK3Model(model: Model<any> | null | undefined): boolean {\n\treturn model?.provider === K3_PROVIDER && model?.id === K3_MODEL_ID;\n}\n\n/** Whether the model is currently in the 256k tier (wire model ID `k3-256k`). */\nexport function isK3256kTier(model: Model<any> | null | undefined): boolean {\n\treturn isK3Model(model) && model?.wireModelId === K3_256K_WIRE_MODEL_ID;\n}\n\n/**\n * Derive the effective model for the current context size.\n *\n * At or below the cutoff: 256k context window with wire model ID `k3-256k`.\n * Above the cutoff: 1M context window with the registry model ID `k3` sent\n * on the wire (the Kimi backend upgrades the cache seamlessly).\n *\n * Returns the input unchanged for non-K3 models and is idempotent for\n * already-derived models. A K3 model whose context window was customized\n * (e.g. a models.json override) is also returned unchanged — automatic\n * tiering never silently replaces user-configured limits.\n */\nexport function deriveK3ContextTierModel<TApi extends Api>(model: Model<TApi>, contextTokens: number): Model<TApi>;\nexport function deriveK3ContextTierModel<TApi extends Api>(\n\tmodel: Model<TApi> | undefined,\n\tcontextTokens: number,\n): Model<TApi> | undefined;\nexport function deriveK3ContextTierModel<TApi extends Api>(\n\tmodel: Model<TApi> | undefined,\n\tcontextTokens: number,\n): Model<TApi> | undefined {\n\tif (!model || !isK3Model(model)) return model;\n\tconst isStock = model.contextWindow === K3_1M_CONTEXT_WINDOW && model.wireModelId === undefined;\n\tconst isDerived256k = model.contextWindow === K3_256K_CONTEXT_WINDOW && model.wireModelId === K3_256K_WIRE_MODEL_ID;\n\tif (!isStock && !isDerived256k) return model;\n\tif (contextTokens > K3_UPGRADE_CUTOFF_TOKENS) {\n\t\tif (isStock) return model;\n\t\tconst { wireModelId: _droppedWireModelId, ...rest } = model;\n\t\treturn { ...rest, contextWindow: K3_1M_CONTEXT_WINDOW };\n\t}\n\tif (isDerived256k) return model;\n\treturn { ...model, contextWindow: K3_256K_CONTEXT_WINDOW, wireModelId: K3_256K_WIRE_MODEL_ID };\n}\n\n/**\n * Whether the session should upgrade from the 256k tier to the 1M tier.\n * Only true while in the 256k tier and past the cutoff.\n */\nexport function shouldUpgradeK3Tier(model: Model<any> | null | undefined, contextTokens: number): boolean {\n\treturn isK3256kTier(model) && contextTokens > K3_UPGRADE_CUTOFF_TOKENS;\n}\n"]}
|
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 \"@dreb/agent-core\";\nimport type { Message, Model } from \"@dreb/ai\";\nimport { getAgentDir, getDocsPath } from \"../config.js\";\nimport { AgentSession } from \"./agent-session.js\";\nimport { AuthStorage } from \"./auth-storage.js\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.js\";\nimport type { ExtensionRunner, LoadExtensionsResult, ToolDefinition } from \"./extensions/index.js\";\nimport { convertToLlm } from \"./messages.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { findInitialModel } from \"./model-resolver.js\";\nimport { configValueWarnings } from \"./resolve-config-value.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 { resolveEffectiveThinkingLevel, resolveThinkingDisplay } from \"./thinking.js\";\nimport { time } from \"./timings.js\";\nimport {\n\tallTools,\n\tbashTool,\n\tcodingTools,\n\tcreateBashTool,\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateSubagentTool,\n\tcreateWriteTool,\n\teditTool,\n\tfindTool,\n\tgetBackgroundAgents,\n\tgetRunningBackgroundAgents,\n\tgrepTool,\n\tlsTool,\n\tpruneBackgroundAgents,\n\treadOnlyTools,\n\treadTool,\n\tsubagentTool,\n\ttype Tool,\n\ttype ToolName,\n\twithFileMutationQueue,\n\twriteTool,\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: ~/.dreb/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: new ModelRegistry(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\n\t/** Built-in tools to use. Default: all standard tools [read, bash, edit, write, grep, find, ls, web_search, web_fetch, subagent, wait]. `skill`, `tasks_update`, and `search` are always active regardless of this setting. */\n\ttools?: Tool[];\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/** UI type for system prompt context (e.g. \"tui\", \"telegram\", \"rpc\") */\n\tuiType?: string;\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 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\t// Pre-built tools (use process.cwd())\n\treadTool,\n\tbashTool,\n\teditTool,\n\twriteTool,\n\tgrepTool,\n\tfindTool,\n\tlsTool,\n\tsubagentTool,\n\tcodingTools,\n\treadOnlyTools,\n\tallTools as allBuiltInTools,\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\tcreateSubagentTool,\n\t// Background agent registry\n\tgetBackgroundAgents,\n\tgetRunningBackgroundAgents,\n\tpruneBackgroundAgents,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n\treturn getAgentDir();\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 '@dreb/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: [readTool, bashTool],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst agentDir = options.agentDir ?? getDefaultAgentDir();\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 ?? new ModelRegistry(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\tconst hasApiKey = restoredModel ? !!(await modelRegistry.getApiKey(restoredModel)) : false;\n\t\tif (restoredModel && hasApiKey) {\n\t\t\tmodel = restoredModel;\n\t\t}\n\t\tif (!model) {\n\t\t\tconst reason = !restoredModel ? \"not found in registry\" : \"no API key available\";\n\t\t\tmodelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId} (${reason})`;\n\t\t\tconsole.warn(`[model-restore] ${modelFallbackMessage}`);\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: options.scopedModels ?? [],\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 = `No models available. Use /login or set an API key environment variable. See ${join(getDocsPath(), \"providers.md\")}. Then use /model to select a model.`;\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\tthinkingLevel = resolveEffectiveThinkingLevel(model, thinkingLevel);\n\tconst thinkingDisplay = resolveThinkingDisplay(\n\t\tmodel,\n\t\tmodel ? settingsManager.getModelThinkingDisplay(model.id) : undefined,\n\t);\n\n\t// Tools that are always active when available (created by factory, not in allTools singleton).\n\t// suggest_next is only auto-activated when tools aren't explicitly specified — subagent\n\t// child processes pass --tools which excludes suggest_next (it would end the turn mid-work).\n\tconst alwaysActiveBuiltins = options.tools\n\t\t? [\"skill\", \"tasks_update\", \"search\"]\n\t\t: [\"skill\", \"tasks_update\", \"search\", \"suggest_next\"];\n\tconst defaultActiveToolNames: ToolName[] = [\n\t\t\"read\",\n\t\t\"bash\",\n\t\t\"edit\",\n\t\t\"write\",\n\t\t\"grep\",\n\t\t\"find\",\n\t\t\"ls\",\n\t\t\"web_search\",\n\t\t\"web_fetch\",\n\t\t\"subagent\",\n\t\t\"wait\",\n\t\t\"ask_user\",\n\t];\n\tconst initialActiveToolNames: string[] = options.tools\n\t\t? [...options.tools.map((t) => t.name).filter((n): n is ToolName => n in allTools), ...alwaysActiveBuiltins]\n\t\t: [...defaultActiveToolNames, ...alwaysActiveBuiltins];\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\tconst sessionRef: { current?: AgentSession } = {};\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\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\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\tthinkingDisplay,\n\t\tmaxRetryDelayMs: settingsManager.getRetrySettings().maxDelayMs,\n\t\tonWarning: (code: string, message: string) => {\n\t\t\t// Wire provider-level warnings to the session for user/agent visibility\n\t\t\tconst informational =\n\t\t\t\tcode === \"sse_parse_error\" || code === \"ws_parse_error\" || code === \"json_parse_total_failure\";\n\t\t\tsessionRef.current?.warnInSession(message, { informational });\n\t\t},\n\t\tgetApiKey: async (provider) => {\n\t\t\t// Use the provider argument from the in-flight request;\n\t\t\t// agent.state.model may already be switched mid-turn.\n\t\t\tconst resolvedProvider = provider || agent.state.model?.provider;\n\t\t\tif (!resolvedProvider) {\n\t\t\t\tthrow new Error(\"No model selected\");\n\t\t\t}\n\t\t\tconst key = await modelRegistry.getApiKeyForProvider(resolvedProvider);\n\t\t\t// Surface any config value resolution warnings (e.g. failed !command API keys)\n\t\t\tif (configValueWarnings.length > 0) {\n\t\t\t\tconst warnings = configValueWarnings.splice(0);\n\t\t\t\tfor (const w of warnings) {\n\t\t\t\t\tsessionRef.current?.warnInSession(w);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!key) {\n\t\t\t\tconst model = agent.state.model;\n\t\t\t\tconst isOAuth = model && modelRegistry.isUsingOAuth(model);\n\t\t\t\tif (isOAuth) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Authentication failed for \"${resolvedProvider}\". ` +\n\t\t\t\t\t\t\t`Credentials may have expired or network is unavailable. ` +\n\t\t\t\t\t\t\t`Run '/login ${resolvedProvider}' to re-authenticate.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No API key found for \"${resolvedProvider}\". ` +\n\t\t\t\t\t\t`Set an API key environment variable or run '/login ${resolvedProvider}'.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn key;\n\t\t},\n\t});\n\n\t// Restore messages if session has existing data\n\tif (hasExistingSession) {\n\t\tagent.replaceMessages(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\textensionRunnerRef,\n\t\tuiType: options.uiType,\n\t});\n\tsessionRef.current = session;\n\tconst extensionsResult = resourceLoader.getExtensions();\n\n\t// Surface any resource diagnostics from initial load\n\tsession.warnResourceDiagnostics(resourceLoader);\n\n\t// Surface a loud warning for agentModels settings keys that reference\n\t// agents which no longer exist (typo or renamed/removed upstream agent),\n\t// since such overrides are otherwise silently ignored at resolution time.\n\tsession.warnStaleAgentModelKeys();\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":"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","sourcesContent":["import { join } from \"node:path\";\nimport { Agent, type AgentMessage, type ThinkingLevel } from \"@dreb/agent-core\";\nimport type { Message, Model } from \"@dreb/ai\";\nimport { getAgentDir, getDocsPath } from \"../config.js\";\nimport { AgentSession } from \"./agent-session.js\";\nimport { AuthStorage } from \"./auth-storage.js\";\nimport { estimateContextTokens } from \"./compaction/index.js\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.js\";\nimport type { ExtensionRunner, LoadExtensionsResult, ToolDefinition } from \"./extensions/index.js\";\nimport { deriveK3ContextTierModel } from \"./k3-context-tier.js\";\nimport { convertToLlm } from \"./messages.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { findInitialModel } from \"./model-resolver.js\";\nimport { configValueWarnings } from \"./resolve-config-value.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 { resolveEffectiveThinkingLevel, resolveThinkingDisplay } from \"./thinking.js\";\nimport { time } from \"./timings.js\";\nimport {\n\tallTools,\n\tbashTool,\n\tcodingTools,\n\tcreateBashTool,\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateSubagentTool,\n\tcreateWriteTool,\n\teditTool,\n\tfindTool,\n\tgetBackgroundAgents,\n\tgetRunningBackgroundAgents,\n\tgrepTool,\n\tlsTool,\n\tpruneBackgroundAgents,\n\treadOnlyTools,\n\treadTool,\n\tsubagentTool,\n\ttype Tool,\n\ttype ToolName,\n\twithFileMutationQueue,\n\twriteTool,\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: ~/.dreb/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: new ModelRegistry(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\n\t/** Built-in tools to use. Default: all standard tools [read, bash, edit, write, grep, find, ls, web_search, web_fetch, subagent, wait]. `skill`, `tasks_update`, and `search` are always active regardless of this setting. */\n\ttools?: Tool[];\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/** UI type for system prompt context (e.g. \"tui\", \"telegram\", \"rpc\") */\n\tuiType?: string;\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 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\t// Pre-built tools (use process.cwd())\n\treadTool,\n\tbashTool,\n\teditTool,\n\twriteTool,\n\tgrepTool,\n\tfindTool,\n\tlsTool,\n\tsubagentTool,\n\tcodingTools,\n\treadOnlyTools,\n\tallTools as allBuiltInTools,\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\tcreateSubagentTool,\n\t// Background agent registry\n\tgetBackgroundAgents,\n\tgetRunningBackgroundAgents,\n\tpruneBackgroundAgents,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n\treturn getAgentDir();\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 '@dreb/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: [readTool, bashTool],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst agentDir = options.agentDir ?? getDefaultAgentDir();\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 ?? new ModelRegistry(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\tconst hasApiKey = restoredModel ? !!(await modelRegistry.getApiKey(restoredModel)) : false;\n\t\tif (restoredModel && hasApiKey) {\n\t\t\tmodel = restoredModel;\n\t\t}\n\t\tif (!model) {\n\t\t\tconst reason = !restoredModel ? \"not found in registry\" : \"no API key available\";\n\t\t\tmodelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId} (${reason})`;\n\t\t\tconsole.warn(`[model-restore] ${modelFallbackMessage}`);\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: options.scopedModels ?? [],\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 = `No models available. Use /login or set an API key environment variable. See ${join(getDocsPath(), \"providers.md\")}. Then use /model to select a model.`;\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\tthinkingLevel = resolveEffectiveThinkingLevel(model, thinkingLevel);\n\tconst thinkingDisplay = resolveThinkingDisplay(\n\t\tmodel,\n\t\tmodel ? settingsManager.getModelThinkingDisplay(model.id) : undefined,\n\t);\n\n\t// Tools that are always active when available (created by factory, not in allTools singleton).\n\t// suggest_next is only auto-activated when tools aren't explicitly specified — subagent\n\t// child processes pass --tools which excludes suggest_next (it would end the turn mid-work).\n\tconst alwaysActiveBuiltins = options.tools\n\t\t? [\"skill\", \"tasks_update\", \"search\"]\n\t\t: [\"skill\", \"tasks_update\", \"search\", \"suggest_next\"];\n\tconst defaultActiveToolNames: ToolName[] = [\n\t\t\"read\",\n\t\t\"bash\",\n\t\t\"edit\",\n\t\t\"write\",\n\t\t\"grep\",\n\t\t\"find\",\n\t\t\"ls\",\n\t\t\"web_search\",\n\t\t\"web_fetch\",\n\t\t\"subagent\",\n\t\t\"wait\",\n\t\t\"ask_user\",\n\t];\n\tconst initialActiveToolNames: string[] = options.tools\n\t\t? [...options.tools.map((t) => t.name).filter((n): n is ToolName => n in allTools), ...alwaysActiveBuiltins]\n\t\t: [...defaultActiveToolNames, ...alwaysActiveBuiltins];\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\tconst sessionRef: { current?: AgentSession } = {};\n\n\tagent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt: \"\",\n\t\t\t// K3 auto context tier: new sessions start on the cheaper 256k wire\n\t\t\t// tier; a resumed session derives the tier from its restored context.\n\t\t\tmodel: deriveK3ContextTierModel(\n\t\t\t\tmodel,\n\t\t\t\thasExistingSession ? estimateContextTokens(existingSession.messages).tokens : 0,\n\t\t\t),\n\t\t\tthinkingLevel,\n\t\t\ttools: [],\n\t\t},\n\t\tconvertToLlm: convertToLlmWithBlockImages,\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\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\tthinkingDisplay,\n\t\tmaxRetryDelayMs: settingsManager.getRetrySettings().maxDelayMs,\n\t\tonWarning: (code: string, message: string) => {\n\t\t\t// Wire provider-level warnings to the session for user/agent visibility\n\t\t\tconst informational =\n\t\t\t\tcode === \"sse_parse_error\" || code === \"ws_parse_error\" || code === \"json_parse_total_failure\";\n\t\t\tsessionRef.current?.warnInSession(message, { informational });\n\t\t},\n\t\tgetApiKey: async (provider) => {\n\t\t\t// Use the provider argument from the in-flight request;\n\t\t\t// agent.state.model may already be switched mid-turn.\n\t\t\tconst resolvedProvider = provider || agent.state.model?.provider;\n\t\t\tif (!resolvedProvider) {\n\t\t\t\tthrow new Error(\"No model selected\");\n\t\t\t}\n\t\t\tconst key = await modelRegistry.getApiKeyForProvider(resolvedProvider);\n\t\t\t// Surface any config value resolution warnings (e.g. failed !command API keys)\n\t\t\tif (configValueWarnings.length > 0) {\n\t\t\t\tconst warnings = configValueWarnings.splice(0);\n\t\t\t\tfor (const w of warnings) {\n\t\t\t\t\tsessionRef.current?.warnInSession(w);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!key) {\n\t\t\t\tconst model = agent.state.model;\n\t\t\t\tconst isOAuth = model && modelRegistry.isUsingOAuth(model);\n\t\t\t\tif (isOAuth) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Authentication failed for \"${resolvedProvider}\". ` +\n\t\t\t\t\t\t\t`Credentials may have expired or network is unavailable. ` +\n\t\t\t\t\t\t\t`Run '/login ${resolvedProvider}' to re-authenticate.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No API key found for \"${resolvedProvider}\". ` +\n\t\t\t\t\t\t`Set an API key environment variable or run '/login ${resolvedProvider}'.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn key;\n\t\t},\n\t});\n\n\t// Restore messages if session has existing data\n\tif (hasExistingSession) {\n\t\tagent.replaceMessages(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\textensionRunnerRef,\n\t\tuiType: options.uiType,\n\t});\n\tsessionRef.current = session;\n\tconst extensionsResult = resourceLoader.getExtensions();\n\n\t// Surface any resource diagnostics from initial load\n\tsession.warnResourceDiagnostics(resourceLoader);\n\n\t// Surface a loud warning for agentModels settings keys that reference\n\t// agents which no longer exist (typo or renamed/removed upstream agent),\n\t// since such overrides are otherwise silently ignored at resolution time.\n\tsession.warnStaleAgentModelKeys();\n\n\treturn {\n\t\tsession,\n\t\textensionsResult,\n\t\tmodelFallbackMessage,\n\t};\n}\n"]}
|
package/dist/core/sdk.js
CHANGED
|
@@ -3,7 +3,9 @@ import { Agent } from "@dreb/agent-core";
|
|
|
3
3
|
import { getAgentDir, getDocsPath } from "../config.js";
|
|
4
4
|
import { AgentSession } from "./agent-session.js";
|
|
5
5
|
import { AuthStorage } from "./auth-storage.js";
|
|
6
|
+
import { estimateContextTokens } from "./compaction/index.js";
|
|
6
7
|
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
8
|
+
import { deriveK3ContextTierModel } from "./k3-context-tier.js";
|
|
7
9
|
import { convertToLlm } from "./messages.js";
|
|
8
10
|
import { ModelRegistry } from "./model-registry.js";
|
|
9
11
|
import { findInitialModel } from "./model-resolver.js";
|
|
@@ -186,7 +188,9 @@ export async function createAgentSession(options = {}) {
|
|
|
186
188
|
agent = new Agent({
|
|
187
189
|
initialState: {
|
|
188
190
|
systemPrompt: "",
|
|
189
|
-
|
|
191
|
+
// K3 auto context tier: new sessions start on the cheaper 256k wire
|
|
192
|
+
// tier; a resumed session derives the tier from its restored context.
|
|
193
|
+
model: deriveK3ContextTierModel(model, hasExistingSession ? estimateContextTokens(existingSession.messages).tokens : 0),
|
|
190
194
|
thinkingLevel,
|
|
191
195
|
tools: [],
|
|
192
196
|
},
|
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 \"@dreb/agent-core\";\nimport type { Message, Model } from \"@dreb/ai\";\nimport { getAgentDir, getDocsPath } from \"../config.js\";\nimport { AgentSession } from \"./agent-session.js\";\nimport { AuthStorage } from \"./auth-storage.js\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.js\";\nimport type { ExtensionRunner, LoadExtensionsResult, ToolDefinition } from \"./extensions/index.js\";\nimport { convertToLlm } from \"./messages.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { findInitialModel } from \"./model-resolver.js\";\nimport { configValueWarnings } from \"./resolve-config-value.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 { resolveEffectiveThinkingLevel, resolveThinkingDisplay } from \"./thinking.js\";\nimport { time } from \"./timings.js\";\nimport {\n\tallTools,\n\tbashTool,\n\tcodingTools,\n\tcreateBashTool,\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateSubagentTool,\n\tcreateWriteTool,\n\teditTool,\n\tfindTool,\n\tgetBackgroundAgents,\n\tgetRunningBackgroundAgents,\n\tgrepTool,\n\tlsTool,\n\tpruneBackgroundAgents,\n\treadOnlyTools,\n\treadTool,\n\tsubagentTool,\n\ttype Tool,\n\ttype ToolName,\n\twithFileMutationQueue,\n\twriteTool,\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: ~/.dreb/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: new ModelRegistry(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\n\t/** Built-in tools to use. Default: all standard tools [read, bash, edit, write, grep, find, ls, web_search, web_fetch, subagent, wait]. `skill`, `tasks_update`, and `search` are always active regardless of this setting. */\n\ttools?: Tool[];\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/** UI type for system prompt context (e.g. \"tui\", \"telegram\", \"rpc\") */\n\tuiType?: string;\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 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\t// Pre-built tools (use process.cwd())\n\treadTool,\n\tbashTool,\n\teditTool,\n\twriteTool,\n\tgrepTool,\n\tfindTool,\n\tlsTool,\n\tsubagentTool,\n\tcodingTools,\n\treadOnlyTools,\n\tallTools as allBuiltInTools,\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\tcreateSubagentTool,\n\t// Background agent registry\n\tgetBackgroundAgents,\n\tgetRunningBackgroundAgents,\n\tpruneBackgroundAgents,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n\treturn getAgentDir();\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 '@dreb/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: [readTool, bashTool],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst agentDir = options.agentDir ?? getDefaultAgentDir();\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 ?? new ModelRegistry(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\tconst hasApiKey = restoredModel ? !!(await modelRegistry.getApiKey(restoredModel)) : false;\n\t\tif (restoredModel && hasApiKey) {\n\t\t\tmodel = restoredModel;\n\t\t}\n\t\tif (!model) {\n\t\t\tconst reason = !restoredModel ? \"not found in registry\" : \"no API key available\";\n\t\t\tmodelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId} (${reason})`;\n\t\t\tconsole.warn(`[model-restore] ${modelFallbackMessage}`);\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: options.scopedModels ?? [],\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 = `No models available. Use /login or set an API key environment variable. See ${join(getDocsPath(), \"providers.md\")}. Then use /model to select a model.`;\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\tthinkingLevel = resolveEffectiveThinkingLevel(model, thinkingLevel);\n\tconst thinkingDisplay = resolveThinkingDisplay(\n\t\tmodel,\n\t\tmodel ? settingsManager.getModelThinkingDisplay(model.id) : undefined,\n\t);\n\n\t// Tools that are always active when available (created by factory, not in allTools singleton).\n\t// suggest_next is only auto-activated when tools aren't explicitly specified — subagent\n\t// child processes pass --tools which excludes suggest_next (it would end the turn mid-work).\n\tconst alwaysActiveBuiltins = options.tools\n\t\t? [\"skill\", \"tasks_update\", \"search\"]\n\t\t: [\"skill\", \"tasks_update\", \"search\", \"suggest_next\"];\n\tconst defaultActiveToolNames: ToolName[] = [\n\t\t\"read\",\n\t\t\"bash\",\n\t\t\"edit\",\n\t\t\"write\",\n\t\t\"grep\",\n\t\t\"find\",\n\t\t\"ls\",\n\t\t\"web_search\",\n\t\t\"web_fetch\",\n\t\t\"subagent\",\n\t\t\"wait\",\n\t\t\"ask_user\",\n\t];\n\tconst initialActiveToolNames: string[] = options.tools\n\t\t? [...options.tools.map((t) => t.name).filter((n): n is ToolName => n in allTools), ...alwaysActiveBuiltins]\n\t\t: [...defaultActiveToolNames, ...alwaysActiveBuiltins];\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\tconst sessionRef: { current?: AgentSession } = {};\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\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\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\tthinkingDisplay,\n\t\tmaxRetryDelayMs: settingsManager.getRetrySettings().maxDelayMs,\n\t\tonWarning: (code: string, message: string) => {\n\t\t\t// Wire provider-level warnings to the session for user/agent visibility\n\t\t\tconst informational =\n\t\t\t\tcode === \"sse_parse_error\" || code === \"ws_parse_error\" || code === \"json_parse_total_failure\";\n\t\t\tsessionRef.current?.warnInSession(message, { informational });\n\t\t},\n\t\tgetApiKey: async (provider) => {\n\t\t\t// Use the provider argument from the in-flight request;\n\t\t\t// agent.state.model may already be switched mid-turn.\n\t\t\tconst resolvedProvider = provider || agent.state.model?.provider;\n\t\t\tif (!resolvedProvider) {\n\t\t\t\tthrow new Error(\"No model selected\");\n\t\t\t}\n\t\t\tconst key = await modelRegistry.getApiKeyForProvider(resolvedProvider);\n\t\t\t// Surface any config value resolution warnings (e.g. failed !command API keys)\n\t\t\tif (configValueWarnings.length > 0) {\n\t\t\t\tconst warnings = configValueWarnings.splice(0);\n\t\t\t\tfor (const w of warnings) {\n\t\t\t\t\tsessionRef.current?.warnInSession(w);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!key) {\n\t\t\t\tconst model = agent.state.model;\n\t\t\t\tconst isOAuth = model && modelRegistry.isUsingOAuth(model);\n\t\t\t\tif (isOAuth) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Authentication failed for \"${resolvedProvider}\". ` +\n\t\t\t\t\t\t\t`Credentials may have expired or network is unavailable. ` +\n\t\t\t\t\t\t\t`Run '/login ${resolvedProvider}' to re-authenticate.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No API key found for \"${resolvedProvider}\". ` +\n\t\t\t\t\t\t`Set an API key environment variable or run '/login ${resolvedProvider}'.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn key;\n\t\t},\n\t});\n\n\t// Restore messages if session has existing data\n\tif (hasExistingSession) {\n\t\tagent.replaceMessages(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\textensionRunnerRef,\n\t\tuiType: options.uiType,\n\t});\n\tsessionRef.current = session;\n\tconst extensionsResult = resourceLoader.getExtensions();\n\n\t// Surface any resource diagnostics from initial load\n\tsession.warnResourceDiagnostics(resourceLoader);\n\n\t// Surface a loud warning for agentModels settings keys that reference\n\t// agents which no longer exist (typo or renamed/removed upstream agent),\n\t// since such overrides are otherwise silently ignored at resolution time.\n\tsession.warnStaleAgentModelKeys();\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 \"@dreb/agent-core\";\nimport type { Message, Model } from \"@dreb/ai\";\nimport { getAgentDir, getDocsPath } from \"../config.js\";\nimport { AgentSession } from \"./agent-session.js\";\nimport { AuthStorage } from \"./auth-storage.js\";\nimport { estimateContextTokens } from \"./compaction/index.js\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.js\";\nimport type { ExtensionRunner, LoadExtensionsResult, ToolDefinition } from \"./extensions/index.js\";\nimport { deriveK3ContextTierModel } from \"./k3-context-tier.js\";\nimport { convertToLlm } from \"./messages.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { findInitialModel } from \"./model-resolver.js\";\nimport { configValueWarnings } from \"./resolve-config-value.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 { resolveEffectiveThinkingLevel, resolveThinkingDisplay } from \"./thinking.js\";\nimport { time } from \"./timings.js\";\nimport {\n\tallTools,\n\tbashTool,\n\tcodingTools,\n\tcreateBashTool,\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateSubagentTool,\n\tcreateWriteTool,\n\teditTool,\n\tfindTool,\n\tgetBackgroundAgents,\n\tgetRunningBackgroundAgents,\n\tgrepTool,\n\tlsTool,\n\tpruneBackgroundAgents,\n\treadOnlyTools,\n\treadTool,\n\tsubagentTool,\n\ttype Tool,\n\ttype ToolName,\n\twithFileMutationQueue,\n\twriteTool,\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: ~/.dreb/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: new ModelRegistry(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\n\t/** Built-in tools to use. Default: all standard tools [read, bash, edit, write, grep, find, ls, web_search, web_fetch, subagent, wait]. `skill`, `tasks_update`, and `search` are always active regardless of this setting. */\n\ttools?: Tool[];\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/** UI type for system prompt context (e.g. \"tui\", \"telegram\", \"rpc\") */\n\tuiType?: string;\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 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\t// Pre-built tools (use process.cwd())\n\treadTool,\n\tbashTool,\n\teditTool,\n\twriteTool,\n\tgrepTool,\n\tfindTool,\n\tlsTool,\n\tsubagentTool,\n\tcodingTools,\n\treadOnlyTools,\n\tallTools as allBuiltInTools,\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\tcreateSubagentTool,\n\t// Background agent registry\n\tgetBackgroundAgents,\n\tgetRunningBackgroundAgents,\n\tpruneBackgroundAgents,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n\treturn getAgentDir();\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 '@dreb/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: [readTool, bashTool],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst agentDir = options.agentDir ?? getDefaultAgentDir();\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 ?? new ModelRegistry(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\tconst hasApiKey = restoredModel ? !!(await modelRegistry.getApiKey(restoredModel)) : false;\n\t\tif (restoredModel && hasApiKey) {\n\t\t\tmodel = restoredModel;\n\t\t}\n\t\tif (!model) {\n\t\t\tconst reason = !restoredModel ? \"not found in registry\" : \"no API key available\";\n\t\t\tmodelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId} (${reason})`;\n\t\t\tconsole.warn(`[model-restore] ${modelFallbackMessage}`);\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: options.scopedModels ?? [],\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 = `No models available. Use /login or set an API key environment variable. See ${join(getDocsPath(), \"providers.md\")}. Then use /model to select a model.`;\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\tthinkingLevel = resolveEffectiveThinkingLevel(model, thinkingLevel);\n\tconst thinkingDisplay = resolveThinkingDisplay(\n\t\tmodel,\n\t\tmodel ? settingsManager.getModelThinkingDisplay(model.id) : undefined,\n\t);\n\n\t// Tools that are always active when available (created by factory, not in allTools singleton).\n\t// suggest_next is only auto-activated when tools aren't explicitly specified — subagent\n\t// child processes pass --tools which excludes suggest_next (it would end the turn mid-work).\n\tconst alwaysActiveBuiltins = options.tools\n\t\t? [\"skill\", \"tasks_update\", \"search\"]\n\t\t: [\"skill\", \"tasks_update\", \"search\", \"suggest_next\"];\n\tconst defaultActiveToolNames: ToolName[] = [\n\t\t\"read\",\n\t\t\"bash\",\n\t\t\"edit\",\n\t\t\"write\",\n\t\t\"grep\",\n\t\t\"find\",\n\t\t\"ls\",\n\t\t\"web_search\",\n\t\t\"web_fetch\",\n\t\t\"subagent\",\n\t\t\"wait\",\n\t\t\"ask_user\",\n\t];\n\tconst initialActiveToolNames: string[] = options.tools\n\t\t? [...options.tools.map((t) => t.name).filter((n): n is ToolName => n in allTools), ...alwaysActiveBuiltins]\n\t\t: [...defaultActiveToolNames, ...alwaysActiveBuiltins];\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\tconst sessionRef: { current?: AgentSession } = {};\n\n\tagent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt: \"\",\n\t\t\t// K3 auto context tier: new sessions start on the cheaper 256k wire\n\t\t\t// tier; a resumed session derives the tier from its restored context.\n\t\t\tmodel: deriveK3ContextTierModel(\n\t\t\t\tmodel,\n\t\t\t\thasExistingSession ? estimateContextTokens(existingSession.messages).tokens : 0,\n\t\t\t),\n\t\t\tthinkingLevel,\n\t\t\ttools: [],\n\t\t},\n\t\tconvertToLlm: convertToLlmWithBlockImages,\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\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\tthinkingDisplay,\n\t\tmaxRetryDelayMs: settingsManager.getRetrySettings().maxDelayMs,\n\t\tonWarning: (code: string, message: string) => {\n\t\t\t// Wire provider-level warnings to the session for user/agent visibility\n\t\t\tconst informational =\n\t\t\t\tcode === \"sse_parse_error\" || code === \"ws_parse_error\" || code === \"json_parse_total_failure\";\n\t\t\tsessionRef.current?.warnInSession(message, { informational });\n\t\t},\n\t\tgetApiKey: async (provider) => {\n\t\t\t// Use the provider argument from the in-flight request;\n\t\t\t// agent.state.model may already be switched mid-turn.\n\t\t\tconst resolvedProvider = provider || agent.state.model?.provider;\n\t\t\tif (!resolvedProvider) {\n\t\t\t\tthrow new Error(\"No model selected\");\n\t\t\t}\n\t\t\tconst key = await modelRegistry.getApiKeyForProvider(resolvedProvider);\n\t\t\t// Surface any config value resolution warnings (e.g. failed !command API keys)\n\t\t\tif (configValueWarnings.length > 0) {\n\t\t\t\tconst warnings = configValueWarnings.splice(0);\n\t\t\t\tfor (const w of warnings) {\n\t\t\t\t\tsessionRef.current?.warnInSession(w);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!key) {\n\t\t\t\tconst model = agent.state.model;\n\t\t\t\tconst isOAuth = model && modelRegistry.isUsingOAuth(model);\n\t\t\t\tif (isOAuth) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Authentication failed for \"${resolvedProvider}\". ` +\n\t\t\t\t\t\t\t`Credentials may have expired or network is unavailable. ` +\n\t\t\t\t\t\t\t`Run '/login ${resolvedProvider}' to re-authenticate.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No API key found for \"${resolvedProvider}\". ` +\n\t\t\t\t\t\t`Set an API key environment variable or run '/login ${resolvedProvider}'.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn key;\n\t\t},\n\t});\n\n\t// Restore messages if session has existing data\n\tif (hasExistingSession) {\n\t\tagent.replaceMessages(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\textensionRunnerRef,\n\t\tuiType: options.uiType,\n\t});\n\tsessionRef.current = session;\n\tconst extensionsResult = resourceLoader.getExtensions();\n\n\t// Surface any resource diagnostics from initial load\n\tsession.warnResourceDiagnostics(resourceLoader);\n\n\t// Surface a loud warning for agentModels settings keys that reference\n\t// agents which no longer exist (typo or renamed/removed upstream agent),\n\t// since such overrides are otherwise silently ignored at resolution time.\n\tsession.warnStaleAgentModelKeys();\n\n\treturn {\n\t\tsession,\n\t\textensionsResult,\n\t\tmodelFallbackMessage,\n\t};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ask-user.d.ts","sourceRoot":"","sources":["../../../src/core/tools/ask-user.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAA2C,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAKtG,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,WAAW,EAAE,OAAO,CAAC;IACrB,yDAAyD;IACzD,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAKD,QAAA,MAAM,aAAa;;;;;;;;EAwCjB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AA0FxD;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,cAAc,CAAC,OAAO,aAAa,EAAE,cAAc,GAAG,SAAS,CAAC,CA+F9G","sourcesContent":["/**\n * ask_user tool.\n *\n * Lets the agent pause and ask the user a structured clarifying question —\n * with optional multiple-choice options, single- or multi-select, and a\n * \"type your own answer\" free-text field — rendered natively in the TUI, the\n * Dashboard, and over RPC. Answering, stopping the turn, aborting, or timing\n * out always settles cleanly so the agent never deadlocks on an absent user.\n *\n * Concurrent calls are serialized through a per-session FIFO queue: only one\n * question is ever shown at a time, and a queued call whose signal aborts\n * settles without opening any UI.\n */\n\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport type { AskRequest, AskResult, ExtensionContext, ToolDefinition } from \"../extensions/types.js\";\n\n// ============================================================================\n// Types\n\nexport interface AskUserDetails {\n\tquestion: string;\n\ttitle?: string;\n\tselected: string[];\n\tcustomText?: string;\n\t/** True when the question closed without an answer. */\n\tskipped: boolean;\n\t/** True when no interactive UI was available (headless/print mode). */\n\tunavailable: boolean;\n\t/** True when the UI host or response protocol failed. */\n\tfailed?: boolean;\n}\n\n// ============================================================================\n// Schema\n\nconst askUserSchema = Type.Object({\n\tquestion: Type.String({\n\t\tdescription: \"The Markdown-formatted question to ask the user. Be specific about what you need to decide.\",\n\t}),\n\ttitle: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Short bold header shown above the question.\",\n\t\t}),\n\t),\n\toptions: Type.Optional(\n\t\tType.Array(Type.String({ minLength: 1, pattern: \"\\\\S\" }), {\n\t\t\tminItems: 2,\n\t\t\tmaxItems: 4,\n\t\t\tdescription: \"2-4 nonblank suggested answers the user can pick from.\",\n\t\t}),\n\t),\n\tallowFreeText: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Offer a 'type your own answer' field. Defaults to true.\",\n\t\t}),\n\t),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Allow selecting multiple options (checkboxes). Only meaningful with options.\",\n\t\t}),\n\t),\n\tmultiline: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Use a large multi-line text area for open-ended answers.\",\n\t\t}),\n\t),\n\ttimeoutSeconds: Type.Optional(\n\t\tType.Number({\n\t\t\tminimum: 5,\n\t\t\tmaximum: 3600,\n\t\t\tdescription:\n\t\t\t\t\"Optional: stop the current agent turn after this many seconds if the user does not respond. \" +\n\t\t\t\t\"Shows a live countdown. Omit to wait indefinitely.\",\n\t\t}),\n\t),\n});\n\nexport type AskUserInput = Static<typeof askUserSchema>;\n\n// ============================================================================\n// Result helpers\n\nfunction textResult(text: string, details: AskUserDetails) {\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text }],\n\t\tdetails,\n\t};\n}\n\nfunction baseDetails(input: AskUserInput): Omit<AskUserDetails, \"selected\" | \"skipped\" | \"unavailable\"> {\n\treturn { question: input.question, title: input.title };\n}\n\nfunction unavailableResult(input: AskUserInput) {\n\treturn textResult(\n\t\t\"The ask_user tool requires an interactive UI, which is not available in this mode. \" +\n\t\t\t\"Proceed using your best judgment without waiting for an answer.\",\n\t\t{ ...baseDetails(input), selected: [], skipped: true, unavailable: true },\n\t);\n}\n\nfunction unansweredResult(input: AskUserInput) {\n\treturn textResult(\"The question closed without an answer.\", {\n\t\t...baseDetails(input),\n\t\tselected: [],\n\t\tskipped: true,\n\t\tunavailable: false,\n\t});\n}\n\nfunction failedResult(input: AskUserInput) {\n\treturn textResult(\n\t\t\"The question could not be delivered because the interactive UI or response protocol failed. \" +\n\t\t\t\"Continue without this input.\",\n\t\t{\n\t\t\t...baseDetails(input),\n\t\t\tselected: [],\n\t\t\tskipped: false,\n\t\t\tunavailable: false,\n\t\t\tfailed: true,\n\t\t},\n\t);\n}\n\nfunction answeredResult(input: AskUserInput, answer: AskResult) {\n\tconst customText = answer.customText?.trim() || undefined;\n\tconst selected = answer.selected;\n\tconst parts: string[] = [];\n\tif (selected.length > 0) {\n\t\tparts.push(\n\t\t\tselected.length === 1 ? `The user selected: ${selected[0]}` : `The user selected: ${selected.join(\", \")}`,\n\t\t);\n\t}\n\tif (customText) {\n\t\tparts.push(selected.length > 0 ? `They also wrote: \"${customText}\"` : `The user answered: \"${customText}\"`);\n\t}\n\treturn textResult(parts.join(\" \"), {\n\t\t...baseDetails(input),\n\t\tselected,\n\t\tcustomText,\n\t\tskipped: false,\n\t\tunavailable: false,\n\t});\n}\n\n// ============================================================================\n// Render helpers\n\nfunction formatCall(args: { question?: string; title?: string } | undefined, theme: any): string {\n\tconst label = (args?.title || args?.question || \"\").replace(/\\s+/g, \" \").trim();\n\tconst shown = label.length > 80 ? `${label.slice(0, 79)}…` : label;\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"ask_user\"))} ${theme.fg(\"accent\", shown)}`;\n}\n\nfunction formatResult(details: AskUserDetails, theme: any): string {\n\tif (details.unavailable) return theme.fg(\"toolOutput\", \"no interactive UI — continued without asking\");\n\tif (details.failed) return theme.fg(\"toolOutput\", \"interactive UI failed — continued without an answer\");\n\tif (details.skipped) return theme.fg(\"toolOutput\", \"question closed without an answer\");\n\tconst parts: string[] = [];\n\tif (details.selected.length > 0) parts.push(details.selected.join(\", \"));\n\tif (details.customText) parts.push(`\"${details.customText}\"`);\n\treturn theme.fg(\"toolOutput\", `→ ${parts.join(\" + \")}`);\n}\n\n// ============================================================================\n// Tool definition factory\n\n/**\n * Create an `ask_user` tool definition. Each call creates an isolated FIFO\n * queue, so concurrent `ask_user` calls in a single session are shown strictly\n * one at a time.\n */\nexport function createAskUserToolDefinition(): ToolDefinition<typeof askUserSchema, AskUserDetails | undefined> {\n\t// Per-session serialization: only one question is ever open at a time.\n\tlet tail: Promise<void> = Promise.resolve();\n\tconst serialize = <T>(run: () => Promise<T>): Promise<T> => {\n\t\tconst result = tail.then(run, run);\n\t\t// Always advance the queue, whether the call resolved, cancelled, timed\n\t\t// out, or threw — so a failure can never wedge later questions.\n\t\ttail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t};\n\n\treturn {\n\t\tname: \"ask_user\",\n\t\tlabel: \"ask_user\",\n\t\tdescription:\n\t\t\t\"Pause and ask the user a structured clarifying question with optional multiple-choice options and a \" +\n\t\t\t\"free-text answer. Use only when genuinely blocked by ambiguity with multiple viable paths — not for routine confirmation.\",\n\n\t\tparameters: askUserSchema,\n\n\t\tpromptSnippet: \"Ask the user a clarifying question with optional multiple-choice options and free text\",\n\n\t\tpromptGuidelines: [\n\t\t\t\"Call ask_user ONLY when you are genuinely blocked by ambiguity and there are multiple viable paths forward\",\n\t\t\t\"Do NOT use it for routine confirmation, permission, or things you can reasonably decide yourself\",\n\t\t\t\"Provide 2-4 concrete `options` when there are clear candidate answers; the user can always type their own\",\n\t\t\t\"Set `multiSelect: true` when several options can be combined; `multiline: true` for open-ended answers\",\n\t\t\t\"The user may stop the current turn instead of answering; never treat that as an answer\",\n\t\t\t\"Prefer one focused question over many; the question blocks the turn until the user responds or stops it\",\n\t\t],\n\n\t\tasync execute(_toolCallId, input: AskUserInput, signal, _onUpdate, ctx?: ExtensionContext) {\n\t\t\tconst hasOptions = (input.options?.length ?? 0) > 0;\n\t\t\tconst request: AskRequest = {\n\t\t\t\tquestion: input.question,\n\t\t\t\ttitle: input.title,\n\t\t\t\toptions: input.options,\n\t\t\t\t// Guarantee at least one answer control: with no options, free text\n\t\t\t\t// must be offered regardless of the requested flag, otherwise both\n\t\t\t\t// surfaces would render only a Skip button and no way to answer.\n\t\t\t\tallowFreeText: hasOptions ? input.allowFreeText : true,\n\t\t\t\t// multiSelect is only meaningful with options; multiline only with\n\t\t\t\t// free text — normalize away impossible combinations.\n\t\t\t\tmultiSelect: hasOptions ? input.multiSelect : undefined,\n\t\t\t\tmultiline: hasOptions ? (input.allowFreeText === false ? undefined : input.multiline) : input.multiline,\n\t\t\t};\n\n\t\t\t// Optional auto-stop timeout, forwarded to every UI surface (TUI\n\t\t\t// countdown, RPC/Dashboard). Model-facing units are seconds.\n\t\t\tconst timeout = input.timeoutSeconds && input.timeoutSeconds > 0 ? input.timeoutSeconds * 1000 : undefined;\n\n\t\t\t// Headless / print / no-host modes: never block on an unreachable UI.\n\t\t\tif (!ctx?.hasUI) {\n\t\t\t\treturn unavailableResult(input);\n\t\t\t}\n\n\t\t\treturn serialize(async () => {\n\t\t\t\t// A queued call whose signal already aborted settles without ever\n\t\t\t\t// opening the UI; the parent turn is already stopping.\n\t\t\t\tif (signal?.aborted) return unansweredResult(input);\n\t\t\t\ttry {\n\t\t\t\t\tconst answer = await ctx.ui.ask(request, { signal, timeout });\n\t\t\t\t\tif (!answer || (answer.selected.length === 0 && !answer.customText?.trim())) {\n\t\t\t\t\t\treturn unansweredResult(input);\n\t\t\t\t\t}\n\t\t\t\t\treturn answeredResult(input, answer);\n\t\t\t\t} catch {\n\t\t\t\t\t// Host/protocol failure must still release the queue and never\n\t\t\t\t\t// deadlock, but it must not masquerade as an intentional user skip.\n\t\t\t\t\treturn failedResult(input);\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\ttext.setText(formatCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\tconst details = (result as any).details as AskUserDetails | undefined;\n\t\t\tif (details) {\n\t\t\t\ttext.setText(formatResult(details, theme));\n\t\t\t} else {\n\t\t\t\tconst content = result.content?.[0];\n\t\t\t\ttext.setText(theme.fg(\"toolOutput\", content?.type === \"text\" ? content.text : \"\"));\n\t\t\t}\n\t\t\treturn text;\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ask-user.d.ts","sourceRoot":"","sources":["../../../src/core/tools/ask-user.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAA2C,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAKtG,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,WAAW,EAAE,OAAO,CAAC;IACrB,yDAAyD;IACzD,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAKD,QAAA,MAAM,aAAa;;;;;;;;EAwCjB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AA0FxD;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,cAAc,CAAC,OAAO,aAAa,EAAE,cAAc,GAAG,SAAS,CAAC,CA+F9G","sourcesContent":["/**\n * ask_user tool.\n *\n * Lets the agent pause and ask the user a structured clarifying question —\n * with optional multiple-choice options, single- or multi-select, and a\n * \"type your own answer\" free-text field — rendered natively in the TUI, the\n * Dashboard, and over RPC. Answering, stopping the turn, aborting, or timing\n * out always settles cleanly so the agent never deadlocks on an absent user.\n *\n * Concurrent calls are serialized through a per-session FIFO queue: only one\n * question is ever shown at a time, and a queued call whose signal aborts\n * settles without opening any UI.\n */\n\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport type { AskRequest, AskResult, ExtensionContext, ToolDefinition } from \"../extensions/types.js\";\n\n// ============================================================================\n// Types\n\nexport interface AskUserDetails {\n\tquestion: string;\n\ttitle?: string;\n\tselected: string[];\n\tcustomText?: string;\n\t/** True when the question closed without an answer. */\n\tskipped: boolean;\n\t/** True when no interactive UI was available (headless/print mode). */\n\tunavailable: boolean;\n\t/** True when the UI host or response protocol failed. */\n\tfailed?: boolean;\n}\n\n// ============================================================================\n// Schema\n\nconst askUserSchema = Type.Object({\n\tquestion: Type.String({\n\t\tdescription: \"The Markdown-formatted question to ask the user. Be specific about what you need to decide.\",\n\t}),\n\ttitle: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Short bold header shown above the question.\",\n\t\t}),\n\t),\n\toptions: Type.Optional(\n\t\tType.Array(Type.String({ minLength: 1, pattern: \"^.*[^ \\\\t\\\\r\\\\n].*$\" }), {\n\t\t\tminItems: 2,\n\t\t\tmaxItems: 4,\n\t\t\tdescription: \"2-4 nonblank suggested answers the user can pick from.\",\n\t\t}),\n\t),\n\tallowFreeText: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Offer a 'type your own answer' field. Defaults to true.\",\n\t\t}),\n\t),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Allow selecting multiple options (checkboxes). Only meaningful with options.\",\n\t\t}),\n\t),\n\tmultiline: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Use a large multi-line text area for open-ended answers.\",\n\t\t}),\n\t),\n\ttimeoutSeconds: Type.Optional(\n\t\tType.Number({\n\t\t\tminimum: 5,\n\t\t\tmaximum: 3600,\n\t\t\tdescription:\n\t\t\t\t\"Optional: stop the current agent turn after this many seconds if the user does not respond. \" +\n\t\t\t\t\"Shows a live countdown. Omit to wait indefinitely.\",\n\t\t}),\n\t),\n});\n\nexport type AskUserInput = Static<typeof askUserSchema>;\n\n// ============================================================================\n// Result helpers\n\nfunction textResult(text: string, details: AskUserDetails) {\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text }],\n\t\tdetails,\n\t};\n}\n\nfunction baseDetails(input: AskUserInput): Omit<AskUserDetails, \"selected\" | \"skipped\" | \"unavailable\"> {\n\treturn { question: input.question, title: input.title };\n}\n\nfunction unavailableResult(input: AskUserInput) {\n\treturn textResult(\n\t\t\"The ask_user tool requires an interactive UI, which is not available in this mode. \" +\n\t\t\t\"Proceed using your best judgment without waiting for an answer.\",\n\t\t{ ...baseDetails(input), selected: [], skipped: true, unavailable: true },\n\t);\n}\n\nfunction unansweredResult(input: AskUserInput) {\n\treturn textResult(\"The question closed without an answer.\", {\n\t\t...baseDetails(input),\n\t\tselected: [],\n\t\tskipped: true,\n\t\tunavailable: false,\n\t});\n}\n\nfunction failedResult(input: AskUserInput) {\n\treturn textResult(\n\t\t\"The question could not be delivered because the interactive UI or response protocol failed. \" +\n\t\t\t\"Continue without this input.\",\n\t\t{\n\t\t\t...baseDetails(input),\n\t\t\tselected: [],\n\t\t\tskipped: false,\n\t\t\tunavailable: false,\n\t\t\tfailed: true,\n\t\t},\n\t);\n}\n\nfunction answeredResult(input: AskUserInput, answer: AskResult) {\n\tconst customText = answer.customText?.trim() || undefined;\n\tconst selected = answer.selected;\n\tconst parts: string[] = [];\n\tif (selected.length > 0) {\n\t\tparts.push(\n\t\t\tselected.length === 1 ? `The user selected: ${selected[0]}` : `The user selected: ${selected.join(\", \")}`,\n\t\t);\n\t}\n\tif (customText) {\n\t\tparts.push(selected.length > 0 ? `They also wrote: \"${customText}\"` : `The user answered: \"${customText}\"`);\n\t}\n\treturn textResult(parts.join(\" \"), {\n\t\t...baseDetails(input),\n\t\tselected,\n\t\tcustomText,\n\t\tskipped: false,\n\t\tunavailable: false,\n\t});\n}\n\n// ============================================================================\n// Render helpers\n\nfunction formatCall(args: { question?: string; title?: string } | undefined, theme: any): string {\n\tconst label = (args?.title || args?.question || \"\").replace(/\\s+/g, \" \").trim();\n\tconst shown = label.length > 80 ? `${label.slice(0, 79)}…` : label;\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"ask_user\"))} ${theme.fg(\"accent\", shown)}`;\n}\n\nfunction formatResult(details: AskUserDetails, theme: any): string {\n\tif (details.unavailable) return theme.fg(\"toolOutput\", \"no interactive UI — continued without asking\");\n\tif (details.failed) return theme.fg(\"toolOutput\", \"interactive UI failed — continued without an answer\");\n\tif (details.skipped) return theme.fg(\"toolOutput\", \"question closed without an answer\");\n\tconst parts: string[] = [];\n\tif (details.selected.length > 0) parts.push(details.selected.join(\", \"));\n\tif (details.customText) parts.push(`\"${details.customText}\"`);\n\treturn theme.fg(\"toolOutput\", `→ ${parts.join(\" + \")}`);\n}\n\n// ============================================================================\n// Tool definition factory\n\n/**\n * Create an `ask_user` tool definition. Each call creates an isolated FIFO\n * queue, so concurrent `ask_user` calls in a single session are shown strictly\n * one at a time.\n */\nexport function createAskUserToolDefinition(): ToolDefinition<typeof askUserSchema, AskUserDetails | undefined> {\n\t// Per-session serialization: only one question is ever open at a time.\n\tlet tail: Promise<void> = Promise.resolve();\n\tconst serialize = <T>(run: () => Promise<T>): Promise<T> => {\n\t\tconst result = tail.then(run, run);\n\t\t// Always advance the queue, whether the call resolved, cancelled, timed\n\t\t// out, or threw — so a failure can never wedge later questions.\n\t\ttail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t};\n\n\treturn {\n\t\tname: \"ask_user\",\n\t\tlabel: \"ask_user\",\n\t\tdescription:\n\t\t\t\"Pause and ask the user a structured clarifying question with optional multiple-choice options and a \" +\n\t\t\t\"free-text answer. Use only when genuinely blocked by ambiguity with multiple viable paths — not for routine confirmation.\",\n\n\t\tparameters: askUserSchema,\n\n\t\tpromptSnippet: \"Ask the user a clarifying question with optional multiple-choice options and free text\",\n\n\t\tpromptGuidelines: [\n\t\t\t\"Call ask_user ONLY when you are genuinely blocked by ambiguity and there are multiple viable paths forward\",\n\t\t\t\"Do NOT use it for routine confirmation, permission, or things you can reasonably decide yourself\",\n\t\t\t\"Provide 2-4 concrete `options` when there are clear candidate answers; the user can always type their own\",\n\t\t\t\"Set `multiSelect: true` when several options can be combined; `multiline: true` for open-ended answers\",\n\t\t\t\"The user may stop the current turn instead of answering; never treat that as an answer\",\n\t\t\t\"Prefer one focused question over many; the question blocks the turn until the user responds or stops it\",\n\t\t],\n\n\t\tasync execute(_toolCallId, input: AskUserInput, signal, _onUpdate, ctx?: ExtensionContext) {\n\t\t\tconst hasOptions = (input.options?.length ?? 0) > 0;\n\t\t\tconst request: AskRequest = {\n\t\t\t\tquestion: input.question,\n\t\t\t\ttitle: input.title,\n\t\t\t\toptions: input.options,\n\t\t\t\t// Guarantee at least one answer control: with no options, free text\n\t\t\t\t// must be offered regardless of the requested flag, otherwise both\n\t\t\t\t// surfaces would render only a Skip button and no way to answer.\n\t\t\t\tallowFreeText: hasOptions ? input.allowFreeText : true,\n\t\t\t\t// multiSelect is only meaningful with options; multiline only with\n\t\t\t\t// free text — normalize away impossible combinations.\n\t\t\t\tmultiSelect: hasOptions ? input.multiSelect : undefined,\n\t\t\t\tmultiline: hasOptions ? (input.allowFreeText === false ? undefined : input.multiline) : input.multiline,\n\t\t\t};\n\n\t\t\t// Optional auto-stop timeout, forwarded to every UI surface (TUI\n\t\t\t// countdown, RPC/Dashboard). Model-facing units are seconds.\n\t\t\tconst timeout = input.timeoutSeconds && input.timeoutSeconds > 0 ? input.timeoutSeconds * 1000 : undefined;\n\n\t\t\t// Headless / print / no-host modes: never block on an unreachable UI.\n\t\t\tif (!ctx?.hasUI) {\n\t\t\t\treturn unavailableResult(input);\n\t\t\t}\n\n\t\t\treturn serialize(async () => {\n\t\t\t\t// A queued call whose signal already aborted settles without ever\n\t\t\t\t// opening the UI; the parent turn is already stopping.\n\t\t\t\tif (signal?.aborted) return unansweredResult(input);\n\t\t\t\ttry {\n\t\t\t\t\tconst answer = await ctx.ui.ask(request, { signal, timeout });\n\t\t\t\t\tif (!answer || (answer.selected.length === 0 && !answer.customText?.trim())) {\n\t\t\t\t\t\treturn unansweredResult(input);\n\t\t\t\t\t}\n\t\t\t\t\treturn answeredResult(input, answer);\n\t\t\t\t} catch {\n\t\t\t\t\t// Host/protocol failure must still release the queue and never\n\t\t\t\t\t// deadlock, but it must not masquerade as an intentional user skip.\n\t\t\t\t\treturn failedResult(input);\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\ttext.setText(formatCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\tconst details = (result as any).details as AskUserDetails | undefined;\n\t\t\tif (details) {\n\t\t\t\ttext.setText(formatResult(details, theme));\n\t\t\t} else {\n\t\t\t\tconst content = result.content?.[0];\n\t\t\t\ttext.setText(theme.fg(\"toolOutput\", content?.type === \"text\" ? content.text : \"\"));\n\t\t\t}\n\t\t\treturn text;\n\t\t},\n\t};\n}\n"]}
|
|
@@ -22,7 +22,7 @@ const askUserSchema = Type.Object({
|
|
|
22
22
|
title: Type.Optional(Type.String({
|
|
23
23
|
description: "Short bold header shown above the question.",
|
|
24
24
|
})),
|
|
25
|
-
options: Type.Optional(Type.Array(Type.String({ minLength: 1, pattern: "\\
|
|
25
|
+
options: Type.Optional(Type.Array(Type.String({ minLength: 1, pattern: "^.*[^ \\t\\r\\n].*$" }), {
|
|
26
26
|
minItems: 2,
|
|
27
27
|
maxItems: 4,
|
|
28
28
|
description: "2-4 nonblank suggested answers the user can pick from.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ask-user.js","sourceRoot":"","sources":["../../../src/core/tools/ask-user.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ask_user tool.\n *\n * Lets the agent pause and ask the user a structured clarifying question —\n * with optional multiple-choice options, single- or multi-select, and a\n * \"type your own answer\" free-text field — rendered natively in the TUI, the\n * Dashboard, and over RPC. Answering, stopping the turn, aborting, or timing\n * out always settles cleanly so the agent never deadlocks on an absent user.\n *\n * Concurrent calls are serialized through a per-session FIFO queue: only one\n * question is ever shown at a time, and a queued call whose signal aborts\n * settles without opening any UI.\n */\n\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport type { AskRequest, AskResult, ExtensionContext, ToolDefinition } from \"../extensions/types.js\";\n\n// ============================================================================\n// Types\n\nexport interface AskUserDetails {\n\tquestion: string;\n\ttitle?: string;\n\tselected: string[];\n\tcustomText?: string;\n\t/** True when the question closed without an answer. */\n\tskipped: boolean;\n\t/** True when no interactive UI was available (headless/print mode). */\n\tunavailable: boolean;\n\t/** True when the UI host or response protocol failed. */\n\tfailed?: boolean;\n}\n\n// ============================================================================\n// Schema\n\nconst askUserSchema = Type.Object({\n\tquestion: Type.String({\n\t\tdescription: \"The Markdown-formatted question to ask the user. Be specific about what you need to decide.\",\n\t}),\n\ttitle: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Short bold header shown above the question.\",\n\t\t}),\n\t),\n\toptions: Type.Optional(\n\t\tType.Array(Type.String({ minLength: 1, pattern: \"\\\\S\" }), {\n\t\t\tminItems: 2,\n\t\t\tmaxItems: 4,\n\t\t\tdescription: \"2-4 nonblank suggested answers the user can pick from.\",\n\t\t}),\n\t),\n\tallowFreeText: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Offer a 'type your own answer' field. Defaults to true.\",\n\t\t}),\n\t),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Allow selecting multiple options (checkboxes). Only meaningful with options.\",\n\t\t}),\n\t),\n\tmultiline: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Use a large multi-line text area for open-ended answers.\",\n\t\t}),\n\t),\n\ttimeoutSeconds: Type.Optional(\n\t\tType.Number({\n\t\t\tminimum: 5,\n\t\t\tmaximum: 3600,\n\t\t\tdescription:\n\t\t\t\t\"Optional: stop the current agent turn after this many seconds if the user does not respond. \" +\n\t\t\t\t\"Shows a live countdown. Omit to wait indefinitely.\",\n\t\t}),\n\t),\n});\n\nexport type AskUserInput = Static<typeof askUserSchema>;\n\n// ============================================================================\n// Result helpers\n\nfunction textResult(text: string, details: AskUserDetails) {\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text }],\n\t\tdetails,\n\t};\n}\n\nfunction baseDetails(input: AskUserInput): Omit<AskUserDetails, \"selected\" | \"skipped\" | \"unavailable\"> {\n\treturn { question: input.question, title: input.title };\n}\n\nfunction unavailableResult(input: AskUserInput) {\n\treturn textResult(\n\t\t\"The ask_user tool requires an interactive UI, which is not available in this mode. \" +\n\t\t\t\"Proceed using your best judgment without waiting for an answer.\",\n\t\t{ ...baseDetails(input), selected: [], skipped: true, unavailable: true },\n\t);\n}\n\nfunction unansweredResult(input: AskUserInput) {\n\treturn textResult(\"The question closed without an answer.\", {\n\t\t...baseDetails(input),\n\t\tselected: [],\n\t\tskipped: true,\n\t\tunavailable: false,\n\t});\n}\n\nfunction failedResult(input: AskUserInput) {\n\treturn textResult(\n\t\t\"The question could not be delivered because the interactive UI or response protocol failed. \" +\n\t\t\t\"Continue without this input.\",\n\t\t{\n\t\t\t...baseDetails(input),\n\t\t\tselected: [],\n\t\t\tskipped: false,\n\t\t\tunavailable: false,\n\t\t\tfailed: true,\n\t\t},\n\t);\n}\n\nfunction answeredResult(input: AskUserInput, answer: AskResult) {\n\tconst customText = answer.customText?.trim() || undefined;\n\tconst selected = answer.selected;\n\tconst parts: string[] = [];\n\tif (selected.length > 0) {\n\t\tparts.push(\n\t\t\tselected.length === 1 ? `The user selected: ${selected[0]}` : `The user selected: ${selected.join(\", \")}`,\n\t\t);\n\t}\n\tif (customText) {\n\t\tparts.push(selected.length > 0 ? `They also wrote: \"${customText}\"` : `The user answered: \"${customText}\"`);\n\t}\n\treturn textResult(parts.join(\" \"), {\n\t\t...baseDetails(input),\n\t\tselected,\n\t\tcustomText,\n\t\tskipped: false,\n\t\tunavailable: false,\n\t});\n}\n\n// ============================================================================\n// Render helpers\n\nfunction formatCall(args: { question?: string; title?: string } | undefined, theme: any): string {\n\tconst label = (args?.title || args?.question || \"\").replace(/\\s+/g, \" \").trim();\n\tconst shown = label.length > 80 ? `${label.slice(0, 79)}…` : label;\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"ask_user\"))} ${theme.fg(\"accent\", shown)}`;\n}\n\nfunction formatResult(details: AskUserDetails, theme: any): string {\n\tif (details.unavailable) return theme.fg(\"toolOutput\", \"no interactive UI — continued without asking\");\n\tif (details.failed) return theme.fg(\"toolOutput\", \"interactive UI failed — continued without an answer\");\n\tif (details.skipped) return theme.fg(\"toolOutput\", \"question closed without an answer\");\n\tconst parts: string[] = [];\n\tif (details.selected.length > 0) parts.push(details.selected.join(\", \"));\n\tif (details.customText) parts.push(`\"${details.customText}\"`);\n\treturn theme.fg(\"toolOutput\", `→ ${parts.join(\" + \")}`);\n}\n\n// ============================================================================\n// Tool definition factory\n\n/**\n * Create an `ask_user` tool definition. Each call creates an isolated FIFO\n * queue, so concurrent `ask_user` calls in a single session are shown strictly\n * one at a time.\n */\nexport function createAskUserToolDefinition(): ToolDefinition<typeof askUserSchema, AskUserDetails | undefined> {\n\t// Per-session serialization: only one question is ever open at a time.\n\tlet tail: Promise<void> = Promise.resolve();\n\tconst serialize = <T>(run: () => Promise<T>): Promise<T> => {\n\t\tconst result = tail.then(run, run);\n\t\t// Always advance the queue, whether the call resolved, cancelled, timed\n\t\t// out, or threw — so a failure can never wedge later questions.\n\t\ttail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t};\n\n\treturn {\n\t\tname: \"ask_user\",\n\t\tlabel: \"ask_user\",\n\t\tdescription:\n\t\t\t\"Pause and ask the user a structured clarifying question with optional multiple-choice options and a \" +\n\t\t\t\"free-text answer. Use only when genuinely blocked by ambiguity with multiple viable paths — not for routine confirmation.\",\n\n\t\tparameters: askUserSchema,\n\n\t\tpromptSnippet: \"Ask the user a clarifying question with optional multiple-choice options and free text\",\n\n\t\tpromptGuidelines: [\n\t\t\t\"Call ask_user ONLY when you are genuinely blocked by ambiguity and there are multiple viable paths forward\",\n\t\t\t\"Do NOT use it for routine confirmation, permission, or things you can reasonably decide yourself\",\n\t\t\t\"Provide 2-4 concrete `options` when there are clear candidate answers; the user can always type their own\",\n\t\t\t\"Set `multiSelect: true` when several options can be combined; `multiline: true` for open-ended answers\",\n\t\t\t\"The user may stop the current turn instead of answering; never treat that as an answer\",\n\t\t\t\"Prefer one focused question over many; the question blocks the turn until the user responds or stops it\",\n\t\t],\n\n\t\tasync execute(_toolCallId, input: AskUserInput, signal, _onUpdate, ctx?: ExtensionContext) {\n\t\t\tconst hasOptions = (input.options?.length ?? 0) > 0;\n\t\t\tconst request: AskRequest = {\n\t\t\t\tquestion: input.question,\n\t\t\t\ttitle: input.title,\n\t\t\t\toptions: input.options,\n\t\t\t\t// Guarantee at least one answer control: with no options, free text\n\t\t\t\t// must be offered regardless of the requested flag, otherwise both\n\t\t\t\t// surfaces would render only a Skip button and no way to answer.\n\t\t\t\tallowFreeText: hasOptions ? input.allowFreeText : true,\n\t\t\t\t// multiSelect is only meaningful with options; multiline only with\n\t\t\t\t// free text — normalize away impossible combinations.\n\t\t\t\tmultiSelect: hasOptions ? input.multiSelect : undefined,\n\t\t\t\tmultiline: hasOptions ? (input.allowFreeText === false ? undefined : input.multiline) : input.multiline,\n\t\t\t};\n\n\t\t\t// Optional auto-stop timeout, forwarded to every UI surface (TUI\n\t\t\t// countdown, RPC/Dashboard). Model-facing units are seconds.\n\t\t\tconst timeout = input.timeoutSeconds && input.timeoutSeconds > 0 ? input.timeoutSeconds * 1000 : undefined;\n\n\t\t\t// Headless / print / no-host modes: never block on an unreachable UI.\n\t\t\tif (!ctx?.hasUI) {\n\t\t\t\treturn unavailableResult(input);\n\t\t\t}\n\n\t\t\treturn serialize(async () => {\n\t\t\t\t// A queued call whose signal already aborted settles without ever\n\t\t\t\t// opening the UI; the parent turn is already stopping.\n\t\t\t\tif (signal?.aborted) return unansweredResult(input);\n\t\t\t\ttry {\n\t\t\t\t\tconst answer = await ctx.ui.ask(request, { signal, timeout });\n\t\t\t\t\tif (!answer || (answer.selected.length === 0 && !answer.customText?.trim())) {\n\t\t\t\t\t\treturn unansweredResult(input);\n\t\t\t\t\t}\n\t\t\t\t\treturn answeredResult(input, answer);\n\t\t\t\t} catch {\n\t\t\t\t\t// Host/protocol failure must still release the queue and never\n\t\t\t\t\t// deadlock, but it must not masquerade as an intentional user skip.\n\t\t\t\t\treturn failedResult(input);\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\ttext.setText(formatCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\tconst details = (result as any).details as AskUserDetails | undefined;\n\t\t\tif (details) {\n\t\t\t\ttext.setText(formatResult(details, theme));\n\t\t\t} else {\n\t\t\t\tconst content = result.content?.[0];\n\t\t\t\ttext.setText(theme.fg(\"toolOutput\", content?.type === \"text\" ? content.text : \"\"));\n\t\t\t}\n\t\t\treturn text;\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ask-user.js","sourceRoot":"","sources":["../../../src/core/tools/ask-user.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ask_user tool.\n *\n * Lets the agent pause and ask the user a structured clarifying question —\n * with optional multiple-choice options, single- or multi-select, and a\n * \"type your own answer\" free-text field — rendered natively in the TUI, the\n * Dashboard, and over RPC. Answering, stopping the turn, aborting, or timing\n * out always settles cleanly so the agent never deadlocks on an absent user.\n *\n * Concurrent calls are serialized through a per-session FIFO queue: only one\n * question is ever shown at a time, and a queued call whose signal aborts\n * settles without opening any UI.\n */\n\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport type { AskRequest, AskResult, ExtensionContext, ToolDefinition } from \"../extensions/types.js\";\n\n// ============================================================================\n// Types\n\nexport interface AskUserDetails {\n\tquestion: string;\n\ttitle?: string;\n\tselected: string[];\n\tcustomText?: string;\n\t/** True when the question closed without an answer. */\n\tskipped: boolean;\n\t/** True when no interactive UI was available (headless/print mode). */\n\tunavailable: boolean;\n\t/** True when the UI host or response protocol failed. */\n\tfailed?: boolean;\n}\n\n// ============================================================================\n// Schema\n\nconst askUserSchema = Type.Object({\n\tquestion: Type.String({\n\t\tdescription: \"The Markdown-formatted question to ask the user. Be specific about what you need to decide.\",\n\t}),\n\ttitle: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Short bold header shown above the question.\",\n\t\t}),\n\t),\n\toptions: Type.Optional(\n\t\tType.Array(Type.String({ minLength: 1, pattern: \"^.*[^ \\\\t\\\\r\\\\n].*$\" }), {\n\t\t\tminItems: 2,\n\t\t\tmaxItems: 4,\n\t\t\tdescription: \"2-4 nonblank suggested answers the user can pick from.\",\n\t\t}),\n\t),\n\tallowFreeText: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Offer a 'type your own answer' field. Defaults to true.\",\n\t\t}),\n\t),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Allow selecting multiple options (checkboxes). Only meaningful with options.\",\n\t\t}),\n\t),\n\tmultiline: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"Use a large multi-line text area for open-ended answers.\",\n\t\t}),\n\t),\n\ttimeoutSeconds: Type.Optional(\n\t\tType.Number({\n\t\t\tminimum: 5,\n\t\t\tmaximum: 3600,\n\t\t\tdescription:\n\t\t\t\t\"Optional: stop the current agent turn after this many seconds if the user does not respond. \" +\n\t\t\t\t\"Shows a live countdown. Omit to wait indefinitely.\",\n\t\t}),\n\t),\n});\n\nexport type AskUserInput = Static<typeof askUserSchema>;\n\n// ============================================================================\n// Result helpers\n\nfunction textResult(text: string, details: AskUserDetails) {\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text }],\n\t\tdetails,\n\t};\n}\n\nfunction baseDetails(input: AskUserInput): Omit<AskUserDetails, \"selected\" | \"skipped\" | \"unavailable\"> {\n\treturn { question: input.question, title: input.title };\n}\n\nfunction unavailableResult(input: AskUserInput) {\n\treturn textResult(\n\t\t\"The ask_user tool requires an interactive UI, which is not available in this mode. \" +\n\t\t\t\"Proceed using your best judgment without waiting for an answer.\",\n\t\t{ ...baseDetails(input), selected: [], skipped: true, unavailable: true },\n\t);\n}\n\nfunction unansweredResult(input: AskUserInput) {\n\treturn textResult(\"The question closed without an answer.\", {\n\t\t...baseDetails(input),\n\t\tselected: [],\n\t\tskipped: true,\n\t\tunavailable: false,\n\t});\n}\n\nfunction failedResult(input: AskUserInput) {\n\treturn textResult(\n\t\t\"The question could not be delivered because the interactive UI or response protocol failed. \" +\n\t\t\t\"Continue without this input.\",\n\t\t{\n\t\t\t...baseDetails(input),\n\t\t\tselected: [],\n\t\t\tskipped: false,\n\t\t\tunavailable: false,\n\t\t\tfailed: true,\n\t\t},\n\t);\n}\n\nfunction answeredResult(input: AskUserInput, answer: AskResult) {\n\tconst customText = answer.customText?.trim() || undefined;\n\tconst selected = answer.selected;\n\tconst parts: string[] = [];\n\tif (selected.length > 0) {\n\t\tparts.push(\n\t\t\tselected.length === 1 ? `The user selected: ${selected[0]}` : `The user selected: ${selected.join(\", \")}`,\n\t\t);\n\t}\n\tif (customText) {\n\t\tparts.push(selected.length > 0 ? `They also wrote: \"${customText}\"` : `The user answered: \"${customText}\"`);\n\t}\n\treturn textResult(parts.join(\" \"), {\n\t\t...baseDetails(input),\n\t\tselected,\n\t\tcustomText,\n\t\tskipped: false,\n\t\tunavailable: false,\n\t});\n}\n\n// ============================================================================\n// Render helpers\n\nfunction formatCall(args: { question?: string; title?: string } | undefined, theme: any): string {\n\tconst label = (args?.title || args?.question || \"\").replace(/\\s+/g, \" \").trim();\n\tconst shown = label.length > 80 ? `${label.slice(0, 79)}…` : label;\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"ask_user\"))} ${theme.fg(\"accent\", shown)}`;\n}\n\nfunction formatResult(details: AskUserDetails, theme: any): string {\n\tif (details.unavailable) return theme.fg(\"toolOutput\", \"no interactive UI — continued without asking\");\n\tif (details.failed) return theme.fg(\"toolOutput\", \"interactive UI failed — continued without an answer\");\n\tif (details.skipped) return theme.fg(\"toolOutput\", \"question closed without an answer\");\n\tconst parts: string[] = [];\n\tif (details.selected.length > 0) parts.push(details.selected.join(\", \"));\n\tif (details.customText) parts.push(`\"${details.customText}\"`);\n\treturn theme.fg(\"toolOutput\", `→ ${parts.join(\" + \")}`);\n}\n\n// ============================================================================\n// Tool definition factory\n\n/**\n * Create an `ask_user` tool definition. Each call creates an isolated FIFO\n * queue, so concurrent `ask_user` calls in a single session are shown strictly\n * one at a time.\n */\nexport function createAskUserToolDefinition(): ToolDefinition<typeof askUserSchema, AskUserDetails | undefined> {\n\t// Per-session serialization: only one question is ever open at a time.\n\tlet tail: Promise<void> = Promise.resolve();\n\tconst serialize = <T>(run: () => Promise<T>): Promise<T> => {\n\t\tconst result = tail.then(run, run);\n\t\t// Always advance the queue, whether the call resolved, cancelled, timed\n\t\t// out, or threw — so a failure can never wedge later questions.\n\t\ttail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t};\n\n\treturn {\n\t\tname: \"ask_user\",\n\t\tlabel: \"ask_user\",\n\t\tdescription:\n\t\t\t\"Pause and ask the user a structured clarifying question with optional multiple-choice options and a \" +\n\t\t\t\"free-text answer. Use only when genuinely blocked by ambiguity with multiple viable paths — not for routine confirmation.\",\n\n\t\tparameters: askUserSchema,\n\n\t\tpromptSnippet: \"Ask the user a clarifying question with optional multiple-choice options and free text\",\n\n\t\tpromptGuidelines: [\n\t\t\t\"Call ask_user ONLY when you are genuinely blocked by ambiguity and there are multiple viable paths forward\",\n\t\t\t\"Do NOT use it for routine confirmation, permission, or things you can reasonably decide yourself\",\n\t\t\t\"Provide 2-4 concrete `options` when there are clear candidate answers; the user can always type their own\",\n\t\t\t\"Set `multiSelect: true` when several options can be combined; `multiline: true` for open-ended answers\",\n\t\t\t\"The user may stop the current turn instead of answering; never treat that as an answer\",\n\t\t\t\"Prefer one focused question over many; the question blocks the turn until the user responds or stops it\",\n\t\t],\n\n\t\tasync execute(_toolCallId, input: AskUserInput, signal, _onUpdate, ctx?: ExtensionContext) {\n\t\t\tconst hasOptions = (input.options?.length ?? 0) > 0;\n\t\t\tconst request: AskRequest = {\n\t\t\t\tquestion: input.question,\n\t\t\t\ttitle: input.title,\n\t\t\t\toptions: input.options,\n\t\t\t\t// Guarantee at least one answer control: with no options, free text\n\t\t\t\t// must be offered regardless of the requested flag, otherwise both\n\t\t\t\t// surfaces would render only a Skip button and no way to answer.\n\t\t\t\tallowFreeText: hasOptions ? input.allowFreeText : true,\n\t\t\t\t// multiSelect is only meaningful with options; multiline only with\n\t\t\t\t// free text — normalize away impossible combinations.\n\t\t\t\tmultiSelect: hasOptions ? input.multiSelect : undefined,\n\t\t\t\tmultiline: hasOptions ? (input.allowFreeText === false ? undefined : input.multiline) : input.multiline,\n\t\t\t};\n\n\t\t\t// Optional auto-stop timeout, forwarded to every UI surface (TUI\n\t\t\t// countdown, RPC/Dashboard). Model-facing units are seconds.\n\t\t\tconst timeout = input.timeoutSeconds && input.timeoutSeconds > 0 ? input.timeoutSeconds * 1000 : undefined;\n\n\t\t\t// Headless / print / no-host modes: never block on an unreachable UI.\n\t\t\tif (!ctx?.hasUI) {\n\t\t\t\treturn unavailableResult(input);\n\t\t\t}\n\n\t\t\treturn serialize(async () => {\n\t\t\t\t// A queued call whose signal already aborted settles without ever\n\t\t\t\t// opening the UI; the parent turn is already stopping.\n\t\t\t\tif (signal?.aborted) return unansweredResult(input);\n\t\t\t\ttry {\n\t\t\t\t\tconst answer = await ctx.ui.ask(request, { signal, timeout });\n\t\t\t\t\tif (!answer || (answer.selected.length === 0 && !answer.customText?.trim())) {\n\t\t\t\t\t\treturn unansweredResult(input);\n\t\t\t\t\t}\n\t\t\t\t\treturn answeredResult(input, answer);\n\t\t\t\t} catch {\n\t\t\t\t\t// Host/protocol failure must still release the queue and never\n\t\t\t\t\t// deadlock, but it must not masquerade as an intentional user skip.\n\t\t\t\t\treturn failedResult(input);\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\ttext.setText(formatCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\tconst details = (result as any).details as AskUserDetails | undefined;\n\t\t\tif (details) {\n\t\t\t\ttext.setText(formatResult(details, theme));\n\t\t\t} else {\n\t\t\t\tconst content = result.content?.[0];\n\t\t\t\ttext.setText(theme.fg(\"toolOutput\", content?.type === \"text\" ? content.text : \"\"));\n\t\t\t}\n\t\t\treturn text;\n\t\t},\n\t};\n}\n"]}
|