@p4code/cli 0.1.16 → 0.1.18
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/bin.mjs +2518 -499
- package/dist/client/assets/DiffPanel-CP_cM9m0.js +98 -0
- package/dist/client/assets/{FilePreviewPanel-CXPZlxI3.js → FilePreviewPanel-CLho0PPu.js} +5 -5
- package/dist/client/assets/PreviewPanel-DvnUpMQv.js +2 -0
- package/dist/client/assets/{angular-html-B5E_J1fS.js → angular-html-CEMakyzJ.js} +2 -2
- package/dist/client/assets/{angular-ts-COv5JKxn.js → angular-ts-YXZHq2uz.js} +2 -2
- package/dist/client/assets/{apl-BQsQyMXO.js → apl-BTa85Bsd.js} +2 -2
- package/dist/client/assets/arrow-right-Bi8kADrV.js +2 -0
- package/dist/client/assets/{astro-B8L9Cn_4.js → astro-DIbti84c.js} +2 -2
- package/dist/client/assets/{blade-B_Che9pT.js → blade-CKAdvdwG.js} +2 -2
- package/dist/client/assets/{c-CPSBTKBx.js → c-DW17xzCK.js} +2 -2
- package/dist/client/assets/{cobol-BriE1iNK.js → cobol-DXiicIcZ.js} +2 -2
- package/dist/client/assets/{coffee-ClgsI2Oz.js → coffee-9Pdmmdn2.js} +2 -2
- package/dist/client/assets/{cpp-BoIGp4CN.js → cpp-bm3vFVmW.js} +2 -2
- package/dist/client/assets/{crystal-B-s92Xor.js → crystal-DbxZDZf9.js} +2 -2
- package/dist/client/assets/{css-lm7yIvqf.js → css-BEBnbxDe.js} +2 -2
- package/dist/client/assets/{edge-BdwKb_XJ.js → edge-K97wVEy_.js} +2 -2
- package/dist/client/assets/{elixir-Dd3gZy8B.js → elixir-Rc2JDLLD.js} +2 -2
- package/dist/client/assets/{elm-BUwJ0Z6D.js → elm-QsA8aREL.js} +2 -2
- package/dist/client/assets/{erb-BmkGyaa3.js → erb-CliRJz4v.js} +2 -2
- package/dist/client/assets/{fileCommentAnnotations-aElZ1ls9.js → fileCommentAnnotations-DtIPA0Fm.js} +2 -2
- package/dist/client/assets/{git-rebase-CgG-fpnH.js → git-rebase-Da_Wuhd_.js} +2 -2
- package/dist/client/assets/{glimmer-js-CgCxySf7.js → glimmer-js-XPbTw3tC.js} +2 -2
- package/dist/client/assets/{glimmer-ts-D0qJCdA3.js → glimmer-ts-BtK3NOkH.js} +2 -2
- package/dist/client/assets/{glsl-D-TJl_NC.js → glsl-DqEtJptk.js} +2 -2
- package/dist/client/assets/{graphql-tJKOdjT8.js → graphql-BSRTeXs0.js} +2 -2
- package/dist/client/assets/{hack-CNRyS4CV.js → hack-Dw9HaNHZ.js} +2 -2
- package/dist/client/assets/{haml-DZHEkAiU.js → haml-BBNabTlb.js} +2 -2
- package/dist/client/assets/{handlebars-C2vebdS9.js → handlebars-BgXzhWSj.js} +2 -2
- package/dist/client/assets/{html-derivative-C5tQldZt.js → html-derivative-CANLUZ-z.js} +2 -2
- package/dist/client/assets/{html-DCuQyWj1.js → html-wBdSHoow.js} +2 -2
- package/dist/client/assets/{http-BAgKJQ4_.js → http-BY44QKMy.js} +2 -2
- package/dist/client/assets/{hurl-CPO35wiX.js → hurl-EMZQkkjM.js} +2 -2
- package/dist/client/assets/index-Dol3ssFp.css +1 -0
- package/dist/client/assets/index-kwzTYbVb.js +775 -0
- package/dist/client/assets/{java-DdKunJP-.js → java-ByjPbT42.js} +2 -2
- package/dist/client/assets/{javascript-BzCImGGh.js → javascript-C9vbUEcm.js} +2 -2
- package/dist/client/assets/{jinja-KxDS67Zm.js → jinja-DH-AbsBN.js} +2 -2
- package/dist/client/assets/{jison-CB_VQ7nH.js → jison-Bef1Ntr_.js} +2 -2
- package/dist/client/assets/{json-DW5yQfU4.js → json-Gk_oM0bP.js} +2 -2
- package/dist/client/assets/{jsx-BCRm4K8K.js → jsx-Ds7YHLou.js} +2 -2
- package/dist/client/assets/{julia-B7zmtt-j.js → julia-Dm-6xpIK.js} +2 -2
- package/dist/client/assets/{just-BPUjF3fe.js → just-BXY93QN4.js} +2 -2
- package/dist/client/assets/{latex-DlHsgJiO.js → latex-B3-eYkFA.js} +2 -2
- package/dist/client/assets/{liquid-DkAYrmIm.js → liquid-C10OBLnm.js} +2 -2
- package/dist/client/assets/{lua-CkWXcEtU.js → lua-BUR8bsxC.js} +2 -2
- package/dist/client/assets/{marko-BreU7Tr4.js → marko-COPLM_fz.js} +2 -2
- package/dist/client/assets/{mdc-BIy4U_CM.js → mdc-CTAUTBsz.js} +2 -2
- package/dist/client/assets/{nginx-CaBRBNvW.js → nginx-D1BLqLRM.js} +2 -2
- package/dist/client/assets/{nim-Dh_VcD-W.js → nim-Xs8VbhdY.js} +2 -2
- package/dist/client/assets/{perl-DljrVQhk.js → perl-D7-zmFa1.js} +2 -2
- package/dist/client/assets/{php-DcpMefw4.js → php-BiTVgoaL.js} +2 -2
- package/dist/client/assets/{pug-BquSKFGg.js → pug-DphfHoFa.js} +2 -2
- package/dist/client/assets/{qml-DEzduZ1z.js → qml-CYcNnFOd.js} +2 -2
- package/dist/client/assets/{r-D0PJ1YHw.js → r-BrJ-p0TS.js} +2 -2
- package/dist/client/assets/{razor-C9NzXzG1.js → razor-1gio6m_g.js} +2 -2
- package/dist/client/assets/{regexp-Bppxv8Nd.js → regexp-CNiuKup7.js} +2 -2
- package/dist/client/assets/{rst-BHL4HPZF.js → rst-BPOsO8_U.js} +2 -2
- package/dist/client/assets/{ruby-BgqgMbbJ.js → ruby-CpfKHKDc.js} +2 -2
- package/dist/client/assets/{sas-CWVKvcbG.js → sas-CngX1we2.js} +2 -2
- package/dist/client/assets/{scss-Cw6AvzKE.js → scss-B2remLQX.js} +2 -2
- package/dist/client/assets/{shellscript-BmSXT-3L.js → shellscript-CQmxALRY.js} +2 -2
- package/dist/client/assets/{shellsession-CoR9jXze.js → shellsession-BB-5CB_r.js} +2 -2
- package/dist/client/assets/{soy-C4LA8zVY.js → soy-kYUnX51I.js} +2 -2
- package/dist/client/assets/{sql-ByI0x14l.js → sql-Cq9fRkwr.js} +2 -2
- package/dist/client/assets/{stata-fHDl5JMD.js → stata-17nboilR.js} +2 -2
- package/dist/client/assets/{surrealql-bNPgbvaA.js → surrealql-D1hrcsSn.js} +2 -2
- package/dist/client/assets/{svelte-BvpeBLSH.js → svelte-GmGi3eK8.js} +2 -2
- package/dist/client/assets/{templ-uUxwDReQ.js → templ-DySdTLEs.js} +2 -2
- package/dist/client/assets/{tex-DvBkmY5z.js → tex-CblcFqPi.js} +2 -2
- package/dist/client/assets/textarea-Cf56oU_6.js +46 -0
- package/dist/client/assets/{ts-tags-CNdGGSEK.js → ts-tags-BQC5pzJI.js} +2 -2
- package/dist/client/assets/{tsx-C-xKLmxT.js → tsx-1bNjXgiV.js} +2 -2
- package/dist/client/assets/{twig-BwP3uniF.js → twig-CNwthYXl.js} +2 -2
- package/dist/client/assets/{typescript-EVQqiZdz.js → typescript-BTKoaKmx.js} +2 -2
- package/dist/client/assets/{vue-CcDCYMax.js → vue-B4XpWxge.js} +2 -2
- package/dist/client/assets/{vue-html-D78T-MxH.js → vue-html-CcUSqexV.js} +2 -2
- package/dist/client/assets/{vue-vine-CZ6XjTDM.js → vue-vine-JDMd26pY.js} +2 -2
- package/dist/client/assets/{xml-Clr4M-ss.js → xml-DRuNSJP8.js} +2 -2
- package/dist/client/assets/{xsl-CboPzliu.js → xsl-BIJJg1B0.js} +2 -2
- package/dist/client/assets/{yaml-DOfnzofq.js → yaml-BNEnOsJI.js} +2 -2
- package/dist/client/index.html +3 -3
- package/package.json +1 -1
- package/dist/client/assets/DiffPanel-rTTo7hcH.js +0 -98
- package/dist/client/assets/PreviewPanel-BFuAVjo6.js +0 -2
- package/dist/client/assets/arrow-right-SCFydakV.js +0 -2
- package/dist/client/assets/index-DXGA7g9I.js +0 -655
- package/dist/client/assets/index-MItVmi4T.css +0 -1
- package/dist/client/assets/textarea-CbUaa52I.js +0 -46
package/dist/bin.mjs
CHANGED
|
@@ -233,7 +233,7 @@ const make$75 = () => {
|
|
|
233
233
|
const layer$71 = Layer.sync(NetService, make$75);
|
|
234
234
|
//#endregion
|
|
235
235
|
//#region package.json
|
|
236
|
-
var version = "0.1.
|
|
236
|
+
var version = "0.1.18";
|
|
237
237
|
//#endregion
|
|
238
238
|
//#region src/config.ts
|
|
239
239
|
/**
|
|
@@ -1509,6 +1509,7 @@ const CODEX_DRIVER_KIND = ProviderDriverKind.make("codex");
|
|
|
1509
1509
|
const CLAUDE_DRIVER_KIND = ProviderDriverKind.make("claudeAgent");
|
|
1510
1510
|
const CURSOR_DRIVER_KIND = ProviderDriverKind.make("cursor");
|
|
1511
1511
|
const GROK_DRIVER_KIND$1 = ProviderDriverKind.make("grok");
|
|
1512
|
+
const MUSE_DRIVER_KIND = ProviderDriverKind.make("muse");
|
|
1512
1513
|
const OPENCODE_DRIVER_KIND = ProviderDriverKind.make("opencode");
|
|
1513
1514
|
const DEFAULT_MODEL = "gpt-5.6-sol";
|
|
1514
1515
|
/**
|
|
@@ -1523,6 +1524,7 @@ const DEFAULT_MODEL_BY_PROVIDER = {
|
|
|
1523
1524
|
[CLAUDE_DRIVER_KIND]: "claude-sonnet-5",
|
|
1524
1525
|
[CURSOR_DRIVER_KIND]: "auto",
|
|
1525
1526
|
[GROK_DRIVER_KIND$1]: "grok-build",
|
|
1527
|
+
[MUSE_DRIVER_KIND]: "muse-spark-1.2",
|
|
1526
1528
|
[OPENCODE_DRIVER_KIND]: "openai/gpt-5"
|
|
1527
1529
|
};
|
|
1528
1530
|
/** Per-provider text generation model defaults. */
|
|
@@ -1530,6 +1532,7 @@ const DEFAULT_TEXT_GENERATION_MODEL_BY_PROVIDER = {
|
|
|
1530
1532
|
[CODEX_DRIVER_KIND]: DEFAULT_TEXT_GENERATION_MODEL,
|
|
1531
1533
|
[CLAUDE_DRIVER_KIND]: "claude-haiku-4-5",
|
|
1532
1534
|
[CURSOR_DRIVER_KIND]: "composer-2",
|
|
1535
|
+
[MUSE_DRIVER_KIND]: "muse-spark-1.2-contributor",
|
|
1533
1536
|
[OPENCODE_DRIVER_KIND]: "openai/gpt-5"
|
|
1534
1537
|
};
|
|
1535
1538
|
const MODEL_SLUG_ALIASES_BY_PROVIDER = {
|
|
@@ -4618,6 +4621,7 @@ const RuntimeEventRawSource = Schema$1.Union([
|
|
|
4618
4621
|
Schema$1.Literal("claude.sdk.permission"),
|
|
4619
4622
|
Schema$1.Literal("codex.sdk.thread-event"),
|
|
4620
4623
|
Schema$1.Literal("opencode.sdk.event"),
|
|
4624
|
+
Schema$1.Literal("muse.exec.jsonl"),
|
|
4621
4625
|
Schema$1.Literal("acp.jsonrpc"),
|
|
4622
4626
|
Schema$1.TemplateLiteral([
|
|
4623
4627
|
"acp.",
|
|
@@ -4769,6 +4773,7 @@ Schema$1.Literals([
|
|
|
4769
4773
|
"config.warning",
|
|
4770
4774
|
"deprecation.notice",
|
|
4771
4775
|
"files.persisted",
|
|
4776
|
+
"code.published",
|
|
4772
4777
|
"runtime.warning",
|
|
4773
4778
|
"runtime.error"
|
|
4774
4779
|
]);
|
|
@@ -4817,6 +4822,7 @@ const ModelReroutedType = Schema$1.Literal("model.rerouted");
|
|
|
4817
4822
|
const ConfigWarningType = Schema$1.Literal("config.warning");
|
|
4818
4823
|
const DeprecationNoticeType = Schema$1.Literal("deprecation.notice");
|
|
4819
4824
|
const FilesPersistedType = Schema$1.Literal("files.persisted");
|
|
4825
|
+
const CodePublishedType = Schema$1.Literal("code.published");
|
|
4820
4826
|
const ToolDeniedType = Schema$1.Literal("tool.denied");
|
|
4821
4827
|
const RuntimeWarningType = Schema$1.Literal("runtime.warning");
|
|
4822
4828
|
const RuntimeErrorType = Schema$1.Literal("runtime.error");
|
|
@@ -5119,6 +5125,12 @@ const FilesPersistedPayload = Schema$1.Struct({
|
|
|
5119
5125
|
error: TrimmedNonEmptyStringSchema$1
|
|
5120
5126
|
})))
|
|
5121
5127
|
});
|
|
5128
|
+
const CodePublishedPayload = Schema$1.Struct({
|
|
5129
|
+
hostingProvider: TrimmedNonEmptyStringSchema$1,
|
|
5130
|
+
url: TrimmedNonEmptyStringSchema$1,
|
|
5131
|
+
repo: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
5132
|
+
identifier: Schema$1.optional(TrimmedNonEmptyStringSchema$1)
|
|
5133
|
+
});
|
|
5122
5134
|
const ToolDeniedPayload = Schema$1.Struct({
|
|
5123
5135
|
toolName: TrimmedNonEmptyStringSchema$1,
|
|
5124
5136
|
toolUseId: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
@@ -5359,6 +5371,11 @@ const ProviderRuntimeFilesPersistedEvent = Schema$1.Struct({
|
|
|
5359
5371
|
type: FilesPersistedType,
|
|
5360
5372
|
payload: FilesPersistedPayload
|
|
5361
5373
|
});
|
|
5374
|
+
const ProviderRuntimeCodePublishedEvent = Schema$1.Struct({
|
|
5375
|
+
...ProviderRuntimeEventBase.fields,
|
|
5376
|
+
type: CodePublishedType,
|
|
5377
|
+
payload: CodePublishedPayload
|
|
5378
|
+
});
|
|
5362
5379
|
const ProviderRuntimeToolDeniedEvent = Schema$1.Struct({
|
|
5363
5380
|
...ProviderRuntimeEventBase.fields,
|
|
5364
5381
|
type: ToolDeniedType,
|
|
@@ -5420,6 +5437,7 @@ Schema$1.Union([
|
|
|
5420
5437
|
ProviderRuntimeConfigWarningEvent,
|
|
5421
5438
|
ProviderRuntimeDeprecationNoticeEvent,
|
|
5422
5439
|
ProviderRuntimeFilesPersistedEvent,
|
|
5440
|
+
ProviderRuntimeCodePublishedEvent,
|
|
5423
5441
|
ProviderRuntimeToolDeniedEvent,
|
|
5424
5442
|
ProviderRuntimeWarningEvent,
|
|
5425
5443
|
ProviderRuntimeErrorEvent
|
|
@@ -5712,6 +5730,26 @@ const GrokSettings = makeProviderSettingsSchema({
|
|
|
5712
5730
|
})),
|
|
5713
5731
|
customModels: Schema$1.Array(Schema$1.String).pipe(Schema$1.withDecodingDefault(Effect.succeed([])), Schema$1.annotateKey({ providerSettingsForm: { hidden: true } }))
|
|
5714
5732
|
}, { order: ["binaryPath"] });
|
|
5733
|
+
const MuseSettings = makeProviderSettingsSchema({
|
|
5734
|
+
enabled: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false)), Schema$1.annotateKey({ providerSettingsForm: { hidden: true } })),
|
|
5735
|
+
binaryPath: makeBinaryPathSetting("muse").pipe(Schema$1.annotateKey({
|
|
5736
|
+
title: "Binary path",
|
|
5737
|
+
description: "Path to the Muse Code binary.",
|
|
5738
|
+
providerSettingsForm: {
|
|
5739
|
+
placeholder: "muse",
|
|
5740
|
+
clearWhenEmpty: "omit"
|
|
5741
|
+
}
|
|
5742
|
+
})),
|
|
5743
|
+
baseUrl: TrimmedString.pipe(Schema$1.withDecodingDefault(Effect.succeed("")), Schema$1.annotateKey({
|
|
5744
|
+
title: "Base URL",
|
|
5745
|
+
description: "Override the Meta Model API base URL for this instance.",
|
|
5746
|
+
providerSettingsForm: {
|
|
5747
|
+
placeholder: "https://api.meta.ai/v1",
|
|
5748
|
+
clearWhenEmpty: "omit"
|
|
5749
|
+
}
|
|
5750
|
+
})),
|
|
5751
|
+
customModels: Schema$1.Array(Schema$1.String).pipe(Schema$1.withDecodingDefault(Effect.succeed([])), Schema$1.annotateKey({ providerSettingsForm: { hidden: true } }))
|
|
5752
|
+
}, { order: ["binaryPath", "baseUrl"] });
|
|
5715
5753
|
const OpenCodeSettings = makeProviderSettingsSchema({
|
|
5716
5754
|
enabled: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true)), Schema$1.annotateKey({ providerSettingsForm: { hidden: true } })),
|
|
5717
5755
|
binaryPath: makeBinaryPathSetting("opencode").pipe(Schema$1.annotateKey({
|
|
@@ -5771,6 +5809,20 @@ const ServerSettings = Schema$1.Struct({
|
|
|
5771
5809
|
* back for sessions started by the Claude provider.
|
|
5772
5810
|
*/
|
|
5773
5811
|
enableToolCallNarration: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
|
|
5812
|
+
/**
|
|
5813
|
+
* Whether the model may spawn subagents without being asked to.
|
|
5814
|
+
*
|
|
5815
|
+
* On by default: a bounded search or review whose result is far smaller than
|
|
5816
|
+
* the reading it takes to produce is exactly the work a subagent is for, and
|
|
5817
|
+
* an agent that has to ask first will simply do it inline instead.
|
|
5818
|
+
*
|
|
5819
|
+
* Stated in the appended prompt whichever way it is set, rather than only
|
|
5820
|
+
* when off. The provider's own preset prompt carries an instruction not to
|
|
5821
|
+
* spawn subagents unless asked, and p4code requests that preset wholesale —
|
|
5822
|
+
* so there is nothing to withhold, and only a later, more specific
|
|
5823
|
+
* instruction can override an earlier general one.
|
|
5824
|
+
*/
|
|
5825
|
+
enableUnpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
|
|
5774
5826
|
automaticGitFetchInterval: Schema$1.DurationFromMillis.pipe(Schema$1.withDecodingDefault(Effect.succeed(Duration.toMillis(DEFAULT_AUTOMATIC_GIT_FETCH_INTERVAL)))),
|
|
5775
5827
|
defaultThreadEnvMode: ThreadEnvMode.pipe(Schema$1.withDecodingDefault(Effect.succeed("local"))),
|
|
5776
5828
|
newWorktreesStartFromOrigin: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
|
|
@@ -5822,6 +5874,7 @@ const ServerSettings = Schema$1.Struct({
|
|
|
5822
5874
|
claudeAgent: ClaudeSettings.pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
|
|
5823
5875
|
cursor: CursorSettings.pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
|
|
5824
5876
|
grok: GrokSettings.pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
|
|
5877
|
+
muse: MuseSettings.pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
|
|
5825
5878
|
opencode: OpenCodeSettings.pipe(Schema$1.withDecodingDefault(Effect.succeed({})))
|
|
5826
5879
|
}).pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
|
|
5827
5880
|
providerInstances: Schema$1.Record(ProviderInstanceId, ProviderInstanceConfig).pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
|
|
@@ -5887,6 +5940,12 @@ const GrokSettingsPatch = Schema$1.Struct({
|
|
|
5887
5940
|
binaryPath: Schema$1.optionalKey(TrimmedString),
|
|
5888
5941
|
customModels: Schema$1.optionalKey(Schema$1.Array(Schema$1.String))
|
|
5889
5942
|
});
|
|
5943
|
+
const MuseSettingsPatch = Schema$1.Struct({
|
|
5944
|
+
enabled: Schema$1.optionalKey(Schema$1.Boolean),
|
|
5945
|
+
binaryPath: Schema$1.optionalKey(TrimmedString),
|
|
5946
|
+
baseUrl: Schema$1.optionalKey(TrimmedString),
|
|
5947
|
+
customModels: Schema$1.optionalKey(Schema$1.Array(Schema$1.String))
|
|
5948
|
+
});
|
|
5890
5949
|
const OpenCodeSettingsPatch = Schema$1.Struct({
|
|
5891
5950
|
enabled: Schema$1.optionalKey(Schema$1.Boolean),
|
|
5892
5951
|
binaryPath: Schema$1.optionalKey(TrimmedString),
|
|
@@ -5898,6 +5957,7 @@ const ServerSettingsPatch = Schema$1.Struct({
|
|
|
5898
5957
|
enableAssistantStreaming: Schema$1.optionalKey(Schema$1.Boolean),
|
|
5899
5958
|
enableProviderUpdateChecks: Schema$1.optionalKey(Schema$1.Boolean),
|
|
5900
5959
|
enableToolCallNarration: Schema$1.optionalKey(Schema$1.Boolean),
|
|
5960
|
+
enableUnpromptedSubagents: Schema$1.optionalKey(Schema$1.Boolean),
|
|
5901
5961
|
automaticGitFetchInterval: Schema$1.optionalKey(Schema$1.DurationFromMillis),
|
|
5902
5962
|
defaultThreadEnvMode: Schema$1.optionalKey(ThreadEnvMode),
|
|
5903
5963
|
newWorktreesStartFromOrigin: Schema$1.optionalKey(Schema$1.Boolean),
|
|
@@ -5920,6 +5980,7 @@ const ServerSettingsPatch = Schema$1.Struct({
|
|
|
5920
5980
|
claudeAgent: Schema$1.optionalKey(ClaudeSettingsPatch),
|
|
5921
5981
|
cursor: Schema$1.optionalKey(CursorSettingsPatch),
|
|
5922
5982
|
grok: Schema$1.optionalKey(GrokSettingsPatch),
|
|
5983
|
+
muse: Schema$1.optionalKey(MuseSettingsPatch),
|
|
5923
5984
|
opencode: Schema$1.optionalKey(OpenCodeSettingsPatch)
|
|
5924
5985
|
})),
|
|
5925
5986
|
providerInstances: Schema$1.optionalKey(Schema$1.Record(ProviderInstanceId, ProviderInstanceConfig))
|
|
@@ -6059,6 +6120,14 @@ const ServerProvider = Schema$1.Struct({
|
|
|
6059
6120
|
continuation: Schema$1.optional(ServerProviderContinuation),
|
|
6060
6121
|
showInteractionModeToggle: Schema$1.optional(Schema$1.Boolean),
|
|
6061
6122
|
requiresNewThreadForModelChange: Schema$1.optional(Schema$1.Boolean),
|
|
6123
|
+
/**
|
|
6124
|
+
* Runtime modes this provider can actually honour. Absent means every
|
|
6125
|
+
* mode is available, which is the case for every provider that can ask
|
|
6126
|
+
* the user for an approval mid-turn. A provider that only runs headless
|
|
6127
|
+
* (Muse Code) lists the subset it can deliver, and clients disable the
|
|
6128
|
+
* rest instead of offering a mode the driver would silently downgrade.
|
|
6129
|
+
*/
|
|
6130
|
+
supportedRuntimeModes: Schema$1.optional(Schema$1.Array(RuntimeMode)),
|
|
6062
6131
|
enabled: Schema$1.Boolean,
|
|
6063
6132
|
installed: Schema$1.Boolean,
|
|
6064
6133
|
version: Schema$1.NullOr(TrimmedNonEmptyString),
|
|
@@ -7780,11 +7849,17 @@ const AGENT_ASSET_TOTAL_MAX_BYTES = 4 * 1024 * 1024;
|
|
|
7780
7849
|
* directory rather than the provider's. The definition travels; the bearer
|
|
7781
7850
|
* tokens and API keys it references do not, and stay in each machine's own
|
|
7782
7851
|
* secret store.
|
|
7852
|
+
* - `agent` — a subagent definition, a single markdown file in
|
|
7853
|
+
* `<configDir>/agents`. The name is the filename, as for `memory`. This is the
|
|
7854
|
+
* only artifact that bounds what a subagent is handed and what it hands back,
|
|
7855
|
+
* because p4code observes subagent runs rather than constructing their
|
|
7856
|
+
* prompts — so the I/O contract can only ship as a file the provider reads.
|
|
7783
7857
|
*/
|
|
7784
7858
|
const AgentAssetKind = Schema$1.Literals([
|
|
7785
7859
|
"skill",
|
|
7786
7860
|
"memory",
|
|
7787
|
-
"mcp"
|
|
7861
|
+
"mcp",
|
|
7862
|
+
"agent"
|
|
7788
7863
|
]);
|
|
7789
7864
|
/**
|
|
7790
7865
|
* An asset's name, which for a skill is also its directory name.
|
|
@@ -15492,7 +15567,15 @@ function buildSkillOverrides(disabled, discovered) {
|
|
|
15492
15567
|
* @module provider/Drivers/ClaudeSkills
|
|
15493
15568
|
*/
|
|
15494
15569
|
const FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
15495
|
-
|
|
15570
|
+
/**
|
|
15571
|
+
* The `name`/`description` header a skill and a subagent definition both carry.
|
|
15572
|
+
*
|
|
15573
|
+
* Exported because agent definitions are the same header in a different file,
|
|
15574
|
+
* and the sync validates them before publishing one. A second copy of this
|
|
15575
|
+
* would be a second answer to "does this load in the provider", which is the
|
|
15576
|
+
* one question both callers are asking.
|
|
15577
|
+
*/
|
|
15578
|
+
function parseMarkdownFrontmatter(contents) {
|
|
15496
15579
|
const match = FRONTMATTER_PATTERN.exec(contents);
|
|
15497
15580
|
if (!match) return { kind: "missing" };
|
|
15498
15581
|
let parsed;
|
|
@@ -15543,6 +15626,19 @@ const resolveClaudeUserSkillsDir = Effect.fn("resolveClaudeUserSkillsDir")(funct
|
|
|
15543
15626
|
return path.join(configDirPath, "skills");
|
|
15544
15627
|
});
|
|
15545
15628
|
/**
|
|
15629
|
+
* The user-scope subagent directory the CLI would read.
|
|
15630
|
+
*
|
|
15631
|
+
* Same resolution as the skills directory beside it, for the same reason: the
|
|
15632
|
+
* sync materializes into exactly the directory the provider loads from, and
|
|
15633
|
+
* deriving it a second time is how the two come to disagree about where an
|
|
15634
|
+
* agent definition lives.
|
|
15635
|
+
*/
|
|
15636
|
+
const resolveClaudeUserAgentsDir = Effect.fn("resolveClaudeUserAgentsDir")(function* (config, environment, cwd) {
|
|
15637
|
+
const path = yield* Path.Path;
|
|
15638
|
+
const configDirPath = yield* resolveClaudeConfigDirPath(config, environment ?? process.env, cwd);
|
|
15639
|
+
return path.join(configDirPath, "agents");
|
|
15640
|
+
});
|
|
15641
|
+
/**
|
|
15546
15642
|
* Enumerate Claude Code skills from the user config dir and the workspace.
|
|
15547
15643
|
* Discovery is best-effort: unreadable roots and malformed skill entries are
|
|
15548
15644
|
* skipped so a broken skill never degrades the provider snapshot. On name
|
|
@@ -15567,7 +15663,7 @@ const discoverClaudeSkills = Effect.fn("discoverClaudeSkills")(function* (config
|
|
|
15567
15663
|
const skillPath = path.join(root.directory, entry, "SKILL.md");
|
|
15568
15664
|
const contents = yield* fileSystem.readFileString(skillPath).pipe(Effect.orElseSucceed(() => void 0));
|
|
15569
15665
|
if (contents === void 0) continue;
|
|
15570
|
-
const frontmatter =
|
|
15666
|
+
const frontmatter = parseMarkdownFrontmatter(contents);
|
|
15571
15667
|
if (frontmatter.kind === "malformed") continue;
|
|
15572
15668
|
const name = (frontmatter.kind === "parsed" ? frontmatter.name : void 0) ?? entry.trim();
|
|
15573
15669
|
if (!name) continue;
|
|
@@ -16389,6 +16485,288 @@ const make$62 = Effect.gen(function* () {
|
|
|
16389
16485
|
});
|
|
16390
16486
|
const layer$57 = Layer.effect(McpRegistry, make$62);
|
|
16391
16487
|
//#endregion
|
|
16488
|
+
//#region src/sync/skillDirectory.ts
|
|
16489
|
+
/**
|
|
16490
|
+
* Reading and writing skill directories on disk.
|
|
16491
|
+
*
|
|
16492
|
+
* Kept apart from the sync itself so the filesystem shape has its own tests,
|
|
16493
|
+
* and so the rules about what is refusable — binaries, oversized files, deep
|
|
16494
|
+
* trees — live in one place rather than being rediscovered at each call site.
|
|
16495
|
+
*
|
|
16496
|
+
* @module sync/skillDirectory
|
|
16497
|
+
*/
|
|
16498
|
+
/** Bounded so a stray symlink or a vendored `node_modules` cannot walk forever. */
|
|
16499
|
+
const MAX_SKILL_DEPTH = 4;
|
|
16500
|
+
/**
|
|
16501
|
+
* Entries that are never part of a skill, skipped rather than refused.
|
|
16502
|
+
*
|
|
16503
|
+
* The distinction matters. Refusing an asset because it cannot be represented
|
|
16504
|
+
* faithfully is right — a half-materialized skill loads and misbehaves. But a
|
|
16505
|
+
* `.DS_Store` is not part of the skill at all, and macOS drops one into any
|
|
16506
|
+
* directory that has been opened in Finder, so refusing on its account rejects
|
|
16507
|
+
* a perfectly good skill for a reason its author cannot see.
|
|
16508
|
+
*
|
|
16509
|
+
* These could not sync in any case: `AgentAssetFilePath` rejects a leading dot,
|
|
16510
|
+
* so a dotfile has no representable path even if it were text.
|
|
16511
|
+
*/
|
|
16512
|
+
const IGNORED_ENTRIES = /* @__PURE__ */ new Set([
|
|
16513
|
+
".DS_Store",
|
|
16514
|
+
"Thumbs.db",
|
|
16515
|
+
".git",
|
|
16516
|
+
".svn",
|
|
16517
|
+
".hg",
|
|
16518
|
+
"node_modules",
|
|
16519
|
+
"__pycache__",
|
|
16520
|
+
".venv",
|
|
16521
|
+
".pytest_cache",
|
|
16522
|
+
".ruff_cache",
|
|
16523
|
+
".mypy_cache"
|
|
16524
|
+
]);
|
|
16525
|
+
/**
|
|
16526
|
+
* Anything else with a leading dot is skipped too — an editor's `.idea`, a
|
|
16527
|
+
* `.gitignore` that belongs to the checkout rather than the skill. The rule is
|
|
16528
|
+
* the same one the path schema already enforces, applied earlier so it reads as
|
|
16529
|
+
* "not part of the skill" instead of "this skill is broken".
|
|
16530
|
+
*/
|
|
16531
|
+
const isIgnoredEntry = (entry) => IGNORED_ENTRIES.has(entry) || entry.startsWith(".");
|
|
16532
|
+
/**
|
|
16533
|
+
* A file is binary if decoding it yields a NUL.
|
|
16534
|
+
*
|
|
16535
|
+
* Cheap, and right for what is being asked: the question is not "is this valid
|
|
16536
|
+
* UTF-8" but "can this round-trip through a JSON text column", and a NUL is
|
|
16537
|
+
* what actually breaks that on the way back out of SQLite.
|
|
16538
|
+
*
|
|
16539
|
+
* Exported so the single-file kinds ask it the same way this one does, rather
|
|
16540
|
+
* than each keeping a copy that could drift into a different answer.
|
|
16541
|
+
*/
|
|
16542
|
+
const looksBinary$1 = (content) => content.includes("\0");
|
|
16543
|
+
/**
|
|
16544
|
+
* Read one directory as an asset's files, or say why it cannot be one.
|
|
16545
|
+
*
|
|
16546
|
+
* Exported because memory's `global-memory/` is the same shape by a different
|
|
16547
|
+
* name: a directory whose whole contents are the asset. Sharing this keeps the
|
|
16548
|
+
* refusal rules — depth, file count, binaries, size — identical for both,
|
|
16549
|
+
* rather than letting a second copy drift into accepting what the first
|
|
16550
|
+
* rejects.
|
|
16551
|
+
*/
|
|
16552
|
+
const readSkillFiles = Effect.fn("skillDirectory.readSkillFiles")(function* (root) {
|
|
16553
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16554
|
+
const path = yield* Path.Path;
|
|
16555
|
+
const files = [];
|
|
16556
|
+
const walk = Effect.fn("skillDirectory.walk")(function* (directory, prefix, depth) {
|
|
16557
|
+
if (depth > MAX_SKILL_DEPTH) return "too-deep";
|
|
16558
|
+
const entries = yield* fileSystem.readDirectory(directory).pipe(Effect.orElseSucceed(() => []));
|
|
16559
|
+
for (const entry of [...entries].sort()) {
|
|
16560
|
+
if (isIgnoredEntry(entry)) continue;
|
|
16561
|
+
const absolute = path.join(directory, entry);
|
|
16562
|
+
const relative = prefix === "" ? entry : `${prefix}/${entry}`;
|
|
16563
|
+
const info = yield* fileSystem.stat(absolute).pipe(Effect.orElseSucceed(() => void 0));
|
|
16564
|
+
if (info === void 0) return "unreadable";
|
|
16565
|
+
if (info.type === "Directory") {
|
|
16566
|
+
const nested = yield* walk(absolute, relative, depth + 1);
|
|
16567
|
+
if (nested !== null) return nested;
|
|
16568
|
+
continue;
|
|
16569
|
+
}
|
|
16570
|
+
if (info.type !== "File") continue;
|
|
16571
|
+
if (files.length >= 512) return "too-many-files";
|
|
16572
|
+
const content = yield* fileSystem.readFileString(absolute).pipe(Effect.orElseSucceed(() => void 0));
|
|
16573
|
+
if (content === void 0) return "unreadable";
|
|
16574
|
+
if (looksBinary$1(content)) return "binary-file";
|
|
16575
|
+
if (content.length > 524288) return "file-too-large";
|
|
16576
|
+
files.push({
|
|
16577
|
+
path: relative,
|
|
16578
|
+
content
|
|
16579
|
+
});
|
|
16580
|
+
}
|
|
16581
|
+
return null;
|
|
16582
|
+
});
|
|
16583
|
+
const refusal = yield* walk(root, "", 1);
|
|
16584
|
+
if (refusal !== null) return { refusal };
|
|
16585
|
+
if (agentAssetTotalBytes(files) > 4194304) return { refusal: "asset-too-large" };
|
|
16586
|
+
return { files };
|
|
16587
|
+
});
|
|
16588
|
+
/**
|
|
16589
|
+
* Read every skill directory under a user-scope `skills/` root.
|
|
16590
|
+
*
|
|
16591
|
+
* A directory with no `SKILL.md` is not a skill and is skipped silently —
|
|
16592
|
+
* Claude Code would ignore it too, so reporting it would be noise about
|
|
16593
|
+
* something that was never a skill in the first place.
|
|
16594
|
+
*/
|
|
16595
|
+
const readSkillDirectory = Effect.fn("skillDirectory.readSkillDirectory")(function* (skillsRoot) {
|
|
16596
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16597
|
+
const path = yield* Path.Path;
|
|
16598
|
+
const entries = yield* fileSystem.readDirectory(skillsRoot).pipe(Effect.orElseSucceed(() => []));
|
|
16599
|
+
const skills = [];
|
|
16600
|
+
const refused = [];
|
|
16601
|
+
for (const entry of [...entries].sort()) {
|
|
16602
|
+
if (isIgnoredEntry(entry)) continue;
|
|
16603
|
+
const directory = path.join(skillsRoot, entry);
|
|
16604
|
+
if ((yield* fileSystem.stat(directory).pipe(Effect.orElseSucceed(() => void 0)))?.type !== "Directory") continue;
|
|
16605
|
+
if (!(yield* fileSystem.exists(path.join(directory, "SKILL.md")).pipe(Effect.orElseSucceed(() => false)))) continue;
|
|
16606
|
+
const read = yield* readSkillFiles(directory);
|
|
16607
|
+
if ("refusal" in read) {
|
|
16608
|
+
refused.push({
|
|
16609
|
+
name: entry,
|
|
16610
|
+
reason: read.refusal
|
|
16611
|
+
});
|
|
16612
|
+
continue;
|
|
16613
|
+
}
|
|
16614
|
+
skills.push({
|
|
16615
|
+
name: entry,
|
|
16616
|
+
files: read.files
|
|
16617
|
+
});
|
|
16618
|
+
}
|
|
16619
|
+
return {
|
|
16620
|
+
skills,
|
|
16621
|
+
refused
|
|
16622
|
+
};
|
|
16623
|
+
});
|
|
16624
|
+
/**
|
|
16625
|
+
* Replace a skill directory with exactly the asset's files.
|
|
16626
|
+
*
|
|
16627
|
+
* Written to a sibling and then swapped, so a crash mid-write leaves the old
|
|
16628
|
+
* skill intact rather than half of the new one. A skill is loaded as a unit and
|
|
16629
|
+
* a partially written one is instructions with the middle missing, which an
|
|
16630
|
+
* agent will happily follow.
|
|
16631
|
+
*/
|
|
16632
|
+
const writeSkillDirectory = Effect.fn("skillDirectory.writeSkillDirectory")(function* (input) {
|
|
16633
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16634
|
+
const path = yield* Path.Path;
|
|
16635
|
+
const target = path.join(input.skillsRoot, input.name);
|
|
16636
|
+
const staging = path.join(input.skillsRoot, `.${input.name}.p4code-staging`);
|
|
16637
|
+
yield* fileSystem.makeDirectory(input.skillsRoot, { recursive: true });
|
|
16638
|
+
yield* fileSystem.remove(staging, {
|
|
16639
|
+
recursive: true,
|
|
16640
|
+
force: true
|
|
16641
|
+
});
|
|
16642
|
+
yield* fileSystem.makeDirectory(staging, { recursive: true });
|
|
16643
|
+
for (const file of input.files) {
|
|
16644
|
+
const destination = path.join(staging, file.path);
|
|
16645
|
+
yield* fileSystem.makeDirectory(path.dirname(destination), { recursive: true });
|
|
16646
|
+
yield* fileSystem.writeFileString(destination, file.content);
|
|
16647
|
+
}
|
|
16648
|
+
yield* fileSystem.remove(target, {
|
|
16649
|
+
recursive: true,
|
|
16650
|
+
force: true
|
|
16651
|
+
});
|
|
16652
|
+
yield* fileSystem.rename(staging, target);
|
|
16653
|
+
});
|
|
16654
|
+
const removeSkillDirectory = Effect.fn("skillDirectory.removeSkillDirectory")(function* (input) {
|
|
16655
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16656
|
+
const path = yield* Path.Path;
|
|
16657
|
+
yield* fileSystem.remove(path.join(input.skillsRoot, input.name), {
|
|
16658
|
+
recursive: true,
|
|
16659
|
+
force: true
|
|
16660
|
+
});
|
|
16661
|
+
});
|
|
16662
|
+
/**
|
|
16663
|
+
* Whether this text is a definition the provider would actually load.
|
|
16664
|
+
*
|
|
16665
|
+
* Both fields, not just the header. `name` is how a subagent is addressed and
|
|
16666
|
+
* `description` is how the orchestrator decides to address it, so a definition
|
|
16667
|
+
* missing either is present on disk and unreachable in practice.
|
|
16668
|
+
*/
|
|
16669
|
+
const isLoadableAgentDefinition = (content) => {
|
|
16670
|
+
const frontmatter = parseMarkdownFrontmatter(content);
|
|
16671
|
+
return frontmatter.kind === "parsed" && frontmatter.name !== void 0 && frontmatter.description !== void 0;
|
|
16672
|
+
};
|
|
16673
|
+
/**
|
|
16674
|
+
* Every definition in the directory, as syncable assets.
|
|
16675
|
+
*
|
|
16676
|
+
* A file that will not load is refused rather than skipped, unlike a stray
|
|
16677
|
+
* `.DS_Store`: a `.md` in this directory is meant to be an agent, so a broken
|
|
16678
|
+
* header is a fault worth reporting instead of noise to ignore.
|
|
16679
|
+
*/
|
|
16680
|
+
const readAgentDefinitions = Effect.fn("agentDefinitionFiles.read")(function* (agentsRoot) {
|
|
16681
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16682
|
+
const path = yield* Path.Path;
|
|
16683
|
+
const entries = yield* fileSystem.readDirectory(agentsRoot).pipe(Effect.orElseSucceed(() => []));
|
|
16684
|
+
const skills = [];
|
|
16685
|
+
const refused = [];
|
|
16686
|
+
for (const entry of [...entries].sort()) {
|
|
16687
|
+
if (!entry.endsWith(".md") || entry.startsWith(".")) continue;
|
|
16688
|
+
const absolute = path.join(agentsRoot, entry);
|
|
16689
|
+
if ((yield* fileSystem.stat(absolute).pipe(Effect.orElseSucceed(() => void 0)))?.type !== "File") continue;
|
|
16690
|
+
const content = yield* fileSystem.readFileString(absolute).pipe(Effect.orElseSucceed(() => void 0));
|
|
16691
|
+
if (content === void 0) {
|
|
16692
|
+
refused.push({
|
|
16693
|
+
name: entry,
|
|
16694
|
+
reason: "unreadable"
|
|
16695
|
+
});
|
|
16696
|
+
continue;
|
|
16697
|
+
}
|
|
16698
|
+
if (looksBinary$1(content)) {
|
|
16699
|
+
refused.push({
|
|
16700
|
+
name: entry,
|
|
16701
|
+
reason: "binary-file"
|
|
16702
|
+
});
|
|
16703
|
+
continue;
|
|
16704
|
+
}
|
|
16705
|
+
if (content.length > 524288) {
|
|
16706
|
+
refused.push({
|
|
16707
|
+
name: entry,
|
|
16708
|
+
reason: "file-too-large"
|
|
16709
|
+
});
|
|
16710
|
+
continue;
|
|
16711
|
+
}
|
|
16712
|
+
if (!isLoadableAgentDefinition(content)) {
|
|
16713
|
+
refused.push({
|
|
16714
|
+
name: entry,
|
|
16715
|
+
reason: "unreadable"
|
|
16716
|
+
});
|
|
16717
|
+
continue;
|
|
16718
|
+
}
|
|
16719
|
+
const files = [{
|
|
16720
|
+
path: entry,
|
|
16721
|
+
content
|
|
16722
|
+
}];
|
|
16723
|
+
if (agentAssetTotalBytes(files) > 4194304) {
|
|
16724
|
+
refused.push({
|
|
16725
|
+
name: entry,
|
|
16726
|
+
reason: "asset-too-large"
|
|
16727
|
+
});
|
|
16728
|
+
continue;
|
|
16729
|
+
}
|
|
16730
|
+
skills.push({
|
|
16731
|
+
name: entry,
|
|
16732
|
+
files
|
|
16733
|
+
});
|
|
16734
|
+
}
|
|
16735
|
+
return {
|
|
16736
|
+
skills,
|
|
16737
|
+
refused
|
|
16738
|
+
};
|
|
16739
|
+
});
|
|
16740
|
+
/**
|
|
16741
|
+
* Write a definition that arrived from the hub.
|
|
16742
|
+
*
|
|
16743
|
+
* Refused unless it is exactly one markdown file whose path is the asset's own
|
|
16744
|
+
* name and whose frontmatter would load. The last check is the one that matters
|
|
16745
|
+
* across machines: it is what stops a hub row edited by hand from installing a
|
|
16746
|
+
* file the provider quietly ignores on every machine that syncs it.
|
|
16747
|
+
*
|
|
16748
|
+
* Written atomically because a definition is read whole when a subagent starts,
|
|
16749
|
+
* and half of one is instructions with the contract missing.
|
|
16750
|
+
*/
|
|
16751
|
+
const writeAgentDefinition = Effect.fn("agentDefinitionFiles.write")(function* (input) {
|
|
16752
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16753
|
+
const path = yield* Path.Path;
|
|
16754
|
+
const file = input.files[0];
|
|
16755
|
+
if (!input.name.endsWith(".md") || input.files.length !== 1 || file === void 0 || file.path !== input.name) return "refused";
|
|
16756
|
+
if (!isLoadableAgentDefinition(file.content)) return "refused";
|
|
16757
|
+
yield* fileSystem.makeDirectory(input.agentsRoot, { recursive: true }).pipe(Effect.ignore);
|
|
16758
|
+
return yield* writeFileStringAtomically({
|
|
16759
|
+
filePath: path.join(input.agentsRoot, input.name),
|
|
16760
|
+
contents: file.content
|
|
16761
|
+
}).pipe(Effect.as("written"), Effect.orElseSucceed(() => "failed"));
|
|
16762
|
+
});
|
|
16763
|
+
const removeAgentDefinition = Effect.fn("agentDefinitionFiles.remove")(function* (input) {
|
|
16764
|
+
if (!input.name.endsWith(".md")) return;
|
|
16765
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
16766
|
+
const path = yield* Path.Path;
|
|
16767
|
+
yield* fileSystem.remove(path.join(input.agentsRoot, input.name), { force: true });
|
|
16768
|
+
});
|
|
16769
|
+
//#endregion
|
|
16392
16770
|
//#region src/sync/HubLink.ts
|
|
16393
16771
|
/**
|
|
16394
16772
|
* Where this server's hub is, what it authenticates with, and whether it syncs
|
|
@@ -16762,178 +17140,6 @@ const removeMcpRegistration = Effect.fn("mcpRegistrationFiles.remove")(function*
|
|
|
16762
17140
|
yield* fileSystem.remove(path.join(input.registrationsRoot, input.name), { force: true });
|
|
16763
17141
|
});
|
|
16764
17142
|
//#endregion
|
|
16765
|
-
//#region src/sync/skillDirectory.ts
|
|
16766
|
-
/**
|
|
16767
|
-
* Reading and writing skill directories on disk.
|
|
16768
|
-
*
|
|
16769
|
-
* Kept apart from the sync itself so the filesystem shape has its own tests,
|
|
16770
|
-
* and so the rules about what is refusable — binaries, oversized files, deep
|
|
16771
|
-
* trees — live in one place rather than being rediscovered at each call site.
|
|
16772
|
-
*
|
|
16773
|
-
* @module sync/skillDirectory
|
|
16774
|
-
*/
|
|
16775
|
-
/** Bounded so a stray symlink or a vendored `node_modules` cannot walk forever. */
|
|
16776
|
-
const MAX_SKILL_DEPTH = 4;
|
|
16777
|
-
/**
|
|
16778
|
-
* Entries that are never part of a skill, skipped rather than refused.
|
|
16779
|
-
*
|
|
16780
|
-
* The distinction matters. Refusing an asset because it cannot be represented
|
|
16781
|
-
* faithfully is right — a half-materialized skill loads and misbehaves. But a
|
|
16782
|
-
* `.DS_Store` is not part of the skill at all, and macOS drops one into any
|
|
16783
|
-
* directory that has been opened in Finder, so refusing on its account rejects
|
|
16784
|
-
* a perfectly good skill for a reason its author cannot see.
|
|
16785
|
-
*
|
|
16786
|
-
* These could not sync in any case: `AgentAssetFilePath` rejects a leading dot,
|
|
16787
|
-
* so a dotfile has no representable path even if it were text.
|
|
16788
|
-
*/
|
|
16789
|
-
const IGNORED_ENTRIES = /* @__PURE__ */ new Set([
|
|
16790
|
-
".DS_Store",
|
|
16791
|
-
"Thumbs.db",
|
|
16792
|
-
".git",
|
|
16793
|
-
".svn",
|
|
16794
|
-
".hg",
|
|
16795
|
-
"node_modules",
|
|
16796
|
-
"__pycache__",
|
|
16797
|
-
".venv",
|
|
16798
|
-
".pytest_cache",
|
|
16799
|
-
".ruff_cache",
|
|
16800
|
-
".mypy_cache"
|
|
16801
|
-
]);
|
|
16802
|
-
/**
|
|
16803
|
-
* Anything else with a leading dot is skipped too — an editor's `.idea`, a
|
|
16804
|
-
* `.gitignore` that belongs to the checkout rather than the skill. The rule is
|
|
16805
|
-
* the same one the path schema already enforces, applied earlier so it reads as
|
|
16806
|
-
* "not part of the skill" instead of "this skill is broken".
|
|
16807
|
-
*/
|
|
16808
|
-
const isIgnoredEntry = (entry) => IGNORED_ENTRIES.has(entry) || entry.startsWith(".");
|
|
16809
|
-
/**
|
|
16810
|
-
* A file is binary if decoding it yields a NUL.
|
|
16811
|
-
*
|
|
16812
|
-
* Cheap, and right for what is being asked: the question is not "is this valid
|
|
16813
|
-
* UTF-8" but "can this round-trip through a JSON text column", and a NUL is
|
|
16814
|
-
* what actually breaks that on the way back out of SQLite.
|
|
16815
|
-
*/
|
|
16816
|
-
const looksBinary$1 = (content) => content.includes("\0");
|
|
16817
|
-
/**
|
|
16818
|
-
* Read one directory as an asset's files, or say why it cannot be one.
|
|
16819
|
-
*
|
|
16820
|
-
* Exported because memory's `global-memory/` is the same shape by a different
|
|
16821
|
-
* name: a directory whose whole contents are the asset. Sharing this keeps the
|
|
16822
|
-
* refusal rules — depth, file count, binaries, size — identical for both,
|
|
16823
|
-
* rather than letting a second copy drift into accepting what the first
|
|
16824
|
-
* rejects.
|
|
16825
|
-
*/
|
|
16826
|
-
const readSkillFiles = Effect.fn("skillDirectory.readSkillFiles")(function* (root) {
|
|
16827
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
16828
|
-
const path = yield* Path.Path;
|
|
16829
|
-
const files = [];
|
|
16830
|
-
const walk = Effect.fn("skillDirectory.walk")(function* (directory, prefix, depth) {
|
|
16831
|
-
if (depth > MAX_SKILL_DEPTH) return "too-deep";
|
|
16832
|
-
const entries = yield* fileSystem.readDirectory(directory).pipe(Effect.orElseSucceed(() => []));
|
|
16833
|
-
for (const entry of [...entries].sort()) {
|
|
16834
|
-
if (isIgnoredEntry(entry)) continue;
|
|
16835
|
-
const absolute = path.join(directory, entry);
|
|
16836
|
-
const relative = prefix === "" ? entry : `${prefix}/${entry}`;
|
|
16837
|
-
const info = yield* fileSystem.stat(absolute).pipe(Effect.orElseSucceed(() => void 0));
|
|
16838
|
-
if (info === void 0) return "unreadable";
|
|
16839
|
-
if (info.type === "Directory") {
|
|
16840
|
-
const nested = yield* walk(absolute, relative, depth + 1);
|
|
16841
|
-
if (nested !== null) return nested;
|
|
16842
|
-
continue;
|
|
16843
|
-
}
|
|
16844
|
-
if (info.type !== "File") continue;
|
|
16845
|
-
if (files.length >= 512) return "too-many-files";
|
|
16846
|
-
const content = yield* fileSystem.readFileString(absolute).pipe(Effect.orElseSucceed(() => void 0));
|
|
16847
|
-
if (content === void 0) return "unreadable";
|
|
16848
|
-
if (looksBinary$1(content)) return "binary-file";
|
|
16849
|
-
if (content.length > 524288) return "file-too-large";
|
|
16850
|
-
files.push({
|
|
16851
|
-
path: relative,
|
|
16852
|
-
content
|
|
16853
|
-
});
|
|
16854
|
-
}
|
|
16855
|
-
return null;
|
|
16856
|
-
});
|
|
16857
|
-
const refusal = yield* walk(root, "", 1);
|
|
16858
|
-
if (refusal !== null) return { refusal };
|
|
16859
|
-
if (agentAssetTotalBytes(files) > 4194304) return { refusal: "asset-too-large" };
|
|
16860
|
-
return { files };
|
|
16861
|
-
});
|
|
16862
|
-
/**
|
|
16863
|
-
* Read every skill directory under a user-scope `skills/` root.
|
|
16864
|
-
*
|
|
16865
|
-
* A directory with no `SKILL.md` is not a skill and is skipped silently —
|
|
16866
|
-
* Claude Code would ignore it too, so reporting it would be noise about
|
|
16867
|
-
* something that was never a skill in the first place.
|
|
16868
|
-
*/
|
|
16869
|
-
const readSkillDirectory = Effect.fn("skillDirectory.readSkillDirectory")(function* (skillsRoot) {
|
|
16870
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
16871
|
-
const path = yield* Path.Path;
|
|
16872
|
-
const entries = yield* fileSystem.readDirectory(skillsRoot).pipe(Effect.orElseSucceed(() => []));
|
|
16873
|
-
const skills = [];
|
|
16874
|
-
const refused = [];
|
|
16875
|
-
for (const entry of [...entries].sort()) {
|
|
16876
|
-
if (isIgnoredEntry(entry)) continue;
|
|
16877
|
-
const directory = path.join(skillsRoot, entry);
|
|
16878
|
-
if ((yield* fileSystem.stat(directory).pipe(Effect.orElseSucceed(() => void 0)))?.type !== "Directory") continue;
|
|
16879
|
-
if (!(yield* fileSystem.exists(path.join(directory, "SKILL.md")).pipe(Effect.orElseSucceed(() => false)))) continue;
|
|
16880
|
-
const read = yield* readSkillFiles(directory);
|
|
16881
|
-
if ("refusal" in read) {
|
|
16882
|
-
refused.push({
|
|
16883
|
-
name: entry,
|
|
16884
|
-
reason: read.refusal
|
|
16885
|
-
});
|
|
16886
|
-
continue;
|
|
16887
|
-
}
|
|
16888
|
-
skills.push({
|
|
16889
|
-
name: entry,
|
|
16890
|
-
files: read.files
|
|
16891
|
-
});
|
|
16892
|
-
}
|
|
16893
|
-
return {
|
|
16894
|
-
skills,
|
|
16895
|
-
refused
|
|
16896
|
-
};
|
|
16897
|
-
});
|
|
16898
|
-
/**
|
|
16899
|
-
* Replace a skill directory with exactly the asset's files.
|
|
16900
|
-
*
|
|
16901
|
-
* Written to a sibling and then swapped, so a crash mid-write leaves the old
|
|
16902
|
-
* skill intact rather than half of the new one. A skill is loaded as a unit and
|
|
16903
|
-
* a partially written one is instructions with the middle missing, which an
|
|
16904
|
-
* agent will happily follow.
|
|
16905
|
-
*/
|
|
16906
|
-
const writeSkillDirectory = Effect.fn("skillDirectory.writeSkillDirectory")(function* (input) {
|
|
16907
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
16908
|
-
const path = yield* Path.Path;
|
|
16909
|
-
const target = path.join(input.skillsRoot, input.name);
|
|
16910
|
-
const staging = path.join(input.skillsRoot, `.${input.name}.p4code-staging`);
|
|
16911
|
-
yield* fileSystem.makeDirectory(input.skillsRoot, { recursive: true });
|
|
16912
|
-
yield* fileSystem.remove(staging, {
|
|
16913
|
-
recursive: true,
|
|
16914
|
-
force: true
|
|
16915
|
-
});
|
|
16916
|
-
yield* fileSystem.makeDirectory(staging, { recursive: true });
|
|
16917
|
-
for (const file of input.files) {
|
|
16918
|
-
const destination = path.join(staging, file.path);
|
|
16919
|
-
yield* fileSystem.makeDirectory(path.dirname(destination), { recursive: true });
|
|
16920
|
-
yield* fileSystem.writeFileString(destination, file.content);
|
|
16921
|
-
}
|
|
16922
|
-
yield* fileSystem.remove(target, {
|
|
16923
|
-
recursive: true,
|
|
16924
|
-
force: true
|
|
16925
|
-
});
|
|
16926
|
-
yield* fileSystem.rename(staging, target);
|
|
16927
|
-
});
|
|
16928
|
-
const removeSkillDirectory = Effect.fn("skillDirectory.removeSkillDirectory")(function* (input) {
|
|
16929
|
-
const fileSystem = yield* FileSystem.FileSystem;
|
|
16930
|
-
const path = yield* Path.Path;
|
|
16931
|
-
yield* fileSystem.remove(path.join(input.skillsRoot, input.name), {
|
|
16932
|
-
recursive: true,
|
|
16933
|
-
force: true
|
|
16934
|
-
});
|
|
16935
|
-
});
|
|
16936
|
-
//#endregion
|
|
16937
17143
|
//#region src/sync/memoryFile.ts
|
|
16938
17144
|
/**
|
|
16939
17145
|
* Reading and writing user-scope memory files.
|
|
@@ -17214,12 +17420,20 @@ const PROCEED = { _tag: "proceed" };
|
|
|
17214
17420
|
* - A skill without `SKILL.md` would be invisible to discovery: the picker
|
|
17215
17421
|
* skips directories without one, so the create would look like it silently
|
|
17216
17422
|
* failed. Refused with the reason instead.
|
|
17423
|
+
* - An agent definition is one markdown file whose name is the asset's own, and
|
|
17424
|
+
* anything else is invisible in the same way — the provider reads `.md` in
|
|
17425
|
+
* that directory and nothing else. The frontmatter check is the writer's,
|
|
17426
|
+
* because it needs a YAML parse this module deliberately does without.
|
|
17217
17427
|
* - An existing local name is never overwritten. Create means create; editing
|
|
17218
17428
|
* is a different operation with a different surface.
|
|
17219
17429
|
*/
|
|
17220
17430
|
function decideCreateLocal(input) {
|
|
17221
17431
|
if (input.asset.kind === "mcp") return refuse("refused", "MCP registrations are created through mcp.save, not as files.");
|
|
17222
17432
|
if (input.asset.kind === "skill" && !input.asset.files.some((file) => file.path === "SKILL.md")) return refuse("refused", "A skill needs a SKILL.md at its root; without one, discovery skips the directory.");
|
|
17433
|
+
if (input.asset.kind === "agent") {
|
|
17434
|
+
const [file, ...rest] = input.asset.files;
|
|
17435
|
+
if (!input.asset.name.endsWith(".md") || rest.length > 0 || file?.path !== input.asset.name) return refuse("refused", "An agent definition is a single markdown file named after the asset, for example 'reviewer.md' holding 'reviewer.md'.");
|
|
17436
|
+
}
|
|
17223
17437
|
if (input.localNames.has(input.asset.name)) return refuse("exists", `An asset named '${input.asset.name}' already exists locally.`);
|
|
17224
17438
|
return PROCEED;
|
|
17225
17439
|
}
|
|
@@ -17462,6 +17676,22 @@ const make$59 = Effect.gen(function* () {
|
|
|
17462
17676
|
name
|
|
17463
17677
|
}).pipe(Effect.ignore)
|
|
17464
17678
|
},
|
|
17679
|
+
{
|
|
17680
|
+
kind: "agent",
|
|
17681
|
+
resolveRoot: Effect.gen(function* () {
|
|
17682
|
+
return yield* resolveClaudeUserAgentsDir(yield* claudeHome());
|
|
17683
|
+
}),
|
|
17684
|
+
read: (root) => readAgentDefinitions(root),
|
|
17685
|
+
write: ({ root, name, files }) => writeAgentDefinition({
|
|
17686
|
+
agentsRoot: root,
|
|
17687
|
+
name,
|
|
17688
|
+
files
|
|
17689
|
+
}),
|
|
17690
|
+
remove: ({ root, name }) => removeAgentDefinition({
|
|
17691
|
+
agentsRoot: root,
|
|
17692
|
+
name
|
|
17693
|
+
}).pipe(Effect.ignore)
|
|
17694
|
+
},
|
|
17465
17695
|
{
|
|
17466
17696
|
kind: "mcp",
|
|
17467
17697
|
resolveRoot: Effect.succeed(path.join(config.stateDir, "mcp")),
|
|
@@ -18103,6 +18333,262 @@ const make$59 = Effect.gen(function* () {
|
|
|
18103
18333
|
});
|
|
18104
18334
|
const layer$54 = Layer.effect(AssetSync, make$59);
|
|
18105
18335
|
//#endregion
|
|
18336
|
+
//#region src/provider/CompressPrompts.ts
|
|
18337
|
+
/**
|
|
18338
|
+
* Caveman-style response compression rulesets, adapted from the caveman
|
|
18339
|
+
* plugin's SKILL.md. Pre-baked per level so injection is a lookup, not a
|
|
18340
|
+
* runtime template. Applies to assistant prose only: code blocks, commits,
|
|
18341
|
+
* PRs, error strings, and safety-critical text stay uncompressed.
|
|
18342
|
+
*/
|
|
18343
|
+
const COMPRESS_SHARED_RULES = `Respond terse. All technical substance stays. Only fluff dies.
|
|
18344
|
+
|
|
18345
|
+
## Persistence
|
|
18346
|
+
|
|
18347
|
+
Active every response. No revert after many turns. No filler drift. Still active if unsure.
|
|
18348
|
+
|
|
18349
|
+
## Rules
|
|
18350
|
+
|
|
18351
|
+
Drop filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Short synonyms (big not extensive, fix not "implement a solution for"). No decorative tables/emoji, no dumping long raw error logs unless asked - quote shortest decisive line. Standard well-known tech acronyms OK (DB/API/HTTP); never invent new abbreviations (cfg/impl/req/res/fn) - tokenizers split them same as the full word: zero tokens saved, reader still decodes. No causal arrows (->) in prose. Technical terms exact. Code blocks unchanged. Errors quoted exact.
|
|
18352
|
+
|
|
18353
|
+
Never drop not/never/no/only/except: flipping the meaning costs more than any token saved. Numbers and units exact.
|
|
18354
|
+
|
|
18355
|
+
Tool calls: fire direct. No plan, no progress note between calls, never announce the next call. If the harness asks for a short lead-in before tool calls, write that one line and nothing more. Otherwise text before a call only to clarify, warn about a security or irreversible action, or resolve ambiguity.
|
|
18356
|
+
|
|
18357
|
+
Preserve the user's dominant language: reply in the language the user writes, whatever language the examples or the surrounding context use. Compress the style, not the language. Every emitted line in that language, lead-in lines included, not just the final reply. Always keep technical terms, code, API names, CLI commands, commit-type keywords (feat/fix/...), and exact error strings verbatim unless the user explicitly asks for translation.
|
|
18358
|
+
|
|
18359
|
+
Dropping articles applies to article languages only. Where small markers carry case or role (particles, postpositions), keep them: grammar, not filler. Compress politeness and filler instead.
|
|
18360
|
+
|
|
18361
|
+
No self-reference. Never name or announce the style. No compressed answer plus normal recap - compressed output only. Exception: user explicitly asks what the mode is.
|
|
18362
|
+
|
|
18363
|
+
## Auto-Clarity
|
|
18364
|
+
|
|
18365
|
+
Drop compression and write normally when:
|
|
18366
|
+
- Security warnings
|
|
18367
|
+
- Irreversible action confirmations
|
|
18368
|
+
- Multi-step sequences where fragment order or omitted conjunctions risk misread
|
|
18369
|
+
- Compression itself creates technical ambiguity (e.g. "migrate table drop column backup first" - order unclear without articles and conjunctions)
|
|
18370
|
+
- User asks to clarify or repeats a question
|
|
18371
|
+
|
|
18372
|
+
Resume compression after the clear part is done. The example below shows the format only: write the warning in the session's language, not the example's.
|
|
18373
|
+
|
|
18374
|
+
Example - destructive operation:
|
|
18375
|
+
> **Warning:** This will permanently delete all rows in the \`users\` table and cannot be undone.
|
|
18376
|
+
> \`\`\`sql
|
|
18377
|
+
> DROP TABLE users;
|
|
18378
|
+
> \`\`\`
|
|
18379
|
+
> Compression resumes. Verify a backup exists first.
|
|
18380
|
+
|
|
18381
|
+
## Boundaries
|
|
18382
|
+
|
|
18383
|
+
Anything persisted outside the chat: write normal, uncompressed prose. Covers code, code comments, commit messages, PR and issue text, docs, memory files, and messages to third parties.`;
|
|
18384
|
+
const COMPRESS_RULESETS = {
|
|
18385
|
+
lite: `## Response compression: lite
|
|
18386
|
+
|
|
18387
|
+
${COMPRESS_SHARED_RULES}
|
|
18388
|
+
|
|
18389
|
+
## Intensity: lite
|
|
18390
|
+
|
|
18391
|
+
No filler or hedging. Keep articles and full sentences. Professional but tight.
|
|
18392
|
+
|
|
18393
|
+
Example - "Why does my React component re-render?"
|
|
18394
|
+
"Your component re-renders because you create a new object reference each render. Wrap it in \`useMemo\`."
|
|
18395
|
+
|
|
18396
|
+
Example - "Explain database connection pooling."
|
|
18397
|
+
"Connection pooling reuses open connections instead of creating a new one per request. Avoids repeated handshake overhead."`,
|
|
18398
|
+
full: `## Response compression: full
|
|
18399
|
+
|
|
18400
|
+
${COMPRESS_SHARED_RULES}
|
|
18401
|
+
|
|
18402
|
+
## Intensity: full
|
|
18403
|
+
|
|
18404
|
+
Drop articles (a/an/the), fragments OK, short synonyms. Pattern: [thing] [action] [reason]. [next step].
|
|
18405
|
+
|
|
18406
|
+
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
|
|
18407
|
+
Yes: "Bug in auth middleware. Token expiry check use \`<\` not \`<=\`. Fix:"
|
|
18408
|
+
|
|
18409
|
+
Example - "Why does my React component re-render?"
|
|
18410
|
+
"New object ref each render. Inline object prop = new ref = re-render. Wrap in \`useMemo\`."
|
|
18411
|
+
|
|
18412
|
+
Example - "Explain database connection pooling."
|
|
18413
|
+
"Pool reuses open DB connections. No new connection per request. Skips handshake overhead."`,
|
|
18414
|
+
ultra: `## Response compression: ultra
|
|
18415
|
+
|
|
18416
|
+
${COMPRESS_SHARED_RULES}
|
|
18417
|
+
|
|
18418
|
+
## Intensity: ultra
|
|
18419
|
+
|
|
18420
|
+
Drop articles, fragments OK, short synonyms. Also strip conjunctions when cause-then-effect stays unambiguous. One word when one word is enough. State each fact once. Still no invented abbreviations (cfg/impl/req/res/fn/auth) and still no arrows (X -> Y): both measure as zero token saving and cost decode clarity. Code symbols, function names, API names, error strings: never touch.
|
|
18421
|
+
|
|
18422
|
+
Example - "Why does my React component re-render?"
|
|
18423
|
+
"Inline object prop, new ref, re-render. \`useMemo\`."
|
|
18424
|
+
|
|
18425
|
+
Example - "Explain database connection pooling."
|
|
18426
|
+
"Pool reuses open DB connections. No per-request handshake."`
|
|
18427
|
+
};
|
|
18428
|
+
const COMPRESS_TURN_REMINDERS = {
|
|
18429
|
+
lite: "[response style: compressed lite - no filler/hedging, full sentences; keep negations and numbers exact; code/commits/security text normal]",
|
|
18430
|
+
full: "[response style: compressed full - terse fragments, drop articles/filler; keep negations and numbers exact; code/commits/security text normal]",
|
|
18431
|
+
ultra: "[response style: compressed ultra - maximum terseness, one word when enough; keep negations and numbers exact; code/commits/security text normal]"
|
|
18432
|
+
};
|
|
18433
|
+
function compressRulesetFor(mode) {
|
|
18434
|
+
return mode === "off" ? void 0 : COMPRESS_RULESETS[mode];
|
|
18435
|
+
}
|
|
18436
|
+
function compressTurnReminderFor(mode) {
|
|
18437
|
+
return mode === "off" ? void 0 : COMPRESS_TURN_REMINDERS[mode];
|
|
18438
|
+
}
|
|
18439
|
+
//#endregion
|
|
18440
|
+
//#region src/sync/agentPresets.ts
|
|
18441
|
+
/**
|
|
18442
|
+
* The row ceiling both presets impose.
|
|
18443
|
+
*
|
|
18444
|
+
* A cap rather than "be brief": an uncapped list is how a subagent turns a
|
|
18445
|
+
* bounded answer back into a page, and a number is the only form of that
|
|
18446
|
+
* instruction a model cannot negotiate with.
|
|
18447
|
+
*/
|
|
18448
|
+
const PRESET_MAX_RESULT_ROWS = 20;
|
|
18449
|
+
/**
|
|
18450
|
+
* Named `p4-*` on purpose.
|
|
18451
|
+
*
|
|
18452
|
+
* The directory is shared with whatever the user has written by hand, and
|
|
18453
|
+
* installing a preset must never quietly replace their own `reviewer`. The
|
|
18454
|
+
* prefix also makes provenance obvious in the settings list, where every kind
|
|
18455
|
+
* of agent definition appears in one column.
|
|
18456
|
+
*/
|
|
18457
|
+
const PRESET_NAME_PREFIX = "p4-";
|
|
18458
|
+
/**
|
|
18459
|
+
* What every preset says about what it is handed.
|
|
18460
|
+
*
|
|
18461
|
+
* Shared because it is the same contract whatever the subagent does: no
|
|
18462
|
+
* inherited history, exact paths, and a refusal instead of a guess. The last
|
|
18463
|
+
* clause is the one that earns its place — a subagent that invents the missing
|
|
18464
|
+
* half of an under-specified prompt returns something confident and wrong,
|
|
18465
|
+
* which costs more than the turn that asked for it.
|
|
18466
|
+
*/
|
|
18467
|
+
const INPUT_CONTRACT = `## What you are given
|
|
18468
|
+
|
|
18469
|
+
You inherit no session history. The prompt is the whole of what you know: one
|
|
18470
|
+
request, plus whatever exact paths the orchestrator has already established.
|
|
18471
|
+
Treat those paths as authoritative and start from them rather than searching for
|
|
18472
|
+
them again.
|
|
18473
|
+
|
|
18474
|
+
What the prompt does not say, you do not assume. If it names no request, or
|
|
18475
|
+
names a path that is not there, answer with the BLOCKED line below. Guessing at
|
|
18476
|
+
the missing half of a request produces an answer that reads as confident and is
|
|
18477
|
+
not checkable, which is worse than no answer.`;
|
|
18478
|
+
/**
|
|
18479
|
+
* What every preset says about the answer.
|
|
18480
|
+
*
|
|
18481
|
+
* The two sentinels matter as much as the table. Without `NONE` a clean run
|
|
18482
|
+
* comes back as a paragraph explaining that everything looked fine; without
|
|
18483
|
+
* `BLOCKED` an under-specified request comes back as an apology. Both are the
|
|
18484
|
+
* narrative this contract exists to remove, and both are cheap to name.
|
|
18485
|
+
*/
|
|
18486
|
+
const outputContract = (row, extra) => [
|
|
18487
|
+
"## What you return",
|
|
18488
|
+
"",
|
|
18489
|
+
"Your final message is the result. It is the table and nothing else: no",
|
|
18490
|
+
"preamble, no restatement of the request, no summary underneath it.",
|
|
18491
|
+
"",
|
|
18492
|
+
`One row per finding, at most ${PRESET_MAX_RESULT_ROWS}, most important first:`,
|
|
18493
|
+
"",
|
|
18494
|
+
` ${row}`,
|
|
18495
|
+
"",
|
|
18496
|
+
"Nothing to report: the single word NONE.",
|
|
18497
|
+
"Request insufficient: BLOCKED - <what is missing>, on one line.",
|
|
18498
|
+
"",
|
|
18499
|
+
"Paths are repository-relative and carry a line number. A row without one is",
|
|
18500
|
+
"a row you have not verified: open the file and get it.",
|
|
18501
|
+
...extra
|
|
18502
|
+
].join("\n");
|
|
18503
|
+
/**
|
|
18504
|
+
* Assemble one definition.
|
|
18505
|
+
*
|
|
18506
|
+
* The compression ruleset goes last so the contract above it is read first.
|
|
18507
|
+
* Both are instructions to the same model and neither overrides the other:
|
|
18508
|
+
* the contract fixes the shape, the ruleset fixes the prose inside it.
|
|
18509
|
+
*/
|
|
18510
|
+
const buildPreset = (input) => {
|
|
18511
|
+
const name = `${PRESET_NAME_PREFIX}${input.name}`;
|
|
18512
|
+
const content = [
|
|
18513
|
+
"---",
|
|
18514
|
+
`name: ${name}`,
|
|
18515
|
+
`description: ${JSON.stringify(input.description)}`,
|
|
18516
|
+
`tools: ${input.tools}`,
|
|
18517
|
+
"---",
|
|
18518
|
+
"",
|
|
18519
|
+
input.body.trim(),
|
|
18520
|
+
"",
|
|
18521
|
+
compressRulesetFor("full") ?? "",
|
|
18522
|
+
""
|
|
18523
|
+
].join("\n");
|
|
18524
|
+
const fileName = `${name}.md`;
|
|
18525
|
+
return {
|
|
18526
|
+
name: fileName,
|
|
18527
|
+
summary: input.summary,
|
|
18528
|
+
files: [{
|
|
18529
|
+
path: fileName,
|
|
18530
|
+
content
|
|
18531
|
+
}]
|
|
18532
|
+
};
|
|
18533
|
+
};
|
|
18534
|
+
/** Every preset, in the order `p4c agents presets` lists them. */
|
|
18535
|
+
const AGENT_PRESETS = [buildPreset({
|
|
18536
|
+
name: "investigator",
|
|
18537
|
+
description: "Find where something lives in the codebase. Use when the answer is a set of locations rather than a change. Returns a fixed table of path:line rows and nothing else.",
|
|
18538
|
+
tools: "Read, Grep, Glob, Bash",
|
|
18539
|
+
summary: "Locates code and answers with a path:line table.",
|
|
18540
|
+
body: `You locate code. You do not change it, and you say no more about any location
|
|
18541
|
+
than the one clause that explains why it is in the list.
|
|
18542
|
+
|
|
18543
|
+
${INPUT_CONTRACT}
|
|
18544
|
+
|
|
18545
|
+
${outputContract("path:line - symbol - one clause on why it matters", [
|
|
18546
|
+
"",
|
|
18547
|
+
"Symbol is the function, type or constant the line belongs to, not a paraphrase",
|
|
18548
|
+
"of the line. Where there is genuinely no symbol, name the block: a route table,",
|
|
18549
|
+
"a config literal."
|
|
18550
|
+
])}`
|
|
18551
|
+
}), buildPreset({
|
|
18552
|
+
name: "reviewer",
|
|
18553
|
+
description: "Review named files or a diff for defects. Use when the answer is a list of findings rather than a change. Returns a fixed table of path:line findings and nothing else.",
|
|
18554
|
+
tools: "Read, Grep, Glob, Bash",
|
|
18555
|
+
summary: "Reviews named files and answers with a severity-tagged finding table.",
|
|
18556
|
+
body: `You review code you did not write. You do not change it, and you do not report
|
|
18557
|
+
what it does well - the orchestrator asked what is wrong, and a list padded with
|
|
18558
|
+
approval is a list that has to be re-read to find the three rows that matter.
|
|
18559
|
+
|
|
18560
|
+
${INPUT_CONTRACT}
|
|
18561
|
+
|
|
18562
|
+
${outputContract("path:line - severity - what is wrong, and what it breaks", [
|
|
18563
|
+
"",
|
|
18564
|
+
"Severity is one of three words:",
|
|
18565
|
+
"",
|
|
18566
|
+
" bug - wrong behaviour reachable in ordinary use. Name the input that reaches it.",
|
|
18567
|
+
" risk - correct today, and depends on something the code does not state.",
|
|
18568
|
+
" nit - style or clarity. Include only when the file is otherwise clean.",
|
|
18569
|
+
"",
|
|
18570
|
+
"A finding you cannot reach with a concrete input is a risk, not a bug. Saying",
|
|
18571
|
+
"so is what keeps the bug rows worth reading."
|
|
18572
|
+
])}`
|
|
18573
|
+
})];
|
|
18574
|
+
/**
|
|
18575
|
+
* Look one up by the name someone typed.
|
|
18576
|
+
*
|
|
18577
|
+
* Both spellings are accepted - `investigator` and `p4-investigator.md` - because
|
|
18578
|
+
* the listing shows the filename and the sentence people write is the bare word,
|
|
18579
|
+
* and refusing one of the two would be refusing whichever the person happened to
|
|
18580
|
+
* read most recently.
|
|
18581
|
+
*/
|
|
18582
|
+
const findAgentPreset = (requested) => {
|
|
18583
|
+
const wanted = requested.trim().toLowerCase();
|
|
18584
|
+
if (wanted.length === 0) return void 0;
|
|
18585
|
+
return AGENT_PRESETS.find((preset) => {
|
|
18586
|
+
const fileName = preset.name.toLowerCase();
|
|
18587
|
+
const bare = fileName.slice(3, -3);
|
|
18588
|
+
return wanted === fileName || wanted === bare || wanted === `${PRESET_NAME_PREFIX}${bare}`;
|
|
18589
|
+
});
|
|
18590
|
+
};
|
|
18591
|
+
//#endregion
|
|
18106
18592
|
//#region src/cli/assets.ts
|
|
18107
18593
|
const SyncServicesLive = layer$54.pipe(Layer.provideMerge(layer$55), Layer.provideMerge(layer$56), Layer.provideMerge(layer$60.pipe(Layer.provide(layer$67))), Layer.provideMerge(layer$67), Layer.provideMerge(FetchHttpClient.layer));
|
|
18108
18594
|
const runWithSync = (flags, run) => Effect.gen(function* () {
|
|
@@ -18146,6 +18632,14 @@ const MCP_WORDS = {
|
|
|
18146
18632
|
rootLabel: "MCP",
|
|
18147
18633
|
nameDescription: "Registration filename, for example linear.json."
|
|
18148
18634
|
};
|
|
18635
|
+
const AGENT_WORDS = {
|
|
18636
|
+
kind: "agent",
|
|
18637
|
+
command: "agents",
|
|
18638
|
+
singular: "agent definition",
|
|
18639
|
+
plural: "agent definitions",
|
|
18640
|
+
rootLabel: "Agents",
|
|
18641
|
+
nameDescription: "Definition filename, for example p4-reviewer.md."
|
|
18642
|
+
};
|
|
18149
18643
|
const rootFor = (status, kind) => status.roots.find((root) => root.kind === kind)?.path ?? null;
|
|
18150
18644
|
/**
|
|
18151
18645
|
* Exported for tests. These two are the only place a wrong report becomes a
|
|
@@ -18208,7 +18702,8 @@ const formatReport = (report) => {
|
|
|
18208
18702
|
const ALL_WORDS = [
|
|
18209
18703
|
SKILL_WORDS,
|
|
18210
18704
|
MEMORY_WORDS,
|
|
18211
|
-
MCP_WORDS
|
|
18705
|
+
MCP_WORDS,
|
|
18706
|
+
AGENT_WORDS
|
|
18212
18707
|
];
|
|
18213
18708
|
/**
|
|
18214
18709
|
* The whole picture, in one page.
|
|
@@ -18340,6 +18835,67 @@ const makeAssetCommands = (words) => {
|
|
|
18340
18835
|
const skillCommands = makeAssetCommands(SKILL_WORDS);
|
|
18341
18836
|
const memoryCommands = makeAssetCommands(MEMORY_WORDS);
|
|
18342
18837
|
const mcpCommands = makeAssetCommands(MCP_WORDS);
|
|
18838
|
+
const agentCommands = makeAssetCommands(AGENT_WORDS);
|
|
18839
|
+
/**
|
|
18840
|
+
* What one install attempt did, in a sentence.
|
|
18841
|
+
*
|
|
18842
|
+
* Exported for tests: `exists` in particular must never read as success. The
|
|
18843
|
+
* whole point of refusing to overwrite is that someone who has edited a preset
|
|
18844
|
+
* keeps their edits, and a line saying "installed" would tell them the opposite.
|
|
18845
|
+
*/
|
|
18846
|
+
const formatPresetInstall = (input) => {
|
|
18847
|
+
if (input.outcome === "created") return `Installed ${input.name}${input.path === null ? "" : ` at ${input.path}`}`;
|
|
18848
|
+
if (input.outcome === "exists") return `Skipped ${input.name}: it already exists here, and installing would overwrite your copy.`;
|
|
18849
|
+
return `Could not install ${input.name}: ${input.detail ?? input.outcome}`;
|
|
18850
|
+
};
|
|
18851
|
+
/**
|
|
18852
|
+
* `p4c agents presets` — what is on offer, before anything is written.
|
|
18853
|
+
*
|
|
18854
|
+
* Its own command rather than a flag on `install`, because reading the list is
|
|
18855
|
+
* what people do first and a listing that requires naming an install target is
|
|
18856
|
+
* not a listing.
|
|
18857
|
+
*/
|
|
18858
|
+
const presetsCommand = Command.make("presets", { ...authLocationFlags }).pipe(Command.withDescription("List the agent definitions p4code ships, without installing any."), Command.withHandler(() => Console.log([
|
|
18859
|
+
...AGENT_PRESETS.map((preset) => ` ${preset.name.padEnd(28)} ${preset.summary}`),
|
|
18860
|
+
"",
|
|
18861
|
+
"Install one with: p4c agents install <name>"
|
|
18862
|
+
].join("\n"))));
|
|
18863
|
+
/**
|
|
18864
|
+
* Install a shipped definition onto this machine, and only this machine.
|
|
18865
|
+
*
|
|
18866
|
+
* `createLocal` rather than a hub write, so an installed preset starts out
|
|
18867
|
+
* exactly like one written by hand: local, unmanaged, and shared only when
|
|
18868
|
+
* something explicitly publishes it. It also means this works with no hub
|
|
18869
|
+
* configured, which is when a fresh machine most wants it.
|
|
18870
|
+
*/
|
|
18871
|
+
const presetInstallCommand = Command.make("install", {
|
|
18872
|
+
...authLocationFlags,
|
|
18873
|
+
name: Argument.string("name").pipe(Argument.withDescription("Preset to install, for example investigator."), Argument.optional),
|
|
18874
|
+
all: Flag.boolean("all").pipe(Flag.withDescription("Install every preset p4code ships."))
|
|
18875
|
+
}).pipe(Command.withDescription("Install a p4code agent definition into this machine's agents directory."), Command.withHandler((flags) => runWithSync(flags, ({ sync }) => Effect.gen(function* () {
|
|
18876
|
+
const requested = Option.getOrUndefined(flags.name);
|
|
18877
|
+
if (!flags.all && requested === void 0) return yield* Console.error("Name a preset, or pass --all to install every one.\nSee them with: p4c agents presets");
|
|
18878
|
+
const chosen = flags.all ? AGENT_PRESETS : (() => {
|
|
18879
|
+
const found = findAgentPreset(requested ?? "");
|
|
18880
|
+
return found === void 0 ? [] : [found];
|
|
18881
|
+
})();
|
|
18882
|
+
if (chosen.length === 0) return yield* Console.error(`No preset named ${requested ?? ""}. See them with: p4c agents presets`);
|
|
18883
|
+
const lines = [];
|
|
18884
|
+
for (const preset of chosen) {
|
|
18885
|
+
const result = yield* sync.createLocal({
|
|
18886
|
+
kind: AGENT_WORDS.kind,
|
|
18887
|
+
name: preset.name,
|
|
18888
|
+
files: preset.files
|
|
18889
|
+
});
|
|
18890
|
+
lines.push(formatPresetInstall({
|
|
18891
|
+
name: preset.name,
|
|
18892
|
+
outcome: result.outcome,
|
|
18893
|
+
path: result.path,
|
|
18894
|
+
detail: result.detail
|
|
18895
|
+
}));
|
|
18896
|
+
}
|
|
18897
|
+
return yield* Console.log(lines.join("\n"));
|
|
18898
|
+
}))));
|
|
18343
18899
|
const shareAllCommand = Command.make("share-all", { ...authLocationFlags }).pipe(Command.withDescription("Share everything, including new items, automatically. The default."), Command.withHandler((flags) => runWithSync(flags, ({ sync, link }) => Effect.gen(function* () {
|
|
18344
18900
|
yield* link.setShareMode("all");
|
|
18345
18901
|
yield* Console.log(formatStatus(yield* sync.status));
|
|
@@ -18392,6 +18948,23 @@ const mcpCommand = Command.make("mcp", {}).pipe(Command.withDescription("Share M
|
|
|
18392
18948
|
mcpCommands.showCommand,
|
|
18393
18949
|
mcpCommands.removeCommand
|
|
18394
18950
|
]));
|
|
18951
|
+
/**
|
|
18952
|
+
* `p4c agents` — the subagent definitions this machine runs.
|
|
18953
|
+
*
|
|
18954
|
+
* The same six subcommands every kind gets, plus the two that are particular to
|
|
18955
|
+
* this one: p4code ships definitions, and a definition is the only place a
|
|
18956
|
+
* subagent's input and output contract can be stated.
|
|
18957
|
+
*/
|
|
18958
|
+
const agentsCommand = Command.make("agents", {}).pipe(Command.withDescription("Share subagent definitions across your machines, and install p4code's."), Command.withSubcommands([
|
|
18959
|
+
agentCommands.statusCommand,
|
|
18960
|
+
agentCommands.syncCommand,
|
|
18961
|
+
agentCommands.publishCommand,
|
|
18962
|
+
agentCommands.unpublishCommand,
|
|
18963
|
+
agentCommands.showCommand,
|
|
18964
|
+
agentCommands.removeCommand,
|
|
18965
|
+
presetsCommand,
|
|
18966
|
+
presetInstallCommand
|
|
18967
|
+
]));
|
|
18395
18968
|
/** Exported for the hub command, which owns `connect`/`disconnect`. */
|
|
18396
18969
|
const hubLinkCommands = [hubConnectCommand, hubDisconnectCommand];
|
|
18397
18970
|
//#endregion
|
|
@@ -45796,12 +46369,12 @@ function decodeProviderDriverKind(providerName, operation) {
|
|
|
45796
46369
|
cause
|
|
45797
46370
|
})));
|
|
45798
46371
|
}
|
|
45799
|
-
function isRecord$
|
|
46372
|
+
function isRecord$5(value) {
|
|
45800
46373
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
45801
46374
|
}
|
|
45802
46375
|
function mergeRuntimePayload(existing, next) {
|
|
45803
46376
|
if (next === void 0) return existing ?? null;
|
|
45804
|
-
if (isRecord$
|
|
46377
|
+
if (isRecord$5(existing) && isRecord$5(next)) return {
|
|
45805
46378
|
...existing,
|
|
45806
46379
|
...next
|
|
45807
46380
|
};
|
|
@@ -47066,6 +47639,7 @@ function buildServerProvider(input) {
|
|
|
47066
47639
|
...input.presentation.badgeLabel ? { badgeLabel: input.presentation.badgeLabel } : {},
|
|
47067
47640
|
...typeof input.presentation.showInteractionModeToggle === "boolean" ? { showInteractionModeToggle: input.presentation.showInteractionModeToggle } : {},
|
|
47068
47641
|
...typeof input.presentation.requiresNewThreadForModelChange === "boolean" ? { requiresNewThreadForModelChange: input.presentation.requiresNewThreadForModelChange } : {},
|
|
47642
|
+
...input.presentation.supportedRuntimeModes ? { supportedRuntimeModes: input.presentation.supportedRuntimeModes } : {},
|
|
47069
47643
|
enabled: input.enabled,
|
|
47070
47644
|
installed: input.probe.installed,
|
|
47071
47645
|
version: input.probe.version,
|
|
@@ -47522,7 +48096,7 @@ var OpenCodeRuntime = class extends Context.Service()("@p4code/cli/provider/open
|
|
|
47522
48096
|
const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$71));
|
|
47523
48097
|
//#endregion
|
|
47524
48098
|
//#region src/textGeneration/TextGenerationUtils.ts
|
|
47525
|
-
const isTextGenerationError$
|
|
48099
|
+
const isTextGenerationError$3 = Schema$1.is(TextGenerationError);
|
|
47526
48100
|
/** Convert an Effect Schema to a flat JSON Schema object, inlining `$defs` when present. */
|
|
47527
48101
|
function toJsonSchemaObject(schema) {
|
|
47528
48102
|
const document = Schema$1.toJsonSchemaDocument(schema);
|
|
@@ -47567,7 +48141,7 @@ function cliLabel(cliName) {
|
|
|
47567
48141
|
* and Claude (and future providers) can share the same logic.
|
|
47568
48142
|
*/
|
|
47569
48143
|
function normalizeCliError(cliName, operation, error, fallback) {
|
|
47570
|
-
if (isTextGenerationError$
|
|
48144
|
+
if (isTextGenerationError$3(error)) return error;
|
|
47571
48145
|
if (error instanceof Error) {
|
|
47572
48146
|
const lower = error.message.toLowerCase();
|
|
47573
48147
|
if (error.message.includes(`Command not found: ${cliName}`) || lower.includes(`spawn ${cliName}`) || lower.includes("enoent")) return new TextGenerationError({
|
|
@@ -49003,88 +49577,6 @@ function formatAskUserQuestionAnswers(answers) {
|
|
|
49003
49577
|
return formatted;
|
|
49004
49578
|
}
|
|
49005
49579
|
//#endregion
|
|
49006
|
-
//#region src/provider/CompressPrompts.ts
|
|
49007
|
-
/**
|
|
49008
|
-
* Caveman-style response compression rulesets, adapted from the caveman
|
|
49009
|
-
* plugin's SKILL.md. Pre-baked per level so injection is a lookup, not a
|
|
49010
|
-
* runtime template. Applies to assistant prose only: code blocks, commits,
|
|
49011
|
-
* PRs, error strings, and safety-critical text stay uncompressed.
|
|
49012
|
-
*/
|
|
49013
|
-
const COMPRESS_SHARED_RULES = `Respond terse. All technical substance stays. Only fluff dies.
|
|
49014
|
-
|
|
49015
|
-
## Persistence
|
|
49016
|
-
|
|
49017
|
-
Active every response. No revert after many turns. No filler drift. Still active if unsure.
|
|
49018
|
-
|
|
49019
|
-
## Rules
|
|
49020
|
-
|
|
49021
|
-
Drop filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Short synonyms (big not extensive, fix not "implement a solution for"). No tool-call narration, no decorative tables/emoji, no dumping long raw error logs unless asked - quote shortest decisive line. Standard well-known tech acronyms OK (DB/API/HTTP); never invent new abbreviations (cfg/impl/req/res/fn) - tokenizers split them same as the full word: zero tokens saved, reader still decodes. No causal arrows (->) in prose. Technical terms exact. Code blocks unchanged. Errors quoted exact.
|
|
49022
|
-
|
|
49023
|
-
Preserve the user's dominant language: compress the style, not the language. Always keep technical terms, code, API names, CLI commands, commit-type keywords (feat/fix/...), and exact error strings verbatim unless the user explicitly asks for translation.
|
|
49024
|
-
|
|
49025
|
-
No self-reference. Never name or announce the style. No compressed answer plus normal recap - compressed output only. Exception: user explicitly asks what the mode is.
|
|
49026
|
-
|
|
49027
|
-
## Auto-Clarity
|
|
49028
|
-
|
|
49029
|
-
Drop compression and write normally when:
|
|
49030
|
-
- Security warnings
|
|
49031
|
-
- Irreversible action confirmations
|
|
49032
|
-
- Multi-step sequences where fragment order or omitted conjunctions risk misread
|
|
49033
|
-
- Compression itself creates technical ambiguity
|
|
49034
|
-
- User asks to clarify or repeats a question
|
|
49035
|
-
|
|
49036
|
-
Resume compression after the clear part is done.
|
|
49037
|
-
|
|
49038
|
-
## Boundaries
|
|
49039
|
-
|
|
49040
|
-
Code, commit messages, and PR descriptions: write normal, uncompressed.`;
|
|
49041
|
-
const COMPRESS_RULESETS = {
|
|
49042
|
-
lite: `## Response compression: lite
|
|
49043
|
-
|
|
49044
|
-
${COMPRESS_SHARED_RULES}
|
|
49045
|
-
|
|
49046
|
-
## Intensity: lite
|
|
49047
|
-
|
|
49048
|
-
No filler or hedging. Keep articles and full sentences. Professional but tight.
|
|
49049
|
-
|
|
49050
|
-
Example - "Why does my React component re-render?"
|
|
49051
|
-
"Your component re-renders because you create a new object reference each render. Wrap it in \`useMemo\`."`,
|
|
49052
|
-
full: `## Response compression: full
|
|
49053
|
-
|
|
49054
|
-
${COMPRESS_SHARED_RULES}
|
|
49055
|
-
|
|
49056
|
-
## Intensity: full
|
|
49057
|
-
|
|
49058
|
-
Drop articles (a/an/the), fragments OK, short synonyms. Pattern: [thing] [action] [reason]. [next step].
|
|
49059
|
-
|
|
49060
|
-
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
|
|
49061
|
-
Yes: "Bug in auth middleware. Token expiry check use \`<\` not \`<=\`. Fix:"
|
|
49062
|
-
|
|
49063
|
-
Example - "Why does my React component re-render?"
|
|
49064
|
-
"New object ref each render. Inline object prop = new ref = re-render. Wrap in \`useMemo\`."`,
|
|
49065
|
-
ultra: `## Response compression: ultra
|
|
49066
|
-
|
|
49067
|
-
${COMPRESS_SHARED_RULES}
|
|
49068
|
-
|
|
49069
|
-
## Intensity: ultra
|
|
49070
|
-
|
|
49071
|
-
Drop articles, fragments OK, short synonyms. Also strip conjunctions when cause-then-effect stays unambiguous. One word when one word is enough. State each fact once. Code symbols, function names, API names, error strings: never touch.
|
|
49072
|
-
|
|
49073
|
-
Example - "Why does my React component re-render?"
|
|
49074
|
-
"Inline object prop, new ref, re-render. \`useMemo\`."`
|
|
49075
|
-
};
|
|
49076
|
-
const COMPRESS_TURN_REMINDERS = {
|
|
49077
|
-
lite: "[response style: compressed lite - no filler/hedging, full sentences; code/commits/security text normal]",
|
|
49078
|
-
full: "[response style: compressed full - terse fragments, drop articles/filler; code/commits/security text normal]",
|
|
49079
|
-
ultra: "[response style: compressed ultra - maximum terseness, one word when enough; code/commits/security text normal]"
|
|
49080
|
-
};
|
|
49081
|
-
function compressRulesetFor(mode) {
|
|
49082
|
-
return mode === "off" ? void 0 : COMPRESS_RULESETS[mode];
|
|
49083
|
-
}
|
|
49084
|
-
function compressTurnReminderFor(mode) {
|
|
49085
|
-
return mode === "off" ? void 0 : COMPRESS_TURN_REMINDERS[mode];
|
|
49086
|
-
}
|
|
49087
|
-
//#endregion
|
|
49088
49580
|
//#region src/provider/Layers/ClaudeAdapter.ts
|
|
49089
49581
|
/**
|
|
49090
49582
|
* ClaudeAdapterLive - Scoped live implementation for the Claude Agent provider adapter.
|
|
@@ -49096,7 +49588,7 @@ function compressTurnReminderFor(mode) {
|
|
|
49096
49588
|
*/
|
|
49097
49589
|
const encodeUnknownJsonStringExit$2 = Schema$1.encodeUnknownExit(Schema$1.UnknownFromJsonString);
|
|
49098
49590
|
const decodeUnknownJsonStringExit = Schema$1.decodeUnknownExit(Schema$1.UnknownFromJsonString);
|
|
49099
|
-
const PROVIDER$
|
|
49591
|
+
const PROVIDER$6 = ProviderDriverKind.make("claudeAgent");
|
|
49100
49592
|
function encodeJsonStringForDiagnostics$2(input) {
|
|
49101
49593
|
const result = encodeUnknownJsonStringExit$2(input);
|
|
49102
49594
|
return Exit.isSuccess(result) ? result.value : void 0;
|
|
@@ -49646,6 +50138,20 @@ const SUPPORTED_CLAUDE_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
|
49646
50138
|
/** Emitted by the CLI but absent from the SDK's `SDKMessage` union. */
|
|
49647
50139
|
const UNTYPED_COMMAND_LIFECYCLE_MESSAGE = "command_lifecycle";
|
|
49648
50140
|
const NARRATE_BEFORE_TOOLS_PROMPT = "Before your first tool call in a turn, write one short sentence saying what you are about to do. Keep it to a single line and skip it when you are answering without tools.";
|
|
50141
|
+
/**
|
|
50142
|
+
* Said whichever way the setting is set, and said as an override.
|
|
50143
|
+
*
|
|
50144
|
+
* The preset system prompt this adapter requests already carries an
|
|
50145
|
+
* instruction not to spawn subagents unless the user asked — and a preset is
|
|
50146
|
+
* taken wholesale, so there is no version of it with that line removed. An
|
|
50147
|
+
* append that merely stayed silent when subagents are allowed would therefore
|
|
50148
|
+
* leave the restriction standing, which is the one outcome the setting exists
|
|
50149
|
+
* to change. Both texts name the earlier instruction they replace, because a
|
|
50150
|
+
* later instruction that does not acknowledge the conflict reads as an
|
|
50151
|
+
* accident rather than a decision.
|
|
50152
|
+
*/
|
|
50153
|
+
const SUBAGENTS_ALLOWED_PROMPT = "You may spawn subagents with the Task tool whenever it helps, without asking the user first. This overrides any earlier instruction to use subagents only when they are requested. Prefer one for bounded search or review work whose result is much smaller than the reading it takes to produce; do the work inline when the answer is a single file or a single edit.";
|
|
50154
|
+
const SUBAGENTS_ON_REQUEST_PROMPT = "Do not spawn subagents with the Task tool unless the user asks for one. This overrides any earlier instruction permitting them on your own initiative. Do the work inline instead, and say so if it would have been better delegated.";
|
|
49649
50155
|
const CLAUDE_SETTING_SOURCES = [
|
|
49650
50156
|
"user",
|
|
49651
50157
|
"project",
|
|
@@ -49704,7 +50210,7 @@ const buildUserMessageEffect = Effect.fn("buildUserMessageEffect")(function* (in
|
|
|
49704
50210
|
for (const attachment of input.attachments ?? []) {
|
|
49705
50211
|
if (attachment.type !== "image") continue;
|
|
49706
50212
|
if (!SUPPORTED_CLAUDE_IMAGE_MIME_TYPES.has(attachment.mimeType)) return yield* new ProviderAdapterRequestError({
|
|
49707
|
-
provider: PROVIDER$
|
|
50213
|
+
provider: PROVIDER$6,
|
|
49708
50214
|
method: "turn/start",
|
|
49709
50215
|
detail: `Unsupported Claude image attachment type '${attachment.mimeType}'.`
|
|
49710
50216
|
});
|
|
@@ -49713,12 +50219,12 @@ const buildUserMessageEffect = Effect.fn("buildUserMessageEffect")(function* (in
|
|
|
49713
50219
|
attachment
|
|
49714
50220
|
});
|
|
49715
50221
|
if (!attachmentPath) return yield* new ProviderAdapterRequestError({
|
|
49716
|
-
provider: PROVIDER$
|
|
50222
|
+
provider: PROVIDER$6,
|
|
49717
50223
|
method: "turn/start",
|
|
49718
50224
|
detail: `Invalid attachment id '${attachment.id}'.`
|
|
49719
50225
|
});
|
|
49720
50226
|
const bytes = yield* dependencies.fileSystem.readFile(attachmentPath).pipe(Effect.mapError((cause) => new ProviderAdapterRequestError({
|
|
49721
|
-
provider: PROVIDER$
|
|
50227
|
+
provider: PROVIDER$6,
|
|
49722
50228
|
method: "turn/start",
|
|
49723
50229
|
detail: "Failed to read attachment file.",
|
|
49724
50230
|
cause
|
|
@@ -49816,12 +50322,12 @@ function toolResultBlocksFromUserMessage(message) {
|
|
|
49816
50322
|
function toSessionError(threadId, cause) {
|
|
49817
50323
|
const normalized = toMessage(cause, "").toLowerCase();
|
|
49818
50324
|
if (normalized.includes("unknown session") || normalized.includes("not found")) return new ProviderAdapterSessionNotFoundError({
|
|
49819
|
-
provider: PROVIDER$
|
|
50325
|
+
provider: PROVIDER$6,
|
|
49820
50326
|
threadId,
|
|
49821
50327
|
cause
|
|
49822
50328
|
});
|
|
49823
50329
|
if (normalized.includes("closed")) return new ProviderAdapterSessionClosedError({
|
|
49824
|
-
provider: PROVIDER$
|
|
50330
|
+
provider: PROVIDER$6,
|
|
49825
50331
|
threadId,
|
|
49826
50332
|
cause
|
|
49827
50333
|
});
|
|
@@ -49830,7 +50336,7 @@ function toRequestError$1(threadId, method, cause) {
|
|
|
49830
50336
|
const sessionError = toSessionError(threadId, cause);
|
|
49831
50337
|
if (sessionError) return sessionError;
|
|
49832
50338
|
return new ProviderAdapterRequestError({
|
|
49833
|
-
provider: PROVIDER$
|
|
50339
|
+
provider: PROVIDER$6,
|
|
49834
50340
|
method,
|
|
49835
50341
|
detail: `${method} failed`,
|
|
49836
50342
|
cause
|
|
@@ -49874,6 +50380,25 @@ function sdkNativeMethod(message) {
|
|
|
49874
50380
|
* would only duplicate a refresh that has already happened.
|
|
49875
50381
|
*/
|
|
49876
50382
|
const CONSUMED_UNDECLARED_SYSTEM_SUBTYPES = /* @__PURE__ */ new Set(["background_tasks_changed", "vcs_state_changed"]);
|
|
50383
|
+
/**
|
|
50384
|
+
* Another undeclared-but-real subtype, emitted once the CLI has pushed the
|
|
50385
|
+
* turn's changes to a hosting provider:
|
|
50386
|
+
* `{provider: "github", url: "https://github.com/org/repo/pull/96",
|
|
50387
|
+
* repo: "org/repo", identifier: "96"}`. Mapped to `code.published` so the
|
|
50388
|
+
* work log links the pull request instead of logging an unknown-subtype
|
|
50389
|
+
* warning.
|
|
50390
|
+
*/
|
|
50391
|
+
const CODE_CHANGE_PUBLISHED_SUBTYPE = "code_change_published";
|
|
50392
|
+
function codePublishedPayloadFromSdkMessage(message) {
|
|
50393
|
+
const record = message;
|
|
50394
|
+
if (typeof record.url !== "string" || record.url.length === 0) return;
|
|
50395
|
+
return {
|
|
50396
|
+
hostingProvider: typeof record.provider === "string" && record.provider.length > 0 ? record.provider : "unknown",
|
|
50397
|
+
url: record.url,
|
|
50398
|
+
...typeof record.repo === "string" && record.repo.length > 0 ? { repo: record.repo } : {},
|
|
50399
|
+
...typeof record.identifier === "string" && record.identifier.length > 0 ? { identifier: record.identifier } : {}
|
|
50400
|
+
};
|
|
50401
|
+
}
|
|
49877
50402
|
const SDK_MESSAGE_NOISE_KEYS = /* @__PURE__ */ new Set([
|
|
49878
50403
|
"type",
|
|
49879
50404
|
"subtype",
|
|
@@ -49930,7 +50455,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
49930
50455
|
const runtimeEventQueue = yield* Queue.unbounded();
|
|
49931
50456
|
const nowIso = Effect.map(DateTime.now, DateTime.formatIso);
|
|
49932
50457
|
const randomUUIDv4 = crypto.randomUUIDv4.pipe(Effect.mapError((cause) => new ProviderAdapterRequestError({
|
|
49933
|
-
provider: PROVIDER$
|
|
50458
|
+
provider: PROVIDER$6,
|
|
49934
50459
|
method: "crypto/randomUUIDv4",
|
|
49935
50460
|
detail: "Failed to generate Claude runtime identifier.",
|
|
49936
50461
|
cause
|
|
@@ -49950,7 +50475,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
49950
50475
|
event: {
|
|
49951
50476
|
id: "uuid" in message && typeof message.uuid === "string" ? message.uuid : yield* randomUUIDv4,
|
|
49952
50477
|
kind: "notification",
|
|
49953
|
-
provider: PROVIDER$
|
|
50478
|
+
provider: PROVIDER$6,
|
|
49954
50479
|
createdAt: observedAt,
|
|
49955
50480
|
method: sdkNativeMethod(message),
|
|
49956
50481
|
...typeof message.session_id === "string" ? { providerThreadId: message.session_id } : {},
|
|
@@ -49963,7 +50488,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
49963
50488
|
const snapshotThread = Effect.fn("snapshotThread")(function* (context) {
|
|
49964
50489
|
const threadId = context.session.threadId;
|
|
49965
50490
|
if (!threadId) return yield* new ProviderAdapterValidationError({
|
|
49966
|
-
provider: PROVIDER$
|
|
50491
|
+
provider: PROVIDER$6,
|
|
49967
50492
|
operation: "readThread",
|
|
49968
50493
|
issue: "Session thread id is not initialized yet."
|
|
49969
50494
|
});
|
|
@@ -50036,7 +50561,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50036
50561
|
yield* offerRuntimeEvent({
|
|
50037
50562
|
type: "content.delta",
|
|
50038
50563
|
eventId: deltaStamp.eventId,
|
|
50039
|
-
provider: PROVIDER$
|
|
50564
|
+
provider: PROVIDER$6,
|
|
50040
50565
|
createdAt: deltaStamp.createdAt,
|
|
50041
50566
|
threadId: context.session.threadId,
|
|
50042
50567
|
turnId: turnState.turnId,
|
|
@@ -50059,7 +50584,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50059
50584
|
yield* offerRuntimeEvent({
|
|
50060
50585
|
type: "item.completed",
|
|
50061
50586
|
eventId: stamp.eventId,
|
|
50062
|
-
provider: PROVIDER$
|
|
50587
|
+
provider: PROVIDER$6,
|
|
50063
50588
|
createdAt: stamp.createdAt,
|
|
50064
50589
|
itemId: asRuntimeItemId$1(block.itemId),
|
|
50065
50590
|
threadId: context.session.threadId,
|
|
@@ -50113,7 +50638,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50113
50638
|
yield* offerRuntimeEvent({
|
|
50114
50639
|
type: "thread.started",
|
|
50115
50640
|
eventId: stamp.eventId,
|
|
50116
|
-
provider: PROVIDER$
|
|
50641
|
+
provider: PROVIDER$6,
|
|
50117
50642
|
createdAt: stamp.createdAt,
|
|
50118
50643
|
threadId: context.session.threadId,
|
|
50119
50644
|
payload: { providerThreadId: nextThreadId },
|
|
@@ -50133,7 +50658,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50133
50658
|
yield* offerRuntimeEvent({
|
|
50134
50659
|
type: "runtime.error",
|
|
50135
50660
|
eventId: stamp.eventId,
|
|
50136
|
-
provider: PROVIDER$
|
|
50661
|
+
provider: PROVIDER$6,
|
|
50137
50662
|
createdAt: stamp.createdAt,
|
|
50138
50663
|
threadId: context.session.threadId,
|
|
50139
50664
|
...turnState ? { turnId: asCanonicalTurnId(turnState.turnId) } : {},
|
|
@@ -50151,7 +50676,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50151
50676
|
yield* offerRuntimeEvent({
|
|
50152
50677
|
type: "runtime.warning",
|
|
50153
50678
|
eventId: stamp.eventId,
|
|
50154
|
-
provider: PROVIDER$
|
|
50679
|
+
provider: PROVIDER$6,
|
|
50155
50680
|
createdAt: stamp.createdAt,
|
|
50156
50681
|
threadId: context.session.threadId,
|
|
50157
50682
|
...turnState ? { turnId: asCanonicalTurnId(turnState.turnId) } : {},
|
|
@@ -50171,7 +50696,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50171
50696
|
yield* offerRuntimeEvent({
|
|
50172
50697
|
type: "thread.token-usage.updated",
|
|
50173
50698
|
eventId: stamp.eventId,
|
|
50174
|
-
provider: PROVIDER$
|
|
50699
|
+
provider: PROVIDER$6,
|
|
50175
50700
|
createdAt: stamp.createdAt,
|
|
50176
50701
|
threadId: context.session.threadId,
|
|
50177
50702
|
...turnState ? { turnId: turnState.turnId } : {},
|
|
@@ -50224,7 +50749,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50224
50749
|
yield* offerRuntimeEvent({
|
|
50225
50750
|
type: "turn.proposed.completed",
|
|
50226
50751
|
eventId: stamp.eventId,
|
|
50227
|
-
provider: PROVIDER$
|
|
50752
|
+
provider: PROVIDER$6,
|
|
50228
50753
|
createdAt: stamp.createdAt,
|
|
50229
50754
|
threadId: context.session.threadId,
|
|
50230
50755
|
turnId: turnState.turnId,
|
|
@@ -50244,7 +50769,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50244
50769
|
yield* offerRuntimeEvent({
|
|
50245
50770
|
type: "turn.plan.updated",
|
|
50246
50771
|
eventId: stamp.eventId,
|
|
50247
|
-
provider: PROVIDER$
|
|
50772
|
+
provider: PROVIDER$6,
|
|
50248
50773
|
createdAt: stamp.createdAt,
|
|
50249
50774
|
threadId: context.session.threadId,
|
|
50250
50775
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -50292,7 +50817,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50292
50817
|
yield* offerRuntimeEvent({
|
|
50293
50818
|
type: "turn.completed",
|
|
50294
50819
|
eventId: stamp.eventId,
|
|
50295
|
-
provider: PROVIDER$
|
|
50820
|
+
provider: PROVIDER$6,
|
|
50296
50821
|
createdAt: stamp.createdAt,
|
|
50297
50822
|
threadId: context.session.threadId,
|
|
50298
50823
|
payload: {
|
|
@@ -50312,7 +50837,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50312
50837
|
yield* offerRuntimeEvent({
|
|
50313
50838
|
type: "item.completed",
|
|
50314
50839
|
eventId: toolStamp.eventId,
|
|
50315
|
-
provider: PROVIDER$
|
|
50840
|
+
provider: PROVIDER$6,
|
|
50316
50841
|
createdAt: toolStamp.createdAt,
|
|
50317
50842
|
threadId: context.session.threadId,
|
|
50318
50843
|
turnId: turnState.turnId,
|
|
@@ -50355,7 +50880,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50355
50880
|
yield* offerRuntimeEvent({
|
|
50356
50881
|
type: "turn.completed",
|
|
50357
50882
|
eventId: stamp.eventId,
|
|
50358
|
-
provider: PROVIDER$
|
|
50883
|
+
provider: PROVIDER$6,
|
|
50359
50884
|
createdAt: stamp.createdAt,
|
|
50360
50885
|
threadId: context.session.threadId,
|
|
50361
50886
|
turnId: turnState.turnId,
|
|
@@ -50429,7 +50954,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50429
50954
|
yield* offerRuntimeEvent({
|
|
50430
50955
|
type: "turn.started",
|
|
50431
50956
|
eventId: turnStartedStamp.eventId,
|
|
50432
|
-
provider: PROVIDER$
|
|
50957
|
+
provider: PROVIDER$6,
|
|
50433
50958
|
createdAt: turnStartedStamp.createdAt,
|
|
50434
50959
|
threadId: context.session.threadId,
|
|
50435
50960
|
turnId,
|
|
@@ -50509,7 +51034,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50509
51034
|
yield* offerRuntimeEvent({
|
|
50510
51035
|
type: "content.delta",
|
|
50511
51036
|
eventId: stamp.eventId,
|
|
50512
|
-
provider: PROVIDER$
|
|
51037
|
+
provider: PROVIDER$6,
|
|
50513
51038
|
createdAt: stamp.createdAt,
|
|
50514
51039
|
threadId: context.session.threadId,
|
|
50515
51040
|
turnId: context.turnState.turnId,
|
|
@@ -50553,7 +51078,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50553
51078
|
yield* offerRuntimeEvent({
|
|
50554
51079
|
type: "item.updated",
|
|
50555
51080
|
eventId: stamp.eventId,
|
|
50556
|
-
provider: PROVIDER$
|
|
51081
|
+
provider: PROVIDER$6,
|
|
50557
51082
|
createdAt: stamp.createdAt,
|
|
50558
51083
|
threadId: context.session.threadId,
|
|
50559
51084
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -50583,7 +51108,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50583
51108
|
yield* offerRuntimeEvent({
|
|
50584
51109
|
type: "turn.plan.updated",
|
|
50585
51110
|
eventId: planStamp.eventId,
|
|
50586
|
-
provider: PROVIDER$
|
|
51111
|
+
provider: PROVIDER$6,
|
|
50587
51112
|
createdAt: planStamp.createdAt,
|
|
50588
51113
|
threadId: context.session.threadId,
|
|
50589
51114
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -50623,7 +51148,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50623
51148
|
yield* offerRuntimeEvent({
|
|
50624
51149
|
type: "item.started",
|
|
50625
51150
|
eventId: stamp.eventId,
|
|
50626
|
-
provider: PROVIDER$
|
|
51151
|
+
provider: PROVIDER$6,
|
|
50627
51152
|
createdAt: stamp.createdAt,
|
|
50628
51153
|
threadId: context.session.threadId,
|
|
50629
51154
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -50681,7 +51206,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50681
51206
|
yield* offerRuntimeEvent({
|
|
50682
51207
|
type: "item.updated",
|
|
50683
51208
|
eventId: updatedStamp.eventId,
|
|
50684
|
-
provider: PROVIDER$
|
|
51209
|
+
provider: PROVIDER$6,
|
|
50685
51210
|
createdAt: updatedStamp.createdAt,
|
|
50686
51211
|
threadId: context.session.threadId,
|
|
50687
51212
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -50706,7 +51231,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50706
51231
|
yield* offerRuntimeEvent({
|
|
50707
51232
|
type: "content.delta",
|
|
50708
51233
|
eventId: deltaStamp.eventId,
|
|
50709
|
-
provider: PROVIDER$
|
|
51234
|
+
provider: PROVIDER$6,
|
|
50710
51235
|
createdAt: deltaStamp.createdAt,
|
|
50711
51236
|
threadId: context.session.threadId,
|
|
50712
51237
|
turnId: context.turnState.turnId,
|
|
@@ -50727,7 +51252,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50727
51252
|
yield* offerRuntimeEvent({
|
|
50728
51253
|
type: "item.completed",
|
|
50729
51254
|
eventId: completedStamp.eventId,
|
|
50730
|
-
provider: PROVIDER$
|
|
51255
|
+
provider: PROVIDER$6,
|
|
50731
51256
|
createdAt: completedStamp.createdAt,
|
|
50732
51257
|
threadId: context.session.threadId,
|
|
50733
51258
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -50779,7 +51304,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50779
51304
|
yield* offerRuntimeEvent({
|
|
50780
51305
|
type: "turn.started",
|
|
50781
51306
|
eventId: turnStartedStamp.eventId,
|
|
50782
|
-
provider: PROVIDER$
|
|
51307
|
+
provider: PROVIDER$6,
|
|
50783
51308
|
createdAt: turnStartedStamp.createdAt,
|
|
50784
51309
|
threadId: context.session.threadId,
|
|
50785
51310
|
turnId,
|
|
@@ -50832,7 +51357,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50832
51357
|
const stamp = yield* makeEventStamp();
|
|
50833
51358
|
const base = {
|
|
50834
51359
|
eventId: stamp.eventId,
|
|
50835
|
-
provider: PROVIDER$
|
|
51360
|
+
provider: PROVIDER$6,
|
|
50836
51361
|
createdAt: stamp.createdAt,
|
|
50837
51362
|
threadId: context.session.threadId,
|
|
50838
51363
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -50845,6 +51370,17 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
50845
51370
|
}
|
|
50846
51371
|
};
|
|
50847
51372
|
if (CONSUMED_UNDECLARED_SYSTEM_SUBTYPES.has(message.subtype)) return;
|
|
51373
|
+
if (message.subtype === CODE_CHANGE_PUBLISHED_SUBTYPE) {
|
|
51374
|
+
const published = codePublishedPayloadFromSdkMessage(message);
|
|
51375
|
+
if (published) {
|
|
51376
|
+
yield* offerRuntimeEvent({
|
|
51377
|
+
...base,
|
|
51378
|
+
type: "code.published",
|
|
51379
|
+
payload: published
|
|
51380
|
+
});
|
|
51381
|
+
return;
|
|
51382
|
+
}
|
|
51383
|
+
}
|
|
50848
51384
|
switch (message.subtype) {
|
|
50849
51385
|
case "init":
|
|
50850
51386
|
yield* offerRuntimeEvent({
|
|
@@ -51035,7 +51571,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51035
51571
|
const stamp = yield* makeEventStamp();
|
|
51036
51572
|
const base = {
|
|
51037
51573
|
eventId: stamp.eventId,
|
|
51038
|
-
provider: PROVIDER$
|
|
51574
|
+
provider: PROVIDER$6,
|
|
51039
51575
|
createdAt: stamp.createdAt,
|
|
51040
51576
|
threadId: context.session.threadId,
|
|
51041
51577
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -51125,12 +51661,12 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51125
51661
|
}
|
|
51126
51662
|
});
|
|
51127
51663
|
const runSdkStream = (context) => Stream.fromAsyncIterable(context.query, (cause) => new ProviderAdapterProcessError({
|
|
51128
|
-
provider: PROVIDER$
|
|
51664
|
+
provider: PROVIDER$6,
|
|
51129
51665
|
threadId: context.session.threadId,
|
|
51130
51666
|
detail: "Claude runtime stream failed.",
|
|
51131
51667
|
cause
|
|
51132
51668
|
})).pipe(Stream.takeWhile(() => !context.stopped), Stream.runForEach((message) => handleSdkMessage(context, message).pipe(Effect.mapError((cause) => new ProviderAdapterProcessError({
|
|
51133
|
-
provider: PROVIDER$
|
|
51669
|
+
provider: PROVIDER$6,
|
|
51134
51670
|
threadId: context.session.threadId,
|
|
51135
51671
|
detail: "Failed to process Claude runtime event.",
|
|
51136
51672
|
cause
|
|
@@ -51160,7 +51696,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51160
51696
|
yield* offerRuntimeEvent({
|
|
51161
51697
|
type: "request.resolved",
|
|
51162
51698
|
eventId: stamp.eventId,
|
|
51163
|
-
provider: PROVIDER$
|
|
51699
|
+
provider: PROVIDER$6,
|
|
51164
51700
|
createdAt: stamp.createdAt,
|
|
51165
51701
|
threadId: context.session.threadId,
|
|
51166
51702
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -51181,7 +51717,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51181
51717
|
yield* Effect.try({
|
|
51182
51718
|
try: () => context.query.close(),
|
|
51183
51719
|
catch: (cause) => new ProviderAdapterProcessError({
|
|
51184
|
-
provider: PROVIDER$
|
|
51720
|
+
provider: PROVIDER$6,
|
|
51185
51721
|
threadId: context.session.threadId,
|
|
51186
51722
|
detail: "Failed to close Claude runtime query.",
|
|
51187
51723
|
cause
|
|
@@ -51204,7 +51740,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51204
51740
|
yield* offerRuntimeEvent({
|
|
51205
51741
|
type: "session.exited",
|
|
51206
51742
|
eventId: stamp.eventId,
|
|
51207
|
-
provider: PROVIDER$
|
|
51743
|
+
provider: PROVIDER$6,
|
|
51208
51744
|
createdAt: stamp.createdAt,
|
|
51209
51745
|
threadId: context.session.threadId,
|
|
51210
51746
|
payload: {
|
|
@@ -51219,20 +51755,20 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51219
51755
|
const requireSession = (threadId) => {
|
|
51220
51756
|
const context = sessions.get(threadId);
|
|
51221
51757
|
if (!context) return Effect.fail(new ProviderAdapterSessionNotFoundError({
|
|
51222
|
-
provider: PROVIDER$
|
|
51758
|
+
provider: PROVIDER$6,
|
|
51223
51759
|
threadId
|
|
51224
51760
|
}));
|
|
51225
51761
|
if (context.stopped || context.session.status === "closed") return Effect.fail(new ProviderAdapterSessionClosedError({
|
|
51226
|
-
provider: PROVIDER$
|
|
51762
|
+
provider: PROVIDER$6,
|
|
51227
51763
|
threadId
|
|
51228
51764
|
}));
|
|
51229
51765
|
return Effect.succeed(context);
|
|
51230
51766
|
};
|
|
51231
51767
|
const startSession = Effect.fn("startSession")(function* (input) {
|
|
51232
|
-
if (input.provider !== void 0 && input.provider !== PROVIDER$
|
|
51233
|
-
provider: PROVIDER$
|
|
51768
|
+
if (input.provider !== void 0 && input.provider !== PROVIDER$6) return yield* new ProviderAdapterValidationError({
|
|
51769
|
+
provider: PROVIDER$6,
|
|
51234
51770
|
operation: "startSession",
|
|
51235
|
-
issue: `Expected provider '${PROVIDER$
|
|
51771
|
+
issue: `Expected provider '${PROVIDER$6}' but received '${input.provider}'.`
|
|
51236
51772
|
});
|
|
51237
51773
|
const existingContext = sessions.get(input.threadId);
|
|
51238
51774
|
if (existingContext) {
|
|
@@ -51288,7 +51824,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51288
51824
|
yield* offerRuntimeEvent({
|
|
51289
51825
|
type: "user-input.requested",
|
|
51290
51826
|
eventId: requestedStamp.eventId,
|
|
51291
|
-
provider: PROVIDER$
|
|
51827
|
+
provider: PROVIDER$6,
|
|
51292
51828
|
createdAt: requestedStamp.createdAt,
|
|
51293
51829
|
threadId: context.session.threadId,
|
|
51294
51830
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -51318,7 +51854,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51318
51854
|
yield* offerRuntimeEvent({
|
|
51319
51855
|
type: "user-input.resolved",
|
|
51320
51856
|
eventId: resolvedStamp.eventId,
|
|
51321
|
-
provider: PROVIDER$
|
|
51857
|
+
provider: PROVIDER$6,
|
|
51322
51858
|
createdAt: resolvedStamp.createdAt,
|
|
51323
51859
|
threadId: context.session.threadId,
|
|
51324
51860
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -51385,7 +51921,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51385
51921
|
yield* offerRuntimeEvent({
|
|
51386
51922
|
type: "request.opened",
|
|
51387
51923
|
eventId: requestedStamp.eventId,
|
|
51388
|
-
provider: PROVIDER$
|
|
51924
|
+
provider: PROVIDER$6,
|
|
51389
51925
|
createdAt: requestedStamp.createdAt,
|
|
51390
51926
|
threadId: context.session.threadId,
|
|
51391
51927
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -51422,7 +51958,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51422
51958
|
yield* offerRuntimeEvent({
|
|
51423
51959
|
type: "request.resolved",
|
|
51424
51960
|
eventId: resolvedStamp.eventId,
|
|
51425
|
-
provider: PROVIDER$
|
|
51961
|
+
provider: PROVIDER$6,
|
|
51426
51962
|
createdAt: resolvedStamp.createdAt,
|
|
51427
51963
|
threadId: context.session.threadId,
|
|
51428
51964
|
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
@@ -51479,8 +52015,13 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51479
52015
|
const mcpSession = readMcpProviderSession(input.threadId);
|
|
51480
52016
|
const externalMcpServers = options?.resolveMcpServers === void 0 ? {} : yield* options.resolveMcpServers;
|
|
51481
52017
|
const narrateBeforeTools = options?.resolveToolCallNarration === void 0 ? DEFAULT_SERVER_SETTINGS.enableToolCallNarration : yield* options.resolveToolCallNarration;
|
|
52018
|
+
const unpromptedSubagents = options?.resolveUnpromptedSubagents === void 0 ? DEFAULT_SERVER_SETTINGS.enableUnpromptedSubagents : yield* options.resolveUnpromptedSubagents;
|
|
51482
52019
|
const compressRuleset = compressRulesetFor(input.compressMode ?? "off");
|
|
51483
|
-
const systemPromptAppend = [
|
|
52020
|
+
const systemPromptAppend = [
|
|
52021
|
+
...narrateBeforeTools ? [NARRATE_BEFORE_TOOLS_PROMPT] : [],
|
|
52022
|
+
unpromptedSubagents ? SUBAGENTS_ALLOWED_PROMPT : SUBAGENTS_ON_REQUEST_PROMPT,
|
|
52023
|
+
...compressRuleset !== void 0 ? [compressRuleset] : []
|
|
52024
|
+
].join("\n\n");
|
|
51484
52025
|
const queryOptions = {
|
|
51485
52026
|
...input.cwd ? { cwd: input.cwd } : {},
|
|
51486
52027
|
...apiModelId ? { model: apiModelId } : {},
|
|
@@ -51512,7 +52053,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51512
52053
|
} } : {}
|
|
51513
52054
|
};
|
|
51514
52055
|
yield* Effect.annotateCurrentSpan({
|
|
51515
|
-
"provider.kind": PROVIDER$
|
|
52056
|
+
"provider.kind": PROVIDER$6,
|
|
51516
52057
|
"provider.thread_id": threadId,
|
|
51517
52058
|
"provider.runtime_mode": input.runtimeMode,
|
|
51518
52059
|
"claude.resume.source": existingResumeSessionId !== void 0 ? "resume-session" : "generated-session",
|
|
@@ -51541,7 +52082,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51541
52082
|
options: queryOptions
|
|
51542
52083
|
}),
|
|
51543
52084
|
catch: (cause) => new ProviderAdapterProcessError({
|
|
51544
|
-
provider: PROVIDER$
|
|
52085
|
+
provider: PROVIDER$6,
|
|
51545
52086
|
threadId,
|
|
51546
52087
|
detail: "Failed to start Claude runtime session.",
|
|
51547
52088
|
cause
|
|
@@ -51549,7 +52090,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51549
52090
|
});
|
|
51550
52091
|
const session = {
|
|
51551
52092
|
threadId,
|
|
51552
|
-
provider: PROVIDER$
|
|
52093
|
+
provider: PROVIDER$6,
|
|
51553
52094
|
providerInstanceId: boundInstanceId,
|
|
51554
52095
|
status: "ready",
|
|
51555
52096
|
runtimeMode: input.runtimeMode,
|
|
@@ -51596,7 +52137,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51596
52137
|
yield* offerRuntimeEvent({
|
|
51597
52138
|
type: "session.started",
|
|
51598
52139
|
eventId: sessionStartedStamp.eventId,
|
|
51599
|
-
provider: PROVIDER$
|
|
52140
|
+
provider: PROVIDER$6,
|
|
51600
52141
|
createdAt: sessionStartedStamp.createdAt,
|
|
51601
52142
|
threadId,
|
|
51602
52143
|
payload: input.resumeCursor !== void 0 ? { resume: input.resumeCursor } : {},
|
|
@@ -51606,7 +52147,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51606
52147
|
yield* offerRuntimeEvent({
|
|
51607
52148
|
type: "session.configured",
|
|
51608
52149
|
eventId: configuredStamp.eventId,
|
|
51609
|
-
provider: PROVIDER$
|
|
52150
|
+
provider: PROVIDER$6,
|
|
51610
52151
|
createdAt: configuredStamp.createdAt,
|
|
51611
52152
|
threadId,
|
|
51612
52153
|
payload: { config: {
|
|
@@ -51622,7 +52163,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51622
52163
|
yield* offerRuntimeEvent({
|
|
51623
52164
|
type: "session.state.changed",
|
|
51624
52165
|
eventId: readyStamp.eventId,
|
|
51625
|
-
provider: PROVIDER$
|
|
52166
|
+
provider: PROVIDER$6,
|
|
51626
52167
|
createdAt: readyStamp.createdAt,
|
|
51627
52168
|
threadId,
|
|
51628
52169
|
payload: { state: "ready" },
|
|
@@ -51703,7 +52244,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51703
52244
|
const context = yield* requireSession(threadId);
|
|
51704
52245
|
const pending = context.pendingApprovals.get(requestId);
|
|
51705
52246
|
if (!pending) return yield* new ProviderAdapterRequestError({
|
|
51706
|
-
provider: PROVIDER$
|
|
52247
|
+
provider: PROVIDER$6,
|
|
51707
52248
|
method: "item/requestApproval/decision",
|
|
51708
52249
|
detail: `Unknown pending approval request: ${requestId}`
|
|
51709
52250
|
});
|
|
@@ -51714,7 +52255,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51714
52255
|
const context = yield* requireSession(threadId);
|
|
51715
52256
|
const pending = context.pendingUserInputs.get(requestId);
|
|
51716
52257
|
if (!pending) return yield* new ProviderAdapterRequestError({
|
|
51717
|
-
provider: PROVIDER$
|
|
52258
|
+
provider: PROVIDER$6,
|
|
51718
52259
|
method: "item/tool/respondToUserInput",
|
|
51719
52260
|
detail: `Unknown pending user-input request: ${requestId}`
|
|
51720
52261
|
});
|
|
@@ -51733,7 +52274,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
51733
52274
|
const stopAll = () => Effect.forEach(sessions, ([, context]) => stopSessionInternal(context, { emitExitEvent: true }), { discard: true });
|
|
51734
52275
|
yield* Effect.addFinalizer(() => Effect.forEach(sessions, ([, context]) => stopSessionInternal(context, { emitExitEvent: false }), { discard: true }).pipe(Effect.catch((cause) => Effect.logError("Failed to emit Claude session shutdown event.", { cause })), Effect.tap(() => Queue.shutdown(runtimeEventQueue))));
|
|
51735
52276
|
return {
|
|
51736
|
-
provider: PROVIDER$
|
|
52277
|
+
provider: PROVIDER$6,
|
|
51737
52278
|
capabilities: { sessionModelSwitch: "in-session" },
|
|
51738
52279
|
startSession,
|
|
51739
52280
|
sendTurn,
|
|
@@ -51877,15 +52418,15 @@ function makeProviderSnapshotSettingsSource(provider, serverSettings) {
|
|
|
51877
52418
|
* @module provider/Drivers/ClaudeDriver
|
|
51878
52419
|
*/
|
|
51879
52420
|
const decodeClaudeSettings = Schema$1.decodeSync(ClaudeSettings);
|
|
51880
|
-
const DRIVER_KIND$
|
|
51881
|
-
const SNAPSHOT_REFRESH_INTERVAL$
|
|
52421
|
+
const DRIVER_KIND$5 = ProviderDriverKind.make("claudeAgent");
|
|
52422
|
+
const SNAPSHOT_REFRESH_INTERVAL$5 = Duration.minutes(5);
|
|
51882
52423
|
const CAPABILITIES_PROBE_TTL = Duration.minutes(5);
|
|
51883
52424
|
function isClaudeNativeCommandPath(commandPath) {
|
|
51884
52425
|
const normalized = normalizeCommandPath(commandPath);
|
|
51885
52426
|
return normalized.endsWith("/.local/bin/claude") || normalized.endsWith("/.local/bin/claude.exe") || normalized.includes("/.local/share/claude/");
|
|
51886
52427
|
}
|
|
51887
|
-
const UPDATE$
|
|
51888
|
-
provider: DRIVER_KIND$
|
|
52428
|
+
const UPDATE$5 = makePackageManagedProviderMaintenanceResolver({
|
|
52429
|
+
provider: DRIVER_KIND$5,
|
|
51889
52430
|
npmPackageName: "@anthropic-ai/claude-code",
|
|
51890
52431
|
homebrewFormula: "claude-code",
|
|
51891
52432
|
nativeUpdate: {
|
|
@@ -51895,16 +52436,16 @@ const UPDATE$4 = makePackageManagedProviderMaintenanceResolver({
|
|
|
51895
52436
|
isCommandPath: isClaudeNativeCommandPath
|
|
51896
52437
|
}
|
|
51897
52438
|
});
|
|
51898
|
-
const withInstanceIdentity$
|
|
52439
|
+
const withInstanceIdentity$5 = (input) => (snapshot) => ({
|
|
51899
52440
|
...snapshot,
|
|
51900
52441
|
instanceId: input.instanceId,
|
|
51901
|
-
driver: DRIVER_KIND$
|
|
52442
|
+
driver: DRIVER_KIND$5,
|
|
51902
52443
|
...input.displayName ? { displayName: input.displayName } : {},
|
|
51903
52444
|
...input.accentColor ? { accentColor: input.accentColor } : {},
|
|
51904
52445
|
continuation: { groupKey: input.continuationGroupKey }
|
|
51905
52446
|
});
|
|
51906
52447
|
const ClaudeDriver = {
|
|
51907
|
-
driverKind: DRIVER_KIND$
|
|
52448
|
+
driverKind: DRIVER_KIND$5,
|
|
51908
52449
|
metadata: {
|
|
51909
52450
|
displayName: "Claude",
|
|
51910
52451
|
supportsMultipleInstances: true
|
|
@@ -51921,19 +52462,19 @@ const ClaudeDriver = {
|
|
|
51921
52462
|
const eventLoggers = yield* ProviderEventLoggers;
|
|
51922
52463
|
const processEnv = mergeProviderInstanceEnvironment(environment);
|
|
51923
52464
|
const fallbackContinuationIdentity = defaultProviderContinuationIdentity({
|
|
51924
|
-
driverKind: DRIVER_KIND$
|
|
52465
|
+
driverKind: DRIVER_KIND$5,
|
|
51925
52466
|
instanceId
|
|
51926
52467
|
});
|
|
51927
52468
|
const effectiveConfig = {
|
|
51928
52469
|
...config,
|
|
51929
52470
|
enabled
|
|
51930
52471
|
};
|
|
51931
|
-
const maintenanceCapabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(UPDATE$
|
|
52472
|
+
const maintenanceCapabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(UPDATE$5, {
|
|
51932
52473
|
binaryPath: effectiveConfig.binaryPath,
|
|
51933
52474
|
env: processEnv
|
|
51934
52475
|
});
|
|
51935
52476
|
const continuationGroupKey = yield* makeClaudeContinuationGroupKey(effectiveConfig);
|
|
51936
|
-
const stampIdentity = withInstanceIdentity$
|
|
52477
|
+
const stampIdentity = withInstanceIdentity$5({
|
|
51937
52478
|
instanceId,
|
|
51938
52479
|
displayName,
|
|
51939
52480
|
accentColor,
|
|
@@ -51942,12 +52483,14 @@ const ClaudeDriver = {
|
|
|
51942
52483
|
const mcpRegistry = yield* McpRegistry;
|
|
51943
52484
|
const resolveDisabledSkills = serverSettings.getSettings.pipe(Effect.map((settings) => settings.disabledSkills), Effect.orElseSucceed(() => []));
|
|
51944
52485
|
const resolveToolCallNarration = serverSettings.getSettings.pipe(Effect.map((settings) => settings.enableToolCallNarration), Effect.orElseSucceed(() => DEFAULT_SERVER_SETTINGS.enableToolCallNarration));
|
|
52486
|
+
const resolveUnpromptedSubagents = serverSettings.getSettings.pipe(Effect.map((settings) => settings.enableUnpromptedSubagents), Effect.orElseSucceed(() => DEFAULT_SERVER_SETTINGS.enableUnpromptedSubagents));
|
|
51945
52487
|
const adapter = yield* makeClaudeAdapter(effectiveConfig, {
|
|
51946
52488
|
instanceId,
|
|
51947
52489
|
environment: processEnv,
|
|
51948
52490
|
resolveMcpServers: mcpRegistry.resolveForSession,
|
|
51949
52491
|
resolveDisabledSkills,
|
|
51950
52492
|
resolveToolCallNarration,
|
|
52493
|
+
resolveUnpromptedSubagents,
|
|
51951
52494
|
...eventLoggers.native ? { nativeEventLogger: eventLoggers.native } : {}
|
|
51952
52495
|
});
|
|
51953
52496
|
const textGeneration = yield* makeClaudeTextGeneration(effectiveConfig, processEnv);
|
|
@@ -51967,16 +52510,16 @@ const ClaudeDriver = {
|
|
|
51967
52510
|
initialSnapshot: (settings) => makePendingClaudeProvider(settings.provider).pipe(Effect.map(stampIdentity)),
|
|
51968
52511
|
checkProvider,
|
|
51969
52512
|
enrichSnapshot: ({ settings, snapshot, publishSnapshot }) => enrichProviderSnapshotWithVersionAdvisory(snapshot, maintenanceCapabilities, { enableProviderUpdateChecks: settings.enableProviderUpdateChecks }).pipe(Effect.provideService(HttpClient.HttpClient, httpClient), Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot))),
|
|
51970
|
-
refreshInterval: SNAPSHOT_REFRESH_INTERVAL$
|
|
52513
|
+
refreshInterval: SNAPSHOT_REFRESH_INTERVAL$5
|
|
51971
52514
|
}).pipe(Effect.mapError((cause) => new ProviderDriverError({
|
|
51972
|
-
driver: DRIVER_KIND$
|
|
52515
|
+
driver: DRIVER_KIND$5,
|
|
51973
52516
|
instanceId,
|
|
51974
52517
|
detail: `Failed to build Claude snapshot: ${cause.message ?? String(cause)}`,
|
|
51975
52518
|
cause
|
|
51976
52519
|
})));
|
|
51977
52520
|
return {
|
|
51978
52521
|
instanceId,
|
|
51979
|
-
driverKind: DRIVER_KIND$
|
|
52522
|
+
driverKind: DRIVER_KIND$5,
|
|
51980
52523
|
continuationIdentity: {
|
|
51981
52524
|
...fallbackContinuationIdentity,
|
|
51982
52525
|
continuationKey: continuationGroupKey
|
|
@@ -70810,7 +71353,7 @@ function buildCodexDeveloperInstructions(interactionMode, runtime, compressMode)
|
|
|
70810
71353
|
//#endregion
|
|
70811
71354
|
//#region src/provider/Layers/CodexSessionRuntime.ts
|
|
70812
71355
|
const decodeV2TurnStartResponse = Schema$1.decodeUnknownEffect(V2TurnStartResponse);
|
|
70813
|
-
const PROVIDER$
|
|
71356
|
+
const PROVIDER$5 = ProviderDriverKind.make("codex");
|
|
70814
71357
|
const ANSI_ESCAPE_REGEX = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
70815
71358
|
const CODEX_STDERR_LOG_REGEX = /^\d{4}-\d{2}-\d{2}T\S+\s+(TRACE|DEBUG|INFO|WARN|ERROR)\s+\S+:\s+(.*)$/;
|
|
70816
71359
|
const BENIGN_ERROR_LOG_SNIPPETS = ["state db missing rollout path for thread", "state db record_discrepancy: find_thread_path_by_id_str_in_subdir, falling_back"];
|
|
@@ -71162,7 +71705,7 @@ const makeCodexSessionRuntime = (options) => Effect.gen(function* () {
|
|
|
71162
71705
|
})));
|
|
71163
71706
|
const sessionCreatedAt = yield* nowIso;
|
|
71164
71707
|
const initialSession = {
|
|
71165
|
-
provider: PROVIDER$
|
|
71708
|
+
provider: PROVIDER$5,
|
|
71166
71709
|
...options.providerInstanceId ? { providerInstanceId: options.providerInstanceId } : {},
|
|
71167
71710
|
status: "connecting",
|
|
71168
71711
|
runtimeMode: options.runtimeMode,
|
|
@@ -71179,7 +71722,7 @@ const makeCodexSessionRuntime = (options) => Effect.gen(function* () {
|
|
|
71179
71722
|
const id = yield* randomUUIDv4("provider-event");
|
|
71180
71723
|
return yield* offerEvent({
|
|
71181
71724
|
id: EventId.make(id),
|
|
71182
|
-
provider: PROVIDER$
|
|
71725
|
+
provider: PROVIDER$5,
|
|
71183
71726
|
...options.providerInstanceId ? { providerInstanceId: options.providerInstanceId } : {},
|
|
71184
71727
|
createdAt: yield* nowIso,
|
|
71185
71728
|
...event
|
|
@@ -71584,20 +72127,20 @@ const isCodexAppServerProcessExitedError = Schema$1.is(CodexAppServerProcessExit
|
|
|
71584
72127
|
const isCodexAppServerTransportError = Schema$1.is(CodexAppServerTransportError);
|
|
71585
72128
|
const isCodexSessionRuntimeThreadIdMissingError = Schema$1.is(CodexSessionRuntimeThreadIdMissingError);
|
|
71586
72129
|
const isCodexResumeCursorSchema = Schema$1.is(CodexResumeCursorSchema);
|
|
71587
|
-
const PROVIDER$
|
|
72130
|
+
const PROVIDER$4 = ProviderDriverKind.make("codex");
|
|
71588
72131
|
function mapCodexRuntimeError(threadId, method, error) {
|
|
71589
72132
|
if (isCodexAppServerProcessExitedError(error) || isCodexAppServerTransportError(error)) return new ProviderAdapterSessionClosedError({
|
|
71590
|
-
provider: PROVIDER$
|
|
72133
|
+
provider: PROVIDER$4,
|
|
71591
72134
|
threadId,
|
|
71592
72135
|
cause: error
|
|
71593
72136
|
});
|
|
71594
72137
|
if (isCodexSessionRuntimeThreadIdMissingError(error)) return new ProviderAdapterSessionNotFoundError({
|
|
71595
|
-
provider: PROVIDER$
|
|
72138
|
+
provider: PROVIDER$4,
|
|
71596
72139
|
threadId,
|
|
71597
72140
|
cause: error
|
|
71598
72141
|
});
|
|
71599
72142
|
return new ProviderAdapterRequestError({
|
|
71600
|
-
provider: PROVIDER$
|
|
72143
|
+
provider: PROVIDER$4,
|
|
71601
72144
|
method,
|
|
71602
72145
|
detail: error.message,
|
|
71603
72146
|
cause: error
|
|
@@ -72362,10 +72905,10 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (codexConfig, o
|
|
|
72362
72905
|
const runtimeEventQueue = yield* Queue.unbounded();
|
|
72363
72906
|
const sessions = /* @__PURE__ */ new Map();
|
|
72364
72907
|
const startSession = (input) => Effect.scoped(Effect.gen(function* () {
|
|
72365
|
-
if (input.provider !== void 0 && input.provider !== PROVIDER$
|
|
72366
|
-
provider: PROVIDER$
|
|
72908
|
+
if (input.provider !== void 0 && input.provider !== PROVIDER$4) return yield* new ProviderAdapterValidationError({
|
|
72909
|
+
provider: PROVIDER$4,
|
|
72367
72910
|
operation: "startSession",
|
|
72368
|
-
issue: `Expected provider '${PROVIDER$
|
|
72911
|
+
issue: `Expected provider '${PROVIDER$4}' but received '${input.provider}'.`
|
|
72369
72912
|
});
|
|
72370
72913
|
const existing = sessions.get(input.threadId);
|
|
72371
72914
|
if (existing && !existing.stopped) yield* Effect.suspend(() => stopSessionInternal(existing));
|
|
@@ -72400,7 +72943,7 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (codexConfig, o
|
|
|
72400
72943
|
let sessionScopeTransferred = false;
|
|
72401
72944
|
yield* Effect.addFinalizer(() => sessionScopeTransferred ? Effect.void : Scope.close(sessionScope, Exit.void));
|
|
72402
72945
|
const runtime = yield* (options?.makeRuntime ?? makeCodexSessionRuntime)(runtimeInput).pipe(Effect.provideService(Scope.Scope, sessionScope), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, childProcessSpawner), Effect.provideService(Crypto.Crypto, crypto), Effect.mapError((cause) => new ProviderAdapterProcessError({
|
|
72403
|
-
provider: PROVIDER$
|
|
72946
|
+
provider: PROVIDER$4,
|
|
72404
72947
|
threadId: input.threadId,
|
|
72405
72948
|
detail: cause.message,
|
|
72406
72949
|
cause
|
|
@@ -72420,7 +72963,7 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (codexConfig, o
|
|
|
72420
72963
|
yield* Queue.offerAll(runtimeEventQueue, runtimeEvents);
|
|
72421
72964
|
})).pipe(Effect.forkChild);
|
|
72422
72965
|
const started = yield* runtime.start().pipe(Effect.mapError((cause) => new ProviderAdapterProcessError({
|
|
72423
|
-
provider: PROVIDER$
|
|
72966
|
+
provider: PROVIDER$4,
|
|
72424
72967
|
threadId: input.threadId,
|
|
72425
72968
|
detail: cause.message,
|
|
72426
72969
|
cause
|
|
@@ -72441,12 +72984,12 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (codexConfig, o
|
|
|
72441
72984
|
attachment
|
|
72442
72985
|
});
|
|
72443
72986
|
if (!attachmentPath) return yield* new ProviderAdapterRequestError({
|
|
72444
|
-
provider: PROVIDER$
|
|
72987
|
+
provider: PROVIDER$4,
|
|
72445
72988
|
method: "turn/start",
|
|
72446
72989
|
detail: `Invalid attachment id '${attachment.id}'.`
|
|
72447
72990
|
});
|
|
72448
72991
|
const bytes = yield* fileSystem.readFile(attachmentPath).pipe(Effect.mapError((cause) => new ProviderAdapterRequestError({
|
|
72449
|
-
provider: PROVIDER$
|
|
72992
|
+
provider: PROVIDER$4,
|
|
72450
72993
|
method: "turn/start",
|
|
72451
72994
|
detail: `Failed to read attachment file: ${cause.message}.`,
|
|
72452
72995
|
cause
|
|
@@ -72474,7 +73017,7 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (codexConfig, o
|
|
|
72474
73017
|
const requireSession = Effect.fn("requireSession")(function* (threadId) {
|
|
72475
73018
|
const session = sessions.get(threadId);
|
|
72476
73019
|
if (!session || session.stopped) return yield* new ProviderAdapterSessionNotFoundError({
|
|
72477
|
-
provider: PROVIDER$
|
|
73020
|
+
provider: PROVIDER$4,
|
|
72478
73021
|
threadId
|
|
72479
73022
|
});
|
|
72480
73023
|
return session;
|
|
@@ -72486,7 +73029,7 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (codexConfig, o
|
|
|
72486
73029
|
})));
|
|
72487
73030
|
const rollbackThread = (threadId, numTurns) => {
|
|
72488
73031
|
if (!Number.isInteger(numTurns) || numTurns < 1) return Effect.fail(new ProviderAdapterValidationError({
|
|
72489
|
-
provider: PROVIDER$
|
|
73032
|
+
provider: PROVIDER$4,
|
|
72490
73033
|
operation: "rollbackThread",
|
|
72491
73034
|
issue: "numTurns must be an integer >= 1."
|
|
72492
73035
|
}));
|
|
@@ -72522,7 +73065,7 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (codexConfig, o
|
|
|
72522
73065
|
}).pipe(Effect.asVoid);
|
|
72523
73066
|
yield* Effect.acquireRelease(Effect.void, () => stopAll().pipe(Effect.andThen(Queue.shutdown(runtimeEventQueue)), Effect.andThen(managedNativeEventLogger?.close() ?? Effect.void), Effect.ignore));
|
|
72524
73067
|
return {
|
|
72525
|
-
provider: PROVIDER$
|
|
73068
|
+
provider: PROVIDER$4,
|
|
72526
73069
|
capabilities: { sessionModelSwitch: "in-session" },
|
|
72527
73070
|
startSession,
|
|
72528
73071
|
sendTurn,
|
|
@@ -72817,8 +73360,8 @@ function codexContinuationIdentity(layout) {
|
|
|
72817
73360
|
* @module provider/Drivers/CodexDriver
|
|
72818
73361
|
*/
|
|
72819
73362
|
const decodeCodexSettings = Schema$1.decodeSync(CodexSettings);
|
|
72820
|
-
const DRIVER_KIND$
|
|
72821
|
-
const SNAPSHOT_REFRESH_INTERVAL$
|
|
73363
|
+
const DRIVER_KIND$4 = ProviderDriverKind.make("codex");
|
|
73364
|
+
const SNAPSHOT_REFRESH_INTERVAL$4 = Duration.minutes(5);
|
|
72822
73365
|
/**
|
|
72823
73366
|
* Codex's own installer puts a launcher in `~/.local/bin` pointing at a
|
|
72824
73367
|
* versioned release under `~/.codex/packages/standalone/`. That layout matches
|
|
@@ -72831,8 +73374,8 @@ function isCodexNativeCommandPath(commandPath) {
|
|
|
72831
73374
|
const normalized = normalizeCommandPath(commandPath);
|
|
72832
73375
|
return normalized.endsWith("/.local/bin/codex") || normalized.endsWith("/.local/bin/codex.exe") || normalized.includes("/.codex/packages/standalone/");
|
|
72833
73376
|
}
|
|
72834
|
-
const UPDATE$
|
|
72835
|
-
provider: DRIVER_KIND$
|
|
73377
|
+
const UPDATE$4 = makePackageManagedProviderMaintenanceResolver({
|
|
73378
|
+
provider: DRIVER_KIND$4,
|
|
72836
73379
|
npmPackageName: "@openai/codex",
|
|
72837
73380
|
homebrewFormula: "codex",
|
|
72838
73381
|
nativeUpdate: {
|
|
@@ -72848,16 +73391,16 @@ const UPDATE$3 = makePackageManagedProviderMaintenanceResolver({
|
|
|
72848
73391
|
* `providerSnapshot.ts` is widened to accept `instanceId`/`driver`, this
|
|
72849
73392
|
* wrapper disappears.
|
|
72850
73393
|
*/
|
|
72851
|
-
const withInstanceIdentity$
|
|
73394
|
+
const withInstanceIdentity$4 = (input) => (snapshot) => ({
|
|
72852
73395
|
...snapshot,
|
|
72853
73396
|
instanceId: input.instanceId,
|
|
72854
|
-
driver: DRIVER_KIND$
|
|
73397
|
+
driver: DRIVER_KIND$4,
|
|
72855
73398
|
...input.displayName ? { displayName: input.displayName } : {},
|
|
72856
73399
|
...input.accentColor ? { accentColor: input.accentColor } : {},
|
|
72857
73400
|
continuation: { groupKey: input.continuationGroupKey }
|
|
72858
73401
|
});
|
|
72859
73402
|
const CodexDriver = {
|
|
72860
|
-
driverKind: DRIVER_KIND$
|
|
73403
|
+
driverKind: DRIVER_KIND$4,
|
|
72861
73404
|
metadata: {
|
|
72862
73405
|
displayName: "Codex",
|
|
72863
73406
|
supportsMultipleInstances: true
|
|
@@ -72872,14 +73415,14 @@ const CodexDriver = {
|
|
|
72872
73415
|
const processEnv = mergeProviderInstanceEnvironment(environment);
|
|
72873
73416
|
const homeLayout = yield* resolveCodexHomeLayout(config);
|
|
72874
73417
|
const continuationIdentity = codexContinuationIdentity(homeLayout);
|
|
72875
|
-
const stampIdentity = withInstanceIdentity$
|
|
73418
|
+
const stampIdentity = withInstanceIdentity$4({
|
|
72876
73419
|
instanceId,
|
|
72877
73420
|
displayName,
|
|
72878
73421
|
accentColor,
|
|
72879
73422
|
continuationGroupKey: continuationIdentity.continuationKey
|
|
72880
73423
|
});
|
|
72881
73424
|
yield* materializeCodexShadowHome(homeLayout).pipe(Effect.mapError((cause) => new ProviderDriverError({
|
|
72882
|
-
driver: DRIVER_KIND$
|
|
73425
|
+
driver: DRIVER_KIND$4,
|
|
72883
73426
|
instanceId,
|
|
72884
73427
|
detail: cause.message,
|
|
72885
73428
|
cause
|
|
@@ -72889,7 +73432,7 @@ const CodexDriver = {
|
|
|
72889
73432
|
enabled,
|
|
72890
73433
|
homePath: homeLayout.effectiveHomePath ?? ""
|
|
72891
73434
|
};
|
|
72892
|
-
const maintenanceCapabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(UPDATE$
|
|
73435
|
+
const maintenanceCapabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(UPDATE$4, {
|
|
72893
73436
|
binaryPath: effectiveConfig.binaryPath,
|
|
72894
73437
|
env: processEnv
|
|
72895
73438
|
});
|
|
@@ -72909,16 +73452,16 @@ const CodexDriver = {
|
|
|
72909
73452
|
initialSnapshot: (settings) => makePendingCodexProvider(settings.provider).pipe(Effect.map(stampIdentity)),
|
|
72910
73453
|
checkProvider,
|
|
72911
73454
|
enrichSnapshot: ({ settings, snapshot, publishSnapshot }) => enrichProviderSnapshotWithVersionAdvisory(snapshot, maintenanceCapabilities, { enableProviderUpdateChecks: settings.enableProviderUpdateChecks }).pipe(Effect.provideService(HttpClient.HttpClient, httpClient), Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot))),
|
|
72912
|
-
refreshInterval: SNAPSHOT_REFRESH_INTERVAL$
|
|
73455
|
+
refreshInterval: SNAPSHOT_REFRESH_INTERVAL$4
|
|
72913
73456
|
}).pipe(Effect.mapError((cause) => new ProviderDriverError({
|
|
72914
|
-
driver: DRIVER_KIND$
|
|
73457
|
+
driver: DRIVER_KIND$4,
|
|
72915
73458
|
instanceId,
|
|
72916
73459
|
detail: `Failed to build Codex snapshot: ${cause.message ?? String(cause)}`,
|
|
72917
73460
|
cause
|
|
72918
73461
|
})));
|
|
72919
73462
|
return {
|
|
72920
73463
|
instanceId,
|
|
72921
|
-
driverKind: DRIVER_KIND$
|
|
73464
|
+
driverKind: DRIVER_KIND$4,
|
|
72922
73465
|
continuationIdentity,
|
|
72923
73466
|
displayName,
|
|
72924
73467
|
accentColor,
|
|
@@ -76895,18 +77438,18 @@ function deriveToolActivityPresentation(input) {
|
|
|
76895
77438
|
}
|
|
76896
77439
|
//#endregion
|
|
76897
77440
|
//#region src/provider/acp/AcpRuntimeModel.ts
|
|
76898
|
-
function isRecord$
|
|
77441
|
+
function isRecord$4(value) {
|
|
76899
77442
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
76900
77443
|
}
|
|
76901
77444
|
function isSessionModelState(value) {
|
|
76902
|
-
if (!isRecord$
|
|
77445
|
+
if (!isRecord$4(value) || typeof value.currentModelId !== "string") return false;
|
|
76903
77446
|
if (!Array.isArray(value.availableModels)) return false;
|
|
76904
|
-
return value.availableModels.every((model) => isRecord$
|
|
77447
|
+
return value.availableModels.every((model) => isRecord$4(model) && typeof model.modelId === "string" && typeof model.name === "string" && (model.description === void 0 || model.description === null || typeof model.description === "string"));
|
|
76905
77448
|
}
|
|
76906
77449
|
function isSessionModeState(value) {
|
|
76907
|
-
if (!isRecord$
|
|
77450
|
+
if (!isRecord$4(value) || typeof value.currentModeId !== "string") return false;
|
|
76908
77451
|
if (!Array.isArray(value.availableModes)) return false;
|
|
76909
|
-
return value.availableModes.every((mode) => isRecord$
|
|
77452
|
+
return value.availableModes.every((mode) => isRecord$4(mode) && typeof mode.id === "string" && typeof mode.name === "string" && (mode.description === void 0 || typeof mode.description === "string"));
|
|
76910
77453
|
}
|
|
76911
77454
|
function extractModelConfigId(sessionResponse) {
|
|
76912
77455
|
const configOptions = sessionResponse.configOptions;
|
|
@@ -76982,7 +77525,7 @@ function extractCommandFromTitle(title) {
|
|
|
76982
77525
|
return /`([^`]+)`/.exec(title)?.[1]?.trim() || void 0;
|
|
76983
77526
|
}
|
|
76984
77527
|
function extractToolCallCommand(rawInput, title) {
|
|
76985
|
-
if (isRecord$
|
|
77528
|
+
if (isRecord$4(rawInput)) {
|
|
76986
77529
|
const directCommand = normalizeCommandValue(rawInput.command);
|
|
76987
77530
|
if (directCommand) return directCommand;
|
|
76988
77531
|
const executable = typeof rawInput.executable === "string" ? rawInput.executable.trim() : "";
|
|
@@ -77104,7 +77647,7 @@ function parsePermissionRequest(params) {
|
|
|
77104
77647
|
}
|
|
77105
77648
|
function sessionUpdateIsReplay(params) {
|
|
77106
77649
|
const meta = params._meta;
|
|
77107
|
-
return isRecord$
|
|
77650
|
+
return isRecord$4(meta) && meta.isReplay === true;
|
|
77108
77651
|
}
|
|
77109
77652
|
const waitForSessionLoadReplayIdle = (input) => Effect.gen(function* () {
|
|
77110
77653
|
const pollInterval = Duration.millis(25);
|
|
@@ -77119,8 +77662,8 @@ const waitForSessionLoadReplayIdle = (input) => Effect.gen(function* () {
|
|
|
77119
77662
|
});
|
|
77120
77663
|
function syntheticLoadSessionResponseFromInitialize(initializeResult) {
|
|
77121
77664
|
const meta = initializeResult._meta;
|
|
77122
|
-
const modelState = isRecord$
|
|
77123
|
-
const modeState = isRecord$
|
|
77665
|
+
const modelState = isRecord$4(meta) ? meta.modelState : void 0;
|
|
77666
|
+
const modeState = isRecord$4(meta) ? meta.modeState : void 0;
|
|
77124
77667
|
const models = isSessionModelState(modelState) ? modelState : void 0;
|
|
77125
77668
|
const modes = isSessionModeState(modeState) ? modeState : void 0;
|
|
77126
77669
|
return {
|
|
@@ -77678,7 +78221,7 @@ const CURSOR_PRESENTATION = {
|
|
|
77678
78221
|
badgeLabel: "Early Access",
|
|
77679
78222
|
showInteractionModeToggle: true
|
|
77680
78223
|
};
|
|
77681
|
-
const EMPTY_CAPABILITIES$
|
|
78224
|
+
const EMPTY_CAPABILITIES$2 = createModelCapabilities({ optionDescriptors: [] });
|
|
77682
78225
|
const CURSOR_ACP_MODEL_DISCOVERY_TIMEOUT_MS = 15e3;
|
|
77683
78226
|
const CURSOR_PARAMETERIZED_MODEL_PICKER_MIN_VERSION_DATE = 20260408;
|
|
77684
78227
|
const CURSOR_CLI_INSTALLATION_DOCS_URL = "https://cursor.com/docs/cli/installation";
|
|
@@ -77786,7 +78329,7 @@ function getBooleanCurrentValue(option) {
|
|
|
77786
78329
|
if (normalized === "false") return false;
|
|
77787
78330
|
}
|
|
77788
78331
|
function buildCursorCapabilitiesFromConfigOptions(configOptions) {
|
|
77789
|
-
if (!configOptions || configOptions.length === 0) return EMPTY_CAPABILITIES$
|
|
78332
|
+
if (!configOptions || configOptions.length === 0) return EMPTY_CAPABILITIES$2;
|
|
77790
78333
|
const reasoningConfig = findCursorEffortConfigOption(configOptions);
|
|
77791
78334
|
const reasoningEffortLevels = reasoningConfig?.type === "select" ? flattenSessionConfigSelectOptions(reasoningConfig).flatMap((entry) => {
|
|
77792
78335
|
const normalizedValue = normalizeCursorReasoningValue(entry.value);
|
|
@@ -77955,7 +78498,7 @@ const discoverCursorModelsViaListAvailableModels = (cursorSettings, environment)
|
|
|
77955
78498
|
}), environment);
|
|
77956
78499
|
const discoverCursorModelsViaAcp = (cursorSettings, environment) => discoverCursorModelsViaListAvailableModels(cursorSettings, environment);
|
|
77957
78500
|
function getCursorFallbackModels(cursorSettings) {
|
|
77958
|
-
return providerModelsFromSettings([], cursorSettings.customModels, EMPTY_CAPABILITIES$
|
|
78501
|
+
return providerModelsFromSettings([], cursorSettings.customModels, EMPTY_CAPABILITIES$2);
|
|
77959
78502
|
}
|
|
77960
78503
|
/** Timeout for `agent about` — it's slower than a simple `--version` probe. */
|
|
77961
78504
|
const ABOUT_TIMEOUT_MS = 8e3;
|
|
@@ -77991,7 +78534,7 @@ function buildCursorProviderSnapshot(input) {
|
|
|
77991
78534
|
presentation: CURSOR_PRESENTATION,
|
|
77992
78535
|
enabled: input.cursorSettings.enabled,
|
|
77993
78536
|
checkedAt: input.checkedAt,
|
|
77994
|
-
models: providerModelsFromSettings(input.discoveredModels ?? [], input.cursorSettings.customModels, EMPTY_CAPABILITIES$
|
|
78537
|
+
models: providerModelsFromSettings(input.discoveredModels ?? [], input.cursorSettings.customModels, EMPTY_CAPABILITIES$2),
|
|
77995
78538
|
probe: {
|
|
77996
78539
|
installed: true,
|
|
77997
78540
|
version: input.parsed.version,
|
|
@@ -78344,7 +78887,7 @@ function applyCursorAcpModelSelection(input) {
|
|
|
78344
78887
|
//#endregion
|
|
78345
78888
|
//#region src/textGeneration/CursorTextGeneration.ts
|
|
78346
78889
|
const CURSOR_TIMEOUT_MS = 18e4;
|
|
78347
|
-
const isTextGenerationError$
|
|
78890
|
+
const isTextGenerationError$2 = Schema$1.is(TextGenerationError);
|
|
78348
78891
|
/**
|
|
78349
78892
|
* Build a Cursor text-generation closure bound to a specific `CursorSettings`
|
|
78350
78893
|
* payload. See `makeCodexAdapter` for the overall per-instance rationale.
|
|
@@ -78395,7 +78938,7 @@ const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(function*
|
|
|
78395
78938
|
detail: "Cursor Agent request timed out."
|
|
78396
78939
|
})),
|
|
78397
78940
|
onSome: (value) => Effect.succeed(value)
|
|
78398
|
-
})), Effect.mapError((cause) => isTextGenerationError$
|
|
78941
|
+
})), Effect.mapError((cause) => isTextGenerationError$2(cause) ? cause : new TextGenerationError({
|
|
78399
78942
|
operation,
|
|
78400
78943
|
detail: "Cursor ACP request failed.",
|
|
78401
78944
|
cause
|
|
@@ -78410,7 +78953,7 @@ const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(function*
|
|
|
78410
78953
|
detail: "Cursor Agent returned invalid structured output.",
|
|
78411
78954
|
cause
|
|
78412
78955
|
})) }));
|
|
78413
|
-
}).pipe(Effect.mapError((cause) => isTextGenerationError$
|
|
78956
|
+
}).pipe(Effect.mapError((cause) => isTextGenerationError$2(cause) ? cause : new TextGenerationError({
|
|
78414
78957
|
operation,
|
|
78415
78958
|
detail: "Cursor ACP text generation failed.",
|
|
78416
78959
|
cause
|
|
@@ -78752,7 +79295,7 @@ const makeAcpNativeLoggerFactory = Effect.fn("makeAcpNativeLoggerFactory")(funct
|
|
|
78752
79295
|
* @module CursorAdapterLive
|
|
78753
79296
|
*/
|
|
78754
79297
|
const encodeUnknownJsonStringExit$1 = Schema$1.encodeUnknownExit(Schema$1.UnknownFromJsonString);
|
|
78755
|
-
const PROVIDER$
|
|
79298
|
+
const PROVIDER$3 = ProviderDriverKind.make("cursor");
|
|
78756
79299
|
const CURSOR_RESUME_VERSION = 1;
|
|
78757
79300
|
const ACP_PLAN_MODE_ALIASES = ["plan", "architect"];
|
|
78758
79301
|
const ACP_IMPLEMENT_MODE_ALIASES = [
|
|
@@ -78775,11 +79318,11 @@ function settlePendingUserInputsAsEmptyAnswers(pendingUserInputs) {
|
|
|
78775
79318
|
const pendingEntries = Array.from(pendingUserInputs.values());
|
|
78776
79319
|
return Effect.forEach(pendingEntries, (pending) => Deferred.succeed(pending.answers, {}).pipe(Effect.ignore), { discard: true });
|
|
78777
79320
|
}
|
|
78778
|
-
function isRecord$
|
|
79321
|
+
function isRecord$3(value) {
|
|
78779
79322
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
78780
79323
|
}
|
|
78781
79324
|
function parseCursorResume(raw) {
|
|
78782
|
-
if (!isRecord$
|
|
79325
|
+
if (!isRecord$3(raw)) return void 0;
|
|
78783
79326
|
if (raw.schemaVersion !== CURSOR_RESUME_VERSION) return void 0;
|
|
78784
79327
|
if (typeof raw.sessionId !== "string" || !raw.sessionId.trim()) return void 0;
|
|
78785
79328
|
return { sessionId: raw.sessionId.trim() };
|
|
@@ -78861,7 +79404,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
78861
79404
|
const runtimeEventPubSub = yield* PubSub.unbounded();
|
|
78862
79405
|
const nowIso = Effect.map(DateTime.now, DateTime.formatIso);
|
|
78863
79406
|
const randomUUIDv4 = crypto.randomUUIDv4.pipe(Effect.mapError((cause) => new ProviderAdapterRequestError({
|
|
78864
|
-
provider: PROVIDER$
|
|
79407
|
+
provider: PROVIDER$3,
|
|
78865
79408
|
method: "crypto/randomUUIDv4",
|
|
78866
79409
|
detail: "Failed to generate Cursor runtime identifier.",
|
|
78867
79410
|
cause
|
|
@@ -78896,7 +79439,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
78896
79439
|
event: {
|
|
78897
79440
|
id: yield* randomUUIDv4,
|
|
78898
79441
|
kind: "notification",
|
|
78899
|
-
provider: PROVIDER$
|
|
79442
|
+
provider: PROVIDER$3,
|
|
78900
79443
|
createdAt: observedAt,
|
|
78901
79444
|
method,
|
|
78902
79445
|
threadId,
|
|
@@ -78910,7 +79453,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
78910
79453
|
ctx.lastPlanFingerprint = fingerprint;
|
|
78911
79454
|
yield* offerRuntimeEvent(makeAcpPlanUpdatedEvent({
|
|
78912
79455
|
stamp: yield* makeEventStamp(),
|
|
78913
|
-
provider: PROVIDER$
|
|
79456
|
+
provider: PROVIDER$3,
|
|
78914
79457
|
threadId: ctx.threadId,
|
|
78915
79458
|
turnId: ctx.activeTurnId,
|
|
78916
79459
|
payload,
|
|
@@ -78922,7 +79465,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
78922
79465
|
const requireSession = (threadId) => {
|
|
78923
79466
|
const ctx = sessions.get(threadId);
|
|
78924
79467
|
if (!ctx || ctx.stopped) return Effect.fail(new ProviderAdapterSessionNotFoundError({
|
|
78925
|
-
provider: PROVIDER$
|
|
79468
|
+
provider: PROVIDER$3,
|
|
78926
79469
|
threadId
|
|
78927
79470
|
}));
|
|
78928
79471
|
return Effect.succeed(ctx);
|
|
@@ -78938,19 +79481,19 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
78938
79481
|
yield* offerRuntimeEvent({
|
|
78939
79482
|
type: "session.exited",
|
|
78940
79483
|
...yield* makeEventStamp(),
|
|
78941
|
-
provider: PROVIDER$
|
|
79484
|
+
provider: PROVIDER$3,
|
|
78942
79485
|
threadId: ctx.threadId,
|
|
78943
79486
|
payload: { exitKind: "graceful" }
|
|
78944
79487
|
});
|
|
78945
79488
|
});
|
|
78946
79489
|
const startSession = (input) => withThreadLock(input.threadId, Effect.gen(function* () {
|
|
78947
|
-
if (input.provider !== void 0 && input.provider !== PROVIDER$
|
|
78948
|
-
provider: PROVIDER$
|
|
79490
|
+
if (input.provider !== void 0 && input.provider !== PROVIDER$3) return yield* new ProviderAdapterValidationError({
|
|
79491
|
+
provider: PROVIDER$3,
|
|
78949
79492
|
operation: "startSession",
|
|
78950
|
-
issue: `Expected provider '${PROVIDER$
|
|
79493
|
+
issue: `Expected provider '${PROVIDER$3}' but received '${input.provider}'.`
|
|
78951
79494
|
});
|
|
78952
79495
|
if (!input.cwd?.trim()) return yield* new ProviderAdapterValidationError({
|
|
78953
|
-
provider: PROVIDER$
|
|
79496
|
+
provider: PROVIDER$3,
|
|
78954
79497
|
operation: "startSession",
|
|
78955
79498
|
issue: "cwd is required and must be non-empty."
|
|
78956
79499
|
});
|
|
@@ -78967,7 +79510,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
78967
79510
|
const resumeSessionId = parseCursorResume(input.resumeCursor)?.sessionId;
|
|
78968
79511
|
const acpNativeLoggers = makeAcpNativeLoggers({
|
|
78969
79512
|
nativeEventLogger,
|
|
78970
|
-
provider: PROVIDER$
|
|
79513
|
+
provider: PROVIDER$3,
|
|
78971
79514
|
threadId: input.threadId
|
|
78972
79515
|
});
|
|
78973
79516
|
const effectiveCursorSettings = options?.resolveSettings ? yield* options.resolveSettings : cursorSettings;
|
|
@@ -78993,7 +79536,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
78993
79536
|
}] } : {},
|
|
78994
79537
|
...acpNativeLoggers
|
|
78995
79538
|
}).pipe(Effect.provideService(Crypto.Crypto, crypto), Effect.provideService(Scope.Scope, sessionScope), Effect.mapError((cause) => new ProviderAdapterProcessError({
|
|
78996
|
-
provider: PROVIDER$
|
|
79539
|
+
provider: PROVIDER$3,
|
|
78997
79540
|
threadId: input.threadId,
|
|
78998
79541
|
detail: cause.message,
|
|
78999
79542
|
cause
|
|
@@ -79008,7 +79551,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79008
79551
|
yield* offerRuntimeEvent({
|
|
79009
79552
|
type: "user-input.requested",
|
|
79010
79553
|
...yield* makeEventStamp(),
|
|
79011
|
-
provider: PROVIDER$
|
|
79554
|
+
provider: PROVIDER$3,
|
|
79012
79555
|
threadId: input.threadId,
|
|
79013
79556
|
turnId: ctx?.activeTurnId,
|
|
79014
79557
|
requestId: runtimeRequestId,
|
|
@@ -79024,7 +79567,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79024
79567
|
yield* offerRuntimeEvent({
|
|
79025
79568
|
type: "user-input.resolved",
|
|
79026
79569
|
...yield* makeEventStamp(),
|
|
79027
|
-
provider: PROVIDER$
|
|
79570
|
+
provider: PROVIDER$3,
|
|
79028
79571
|
threadId: input.threadId,
|
|
79029
79572
|
turnId: ctx?.activeTurnId,
|
|
79030
79573
|
requestId: runtimeRequestId,
|
|
@@ -79037,7 +79580,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79037
79580
|
yield* offerRuntimeEvent({
|
|
79038
79581
|
type: "turn.proposed.completed",
|
|
79039
79582
|
...yield* makeEventStamp(),
|
|
79040
|
-
provider: PROVIDER$
|
|
79583
|
+
provider: PROVIDER$3,
|
|
79041
79584
|
threadId: input.threadId,
|
|
79042
79585
|
turnId: ctx?.activeTurnId,
|
|
79043
79586
|
payload: { planMarkdown: extractPlanMarkdown(params) },
|
|
@@ -79072,7 +79615,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79072
79615
|
});
|
|
79073
79616
|
yield* offerRuntimeEvent(makeAcpRequestOpenedEvent({
|
|
79074
79617
|
stamp: yield* makeEventStamp(),
|
|
79075
|
-
provider: PROVIDER$
|
|
79618
|
+
provider: PROVIDER$3,
|
|
79076
79619
|
threadId: input.threadId,
|
|
79077
79620
|
turnId: ctx?.activeTurnId,
|
|
79078
79621
|
requestId: runtimeRequestId,
|
|
@@ -79087,7 +79630,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79087
79630
|
pendingApprovals.delete(requestId);
|
|
79088
79631
|
yield* offerRuntimeEvent(makeAcpRequestResolvedEvent({
|
|
79089
79632
|
stamp: yield* makeEventStamp(),
|
|
79090
|
-
provider: PROVIDER$
|
|
79633
|
+
provider: PROVIDER$3,
|
|
79091
79634
|
threadId: input.threadId,
|
|
79092
79635
|
turnId: ctx?.activeTurnId,
|
|
79093
79636
|
requestId: runtimeRequestId,
|
|
@@ -79100,17 +79643,17 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79100
79643
|
} };
|
|
79101
79644
|
})));
|
|
79102
79645
|
return yield* acp.start();
|
|
79103
|
-
}).pipe(Effect.mapError((error) => mapAcpToAdapterError(PROVIDER$
|
|
79646
|
+
}).pipe(Effect.mapError((error) => mapAcpToAdapterError(PROVIDER$3, input.threadId, "session/start", error)));
|
|
79104
79647
|
yield* applyRequestedSessionConfiguration({
|
|
79105
79648
|
runtime: acp,
|
|
79106
79649
|
runtimeMode: input.runtimeMode,
|
|
79107
79650
|
interactionMode: void 0,
|
|
79108
79651
|
modelSelection: cursorModelSelection,
|
|
79109
|
-
mapError: ({ cause, method }) => mapAcpToAdapterError(PROVIDER$
|
|
79652
|
+
mapError: ({ cause, method }) => mapAcpToAdapterError(PROVIDER$3, input.threadId, method, cause)
|
|
79110
79653
|
});
|
|
79111
79654
|
const now = yield* nowIso;
|
|
79112
79655
|
const session = {
|
|
79113
|
-
provider: PROVIDER$
|
|
79656
|
+
provider: PROVIDER$3,
|
|
79114
79657
|
providerInstanceId: boundInstanceId,
|
|
79115
79658
|
status: "ready",
|
|
79116
79659
|
runtimeMode: input.runtimeMode,
|
|
@@ -79147,7 +79690,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79147
79690
|
case "AssistantItemStarted":
|
|
79148
79691
|
yield* offerRuntimeEvent(makeAcpAssistantItemEvent({
|
|
79149
79692
|
stamp: yield* makeEventStamp(),
|
|
79150
|
-
provider: PROVIDER$
|
|
79693
|
+
provider: PROVIDER$3,
|
|
79151
79694
|
threadId: ctx.threadId,
|
|
79152
79695
|
turnId: ctx.activeTurnId,
|
|
79153
79696
|
itemId: event.itemId,
|
|
@@ -79157,7 +79700,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79157
79700
|
case "AssistantItemCompleted":
|
|
79158
79701
|
yield* offerRuntimeEvent(makeAcpAssistantItemEvent({
|
|
79159
79702
|
stamp: yield* makeEventStamp(),
|
|
79160
|
-
provider: PROVIDER$
|
|
79703
|
+
provider: PROVIDER$3,
|
|
79161
79704
|
threadId: ctx.threadId,
|
|
79162
79705
|
turnId: ctx.activeTurnId,
|
|
79163
79706
|
itemId: event.itemId,
|
|
@@ -79172,7 +79715,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79172
79715
|
yield* logNative(ctx.threadId, "session/update", event.rawPayload, "acp.jsonrpc");
|
|
79173
79716
|
yield* offerRuntimeEvent(makeAcpToolCallEvent({
|
|
79174
79717
|
stamp: yield* makeEventStamp(),
|
|
79175
|
-
provider: PROVIDER$
|
|
79718
|
+
provider: PROVIDER$3,
|
|
79176
79719
|
threadId: ctx.threadId,
|
|
79177
79720
|
turnId: ctx.activeTurnId,
|
|
79178
79721
|
toolCall: event.toolCall,
|
|
@@ -79183,7 +79726,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79183
79726
|
yield* logNative(ctx.threadId, "session/update", event.rawPayload, "acp.jsonrpc");
|
|
79184
79727
|
yield* offerRuntimeEvent(makeAcpContentDeltaEvent({
|
|
79185
79728
|
stamp: yield* makeEventStamp(),
|
|
79186
|
-
provider: PROVIDER$
|
|
79729
|
+
provider: PROVIDER$3,
|
|
79187
79730
|
threadId: ctx.threadId,
|
|
79188
79731
|
turnId: ctx.activeTurnId,
|
|
79189
79732
|
...event.itemId ? { itemId: event.itemId } : {},
|
|
@@ -79199,14 +79742,14 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79199
79742
|
yield* offerRuntimeEvent({
|
|
79200
79743
|
type: "session.started",
|
|
79201
79744
|
...yield* makeEventStamp(),
|
|
79202
|
-
provider: PROVIDER$
|
|
79745
|
+
provider: PROVIDER$3,
|
|
79203
79746
|
threadId: input.threadId,
|
|
79204
79747
|
payload: { resume: started.initializeResult }
|
|
79205
79748
|
});
|
|
79206
79749
|
yield* offerRuntimeEvent({
|
|
79207
79750
|
type: "session.state.changed",
|
|
79208
79751
|
...yield* makeEventStamp(),
|
|
79209
|
-
provider: PROVIDER$
|
|
79752
|
+
provider: PROVIDER$3,
|
|
79210
79753
|
threadId: input.threadId,
|
|
79211
79754
|
payload: {
|
|
79212
79755
|
state: "ready",
|
|
@@ -79216,7 +79759,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79216
79759
|
yield* offerRuntimeEvent({
|
|
79217
79760
|
type: "thread.started",
|
|
79218
79761
|
...yield* makeEventStamp(),
|
|
79219
|
-
provider: PROVIDER$
|
|
79762
|
+
provider: PROVIDER$3,
|
|
79220
79763
|
threadId: input.threadId,
|
|
79221
79764
|
payload: { providerThreadId: started.sessionId }
|
|
79222
79765
|
});
|
|
@@ -79239,7 +79782,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79239
79782
|
model,
|
|
79240
79783
|
options: turnModelSelection?.options
|
|
79241
79784
|
},
|
|
79242
|
-
mapError: ({ cause, method }) => mapAcpToAdapterError(PROVIDER$
|
|
79785
|
+
mapError: ({ cause, method }) => mapAcpToAdapterError(PROVIDER$3, input.threadId, method, cause)
|
|
79243
79786
|
});
|
|
79244
79787
|
ctx.activeTurnId = turnId;
|
|
79245
79788
|
if (steeringTurnId === void 0) ctx.lastPlanFingerprint = void 0;
|
|
@@ -79251,7 +79794,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79251
79794
|
if (steeringTurnId === void 0) yield* offerRuntimeEvent({
|
|
79252
79795
|
type: "turn.started",
|
|
79253
79796
|
...yield* makeEventStamp(),
|
|
79254
|
-
provider: PROVIDER$
|
|
79797
|
+
provider: PROVIDER$3,
|
|
79255
79798
|
threadId: input.threadId,
|
|
79256
79799
|
turnId,
|
|
79257
79800
|
payload: { model: resolvedModel }
|
|
@@ -79267,12 +79810,12 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79267
79810
|
attachment
|
|
79268
79811
|
});
|
|
79269
79812
|
if (!attachmentPath) return yield* new ProviderAdapterRequestError({
|
|
79270
|
-
provider: PROVIDER$
|
|
79813
|
+
provider: PROVIDER$3,
|
|
79271
79814
|
method: "session/prompt",
|
|
79272
79815
|
detail: `Invalid attachment id '${attachment.id}'.`
|
|
79273
79816
|
});
|
|
79274
79817
|
const bytes = yield* fileSystem.readFile(attachmentPath).pipe(Effect.mapError((cause) => new ProviderAdapterRequestError({
|
|
79275
|
-
provider: PROVIDER$
|
|
79818
|
+
provider: PROVIDER$3,
|
|
79276
79819
|
method: "session/prompt",
|
|
79277
79820
|
detail: cause.message,
|
|
79278
79821
|
cause
|
|
@@ -79284,11 +79827,11 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79284
79827
|
});
|
|
79285
79828
|
}
|
|
79286
79829
|
if (promptParts.length === 0) return yield* new ProviderAdapterValidationError({
|
|
79287
|
-
provider: PROVIDER$
|
|
79830
|
+
provider: PROVIDER$3,
|
|
79288
79831
|
operation: "sendTurn",
|
|
79289
79832
|
issue: "Turn requires non-empty text or attachments."
|
|
79290
79833
|
});
|
|
79291
|
-
const result = yield* ctx.acp.prompt({ prompt: promptParts }).pipe(Effect.mapError((error) => mapAcpToAdapterError(PROVIDER$
|
|
79834
|
+
const result = yield* ctx.acp.prompt({ prompt: promptParts }).pipe(Effect.mapError((error) => mapAcpToAdapterError(PROVIDER$3, input.threadId, "session/prompt", error)));
|
|
79292
79835
|
const turnRecord = ctx.turns.find((turn) => turn.id === turnId);
|
|
79293
79836
|
if (turnRecord) turnRecord.items.push({
|
|
79294
79837
|
prompt: promptParts,
|
|
@@ -79310,7 +79853,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79310
79853
|
if (ctx.promptsInFlight === 1) yield* offerRuntimeEvent({
|
|
79311
79854
|
type: "turn.completed",
|
|
79312
79855
|
...yield* makeEventStamp(),
|
|
79313
|
-
provider: PROVIDER$
|
|
79856
|
+
provider: PROVIDER$3,
|
|
79314
79857
|
threadId: input.threadId,
|
|
79315
79858
|
turnId,
|
|
79316
79859
|
payload: {
|
|
@@ -79331,12 +79874,12 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79331
79874
|
const ctx = yield* requireSession(threadId);
|
|
79332
79875
|
yield* settlePendingApprovalsAsCancelled$1(ctx.pendingApprovals);
|
|
79333
79876
|
yield* settlePendingUserInputsAsEmptyAnswers(ctx.pendingUserInputs);
|
|
79334
|
-
yield* Effect.ignore(ctx.acp.cancel.pipe(Effect.mapError((error) => mapAcpToAdapterError(PROVIDER$
|
|
79877
|
+
yield* Effect.ignore(ctx.acp.cancel.pipe(Effect.mapError((error) => mapAcpToAdapterError(PROVIDER$3, threadId, "session/cancel", error))));
|
|
79335
79878
|
});
|
|
79336
79879
|
const respondToRequest = (threadId, requestId, decision) => Effect.gen(function* () {
|
|
79337
79880
|
const pending = (yield* requireSession(threadId)).pendingApprovals.get(requestId);
|
|
79338
79881
|
if (!pending) return yield* new ProviderAdapterRequestError({
|
|
79339
|
-
provider: PROVIDER$
|
|
79882
|
+
provider: PROVIDER$3,
|
|
79340
79883
|
method: "session/request_permission",
|
|
79341
79884
|
detail: `Unknown pending approval request: ${requestId}`
|
|
79342
79885
|
});
|
|
@@ -79345,7 +79888,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79345
79888
|
const respondToUserInput = (threadId, requestId, answers) => Effect.gen(function* () {
|
|
79346
79889
|
const pending = (yield* requireSession(threadId)).pendingUserInputs.get(requestId);
|
|
79347
79890
|
if (!pending) return yield* new ProviderAdapterRequestError({
|
|
79348
|
-
provider: PROVIDER$
|
|
79891
|
+
provider: PROVIDER$3,
|
|
79349
79892
|
method: "cursor/ask_question",
|
|
79350
79893
|
detail: `Unknown pending user-input request: ${requestId}`
|
|
79351
79894
|
});
|
|
@@ -79360,7 +79903,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79360
79903
|
const rollbackThread = (threadId, numTurns) => Effect.gen(function* () {
|
|
79361
79904
|
const ctx = yield* requireSession(threadId);
|
|
79362
79905
|
if (!Number.isInteger(numTurns) || numTurns < 1) return yield* new ProviderAdapterValidationError({
|
|
79363
|
-
provider: PROVIDER$
|
|
79906
|
+
provider: PROVIDER$3,
|
|
79364
79907
|
operation: "rollbackThread",
|
|
79365
79908
|
issue: "numTurns must be an integer >= 1."
|
|
79366
79909
|
});
|
|
@@ -79384,7 +79927,7 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79384
79927
|
yield* Effect.addFinalizer(() => Effect.forEach(sessions.values(), stopSessionInternal, { discard: true }).pipe(Effect.catch((cause) => Effect.logError("Failed to emit Cursor session shutdown event.", { cause })), Effect.tap(() => PubSub.shutdown(runtimeEventPubSub)), Effect.tap(() => managedNativeEventLogger?.close() ?? Effect.void)));
|
|
79385
79928
|
const streamEvents = Stream.fromPubSub(runtimeEventPubSub);
|
|
79386
79929
|
return {
|
|
79387
|
-
provider: PROVIDER$
|
|
79930
|
+
provider: PROVIDER$3,
|
|
79388
79931
|
capabilities: { sessionModelSwitch: "in-session" },
|
|
79389
79932
|
startSession,
|
|
79390
79933
|
sendTurn,
|
|
@@ -79417,25 +79960,25 @@ function makeCursorAdapter(cursorSettings, options) {
|
|
|
79417
79960
|
* @module provider/Drivers/CursorDriver
|
|
79418
79961
|
*/
|
|
79419
79962
|
const decodeCursorSettings = Schema$1.decodeSync(CursorSettings);
|
|
79420
|
-
const DRIVER_KIND$
|
|
79421
|
-
const SNAPSHOT_REFRESH_INTERVAL$
|
|
79422
|
-
const UPDATE$
|
|
79423
|
-
provider: DRIVER_KIND$
|
|
79963
|
+
const DRIVER_KIND$3 = ProviderDriverKind.make("cursor");
|
|
79964
|
+
const SNAPSHOT_REFRESH_INTERVAL$3 = Duration.minutes(5);
|
|
79965
|
+
const UPDATE$3 = { resolve: (options) => makeProviderMaintenanceCapabilities({
|
|
79966
|
+
provider: DRIVER_KIND$3,
|
|
79424
79967
|
packageName: null,
|
|
79425
79968
|
updateExecutable: options?.binaryPath?.trim() || "cursor-agent",
|
|
79426
79969
|
updateArgs: ["update"],
|
|
79427
79970
|
updateLockKey: "cursor-agent"
|
|
79428
79971
|
}) };
|
|
79429
|
-
const withInstanceIdentity$
|
|
79972
|
+
const withInstanceIdentity$3 = (input) => (snapshot) => ({
|
|
79430
79973
|
...snapshot,
|
|
79431
79974
|
instanceId: input.instanceId,
|
|
79432
|
-
driver: DRIVER_KIND$
|
|
79975
|
+
driver: DRIVER_KIND$3,
|
|
79433
79976
|
...input.displayName ? { displayName: input.displayName } : {},
|
|
79434
79977
|
...input.accentColor ? { accentColor: input.accentColor } : {},
|
|
79435
79978
|
continuation: { groupKey: input.continuationGroupKey }
|
|
79436
79979
|
});
|
|
79437
79980
|
const CursorDriver = {
|
|
79438
|
-
driverKind: DRIVER_KIND$
|
|
79981
|
+
driverKind: DRIVER_KIND$3,
|
|
79439
79982
|
metadata: {
|
|
79440
79983
|
displayName: "Cursor",
|
|
79441
79984
|
supportsMultipleInstances: true
|
|
@@ -79452,10 +79995,10 @@ const CursorDriver = {
|
|
|
79452
79995
|
const eventLoggers = yield* ProviderEventLoggers;
|
|
79453
79996
|
const processEnv = mergeProviderInstanceEnvironment(environment);
|
|
79454
79997
|
const continuationIdentity = defaultProviderContinuationIdentity({
|
|
79455
|
-
driverKind: DRIVER_KIND$
|
|
79998
|
+
driverKind: DRIVER_KIND$3,
|
|
79456
79999
|
instanceId
|
|
79457
80000
|
});
|
|
79458
|
-
const stampIdentity = withInstanceIdentity$
|
|
80001
|
+
const stampIdentity = withInstanceIdentity$3({
|
|
79459
80002
|
instanceId,
|
|
79460
80003
|
displayName,
|
|
79461
80004
|
accentColor,
|
|
@@ -79465,7 +80008,7 @@ const CursorDriver = {
|
|
|
79465
80008
|
...config,
|
|
79466
80009
|
enabled
|
|
79467
80010
|
};
|
|
79468
|
-
const maintenanceCapabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(UPDATE$
|
|
80011
|
+
const maintenanceCapabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(UPDATE$3, {
|
|
79469
80012
|
binaryPath: effectiveConfig.binaryPath,
|
|
79470
80013
|
env: processEnv
|
|
79471
80014
|
});
|
|
@@ -79493,16 +80036,16 @@ const CursorDriver = {
|
|
|
79493
80036
|
stampIdentity,
|
|
79494
80037
|
httpClient
|
|
79495
80038
|
}),
|
|
79496
|
-
refreshInterval: SNAPSHOT_REFRESH_INTERVAL$
|
|
80039
|
+
refreshInterval: SNAPSHOT_REFRESH_INTERVAL$3
|
|
79497
80040
|
}).pipe(Effect.mapError((cause) => new ProviderDriverError({
|
|
79498
|
-
driver: DRIVER_KIND$
|
|
80041
|
+
driver: DRIVER_KIND$3,
|
|
79499
80042
|
instanceId,
|
|
79500
80043
|
detail: `Failed to build Cursor snapshot: ${cause.message ?? String(cause)}`,
|
|
79501
80044
|
cause
|
|
79502
80045
|
})));
|
|
79503
80046
|
return {
|
|
79504
80047
|
instanceId,
|
|
79505
|
-
driverKind: DRIVER_KIND$
|
|
80048
|
+
driverKind: DRIVER_KIND$3,
|
|
79506
80049
|
continuationIdentity,
|
|
79507
80050
|
displayName,
|
|
79508
80051
|
accentColor,
|
|
@@ -79767,7 +80310,7 @@ function applyGrokAcpModelSelection(input) {
|
|
|
79767
80310
|
//#endregion
|
|
79768
80311
|
//#region src/textGeneration/GrokTextGeneration.ts
|
|
79769
80312
|
const GROK_TIMEOUT_MS = 18e4;
|
|
79770
|
-
const isTextGenerationError = Schema$1.is(TextGenerationError);
|
|
80313
|
+
const isTextGenerationError$1 = Schema$1.is(TextGenerationError);
|
|
79771
80314
|
const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(function* (grokSettings, environment = process.env) {
|
|
79772
80315
|
const crypto = yield* Crypto.Crypto;
|
|
79773
80316
|
const commandSpawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
@@ -79813,7 +80356,7 @@ const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(function* (gr
|
|
|
79813
80356
|
detail: "Grok ACP request timed out."
|
|
79814
80357
|
})),
|
|
79815
80358
|
onSome: (value) => Effect.succeed(value)
|
|
79816
|
-
})), Effect.mapError((cause) => isTextGenerationError(cause) ? cause : new TextGenerationError({
|
|
80359
|
+
})), Effect.mapError((cause) => isTextGenerationError$1(cause) ? cause : new TextGenerationError({
|
|
79817
80360
|
operation,
|
|
79818
80361
|
detail: "Grok ACP request failed.",
|
|
79819
80362
|
cause
|
|
@@ -79828,7 +80371,7 @@ const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(function* (gr
|
|
|
79828
80371
|
detail: "Grok Agent returned invalid structured output.",
|
|
79829
80372
|
cause
|
|
79830
80373
|
})) }));
|
|
79831
|
-
}).pipe(Effect.mapError((cause) => isTextGenerationError(cause) ? cause : new TextGenerationError({
|
|
80374
|
+
}).pipe(Effect.mapError((cause) => isTextGenerationError$1(cause) ? cause : new TextGenerationError({
|
|
79832
80375
|
operation,
|
|
79833
80376
|
detail: "Grok ACP text generation failed.",
|
|
79834
80377
|
cause
|
|
@@ -79908,7 +80451,7 @@ const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(function* (gr
|
|
|
79908
80451
|
//#endregion
|
|
79909
80452
|
//#region src/provider/Layers/GrokAdapter.ts
|
|
79910
80453
|
const encodeUnknownJsonStringExit = Schema$1.encodeUnknownExit(Schema$1.UnknownFromJsonString);
|
|
79911
|
-
const PROVIDER$
|
|
80454
|
+
const PROVIDER$2 = ProviderDriverKind.make("grok");
|
|
79912
80455
|
const GROK_RESUME_VERSION = 1;
|
|
79913
80456
|
function encodeJsonStringForDiagnostics(input) {
|
|
79914
80457
|
const result = encodeUnknownJsonStringExit(input);
|
|
@@ -79935,7 +80478,7 @@ function appendPromptResultToTurn(ctx, turnId, promptParts, result) {
|
|
|
79935
80478
|
}]
|
|
79936
80479
|
}];
|
|
79937
80480
|
}
|
|
79938
|
-
function isRecord(value) {
|
|
80481
|
+
function isRecord$2(value) {
|
|
79939
80482
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
79940
80483
|
}
|
|
79941
80484
|
const resolveNotificationTurnId = (ctx) => ctx.activeTurnId;
|
|
@@ -79945,7 +80488,7 @@ const resolveSessionCallbackTurnId = (sessions, threadId) => {
|
|
|
79945
80488
|
return ctx ? resolveCallbackTurnId(ctx) : void 0;
|
|
79946
80489
|
};
|
|
79947
80490
|
function parseGrokResume(raw) {
|
|
79948
|
-
if (!isRecord(raw)) return void 0;
|
|
80491
|
+
if (!isRecord$2(raw)) return void 0;
|
|
79949
80492
|
if (raw.schemaVersion !== GROK_RESUME_VERSION) return void 0;
|
|
79950
80493
|
if (typeof raw.sessionId !== "string" || !raw.sessionId.trim()) return void 0;
|
|
79951
80494
|
return { sessionId: raw.sessionId.trim() };
|
|
@@ -79980,7 +80523,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
79980
80523
|
const runtimeEventPubSub = yield* PubSub.unbounded();
|
|
79981
80524
|
const nowIso = Effect.map(DateTime.now, DateTime.formatIso);
|
|
79982
80525
|
const randomUUIDv4 = crypto.randomUUIDv4.pipe(Effect.mapError((cause) => new ProviderAdapterRequestError({
|
|
79983
|
-
provider: PROVIDER$
|
|
80526
|
+
provider: PROVIDER$2,
|
|
79984
80527
|
method: "crypto/randomUUIDv4",
|
|
79985
80528
|
detail: "Failed to generate Grok runtime identifier.",
|
|
79986
80529
|
cause
|
|
@@ -80022,7 +80565,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80022
80565
|
if (options?.errorMessage !== void 0) yield* offerRuntimeEvent({
|
|
80023
80566
|
type: "turn.completed",
|
|
80024
80567
|
...yield* makeEventStamp(),
|
|
80025
|
-
provider: PROVIDER$
|
|
80568
|
+
provider: PROVIDER$2,
|
|
80026
80569
|
threadId,
|
|
80027
80570
|
turnId,
|
|
80028
80571
|
payload: {
|
|
@@ -80033,7 +80576,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80033
80576
|
else if (options?.completedStopReason !== void 0) yield* offerRuntimeEvent({
|
|
80034
80577
|
type: "turn.completed",
|
|
80035
80578
|
...yield* makeEventStamp(),
|
|
80036
|
-
provider: PROVIDER$
|
|
80579
|
+
provider: PROVIDER$2,
|
|
80037
80580
|
threadId,
|
|
80038
80581
|
turnId,
|
|
80039
80582
|
payload: {
|
|
@@ -80087,7 +80630,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80087
80630
|
if (shouldEmitFailedTurn) yield* offerRuntimeEvent({
|
|
80088
80631
|
type: "turn.completed",
|
|
80089
80632
|
...yield* makeEventStamp(),
|
|
80090
|
-
provider: PROVIDER$
|
|
80633
|
+
provider: PROVIDER$2,
|
|
80091
80634
|
threadId,
|
|
80092
80635
|
turnId: settleTurnId,
|
|
80093
80636
|
payload: {
|
|
@@ -80098,7 +80641,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80098
80641
|
else if (shouldEmitCompletedTurn) yield* offerRuntimeEvent({
|
|
80099
80642
|
type: "turn.completed",
|
|
80100
80643
|
...yield* makeEventStamp(),
|
|
80101
|
-
provider: PROVIDER$
|
|
80644
|
+
provider: PROVIDER$2,
|
|
80102
80645
|
threadId,
|
|
80103
80646
|
turnId: settleTurnId,
|
|
80104
80647
|
payload: {
|
|
@@ -80115,7 +80658,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80115
80658
|
event: {
|
|
80116
80659
|
id: yield* randomUUIDv4,
|
|
80117
80660
|
kind: "notification",
|
|
80118
|
-
provider: PROVIDER$
|
|
80661
|
+
provider: PROVIDER$2,
|
|
80119
80662
|
createdAt: observedAt,
|
|
80120
80663
|
method,
|
|
80121
80664
|
threadId,
|
|
@@ -80133,7 +80676,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80133
80676
|
ctx.lastPlanFingerprint = fingerprint;
|
|
80134
80677
|
yield* offerRuntimeEvent(makeAcpPlanUpdatedEvent({
|
|
80135
80678
|
stamp,
|
|
80136
|
-
provider: PROVIDER$
|
|
80679
|
+
provider: PROVIDER$2,
|
|
80137
80680
|
threadId: ctx.threadId,
|
|
80138
80681
|
turnId,
|
|
80139
80682
|
payload,
|
|
@@ -80145,7 +80688,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80145
80688
|
const requireSession = (threadId) => {
|
|
80146
80689
|
const ctx = sessions.get(threadId);
|
|
80147
80690
|
if (!ctx || ctx.stopped) return Effect.fail(new ProviderAdapterSessionNotFoundError({
|
|
80148
|
-
provider: PROVIDER$
|
|
80691
|
+
provider: PROVIDER$2,
|
|
80149
80692
|
threadId
|
|
80150
80693
|
}));
|
|
80151
80694
|
return Effect.succeed(ctx);
|
|
@@ -80161,19 +80704,19 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80161
80704
|
yield* offerRuntimeEvent({
|
|
80162
80705
|
type: "session.exited",
|
|
80163
80706
|
...yield* makeEventStamp(),
|
|
80164
|
-
provider: PROVIDER$
|
|
80707
|
+
provider: PROVIDER$2,
|
|
80165
80708
|
threadId: ctx.threadId,
|
|
80166
80709
|
payload: { exitKind: "graceful" }
|
|
80167
80710
|
});
|
|
80168
80711
|
});
|
|
80169
80712
|
const startSession = (input) => withThreadLock(input.threadId, Effect.gen(function* () {
|
|
80170
|
-
if (input.provider !== void 0 && input.provider !== PROVIDER$
|
|
80171
|
-
provider: PROVIDER$
|
|
80713
|
+
if (input.provider !== void 0 && input.provider !== PROVIDER$2) return yield* new ProviderAdapterValidationError({
|
|
80714
|
+
provider: PROVIDER$2,
|
|
80172
80715
|
operation: "startSession",
|
|
80173
|
-
issue: `Expected provider '${PROVIDER$
|
|
80716
|
+
issue: `Expected provider '${PROVIDER$2}' but received '${input.provider}'.`
|
|
80174
80717
|
});
|
|
80175
80718
|
if (!input.cwd?.trim()) return yield* new ProviderAdapterValidationError({
|
|
80176
|
-
provider: PROVIDER$
|
|
80719
|
+
provider: PROVIDER$2,
|
|
80177
80720
|
operation: "startSession",
|
|
80178
80721
|
issue: "cwd is required and must be non-empty."
|
|
80179
80722
|
});
|
|
@@ -80189,7 +80732,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80189
80732
|
const resumeSessionId = parseGrokResume(input.resumeCursor)?.sessionId;
|
|
80190
80733
|
const acpNativeLoggers = makeAcpNativeLoggers({
|
|
80191
80734
|
nativeEventLogger,
|
|
80192
|
-
provider: PROVIDER$
|
|
80735
|
+
provider: PROVIDER$2,
|
|
80193
80736
|
threadId: input.threadId
|
|
80194
80737
|
});
|
|
80195
80738
|
const mcpSession = readMcpProviderSession(input.threadId);
|
|
@@ -80214,7 +80757,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80214
80757
|
}] } : {},
|
|
80215
80758
|
...acpNativeLoggers
|
|
80216
80759
|
}).pipe(Effect.provideService(Crypto.Crypto, crypto), Effect.provideService(Scope.Scope, sessionScope), Effect.mapError((cause) => new ProviderAdapterProcessError({
|
|
80217
|
-
provider: PROVIDER$
|
|
80760
|
+
provider: PROVIDER$2,
|
|
80218
80761
|
threadId: input.threadId,
|
|
80219
80762
|
detail: cause.message,
|
|
80220
80763
|
cause
|
|
@@ -80230,7 +80773,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80230
80773
|
yield* offerRuntimeEvent({
|
|
80231
80774
|
type: "user-input.requested",
|
|
80232
80775
|
...yield* makeEventStamp(),
|
|
80233
|
-
provider: PROVIDER$
|
|
80776
|
+
provider: PROVIDER$2,
|
|
80234
80777
|
threadId: input.threadId,
|
|
80235
80778
|
turnId,
|
|
80236
80779
|
requestId: runtimeRequestId,
|
|
@@ -80247,7 +80790,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80247
80790
|
yield* offerRuntimeEvent({
|
|
80248
80791
|
type: "user-input.resolved",
|
|
80249
80792
|
...yield* makeEventStamp(),
|
|
80250
|
-
provider: PROVIDER$
|
|
80793
|
+
provider: PROVIDER$2,
|
|
80251
80794
|
threadId: input.threadId,
|
|
80252
80795
|
turnId,
|
|
80253
80796
|
requestId: runtimeRequestId,
|
|
@@ -80280,7 +80823,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80280
80823
|
pendingApprovals.set(requestId, { decision });
|
|
80281
80824
|
yield* offerRuntimeEvent(makeAcpRequestOpenedEvent({
|
|
80282
80825
|
stamp: yield* makeEventStamp(),
|
|
80283
|
-
provider: PROVIDER$
|
|
80826
|
+
provider: PROVIDER$2,
|
|
80284
80827
|
threadId: input.threadId,
|
|
80285
80828
|
turnId,
|
|
80286
80829
|
requestId: runtimeRequestId,
|
|
@@ -80295,7 +80838,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80295
80838
|
pendingApprovals.delete(requestId);
|
|
80296
80839
|
yield* offerRuntimeEvent(makeAcpRequestResolvedEvent({
|
|
80297
80840
|
stamp: yield* makeEventStamp(),
|
|
80298
|
-
provider: PROVIDER$
|
|
80841
|
+
provider: PROVIDER$2,
|
|
80299
80842
|
threadId: input.threadId,
|
|
80300
80843
|
turnId,
|
|
80301
80844
|
requestId: runtimeRequestId,
|
|
@@ -80309,17 +80852,17 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80309
80852
|
} : { outcome: "cancelled" } };
|
|
80310
80853
|
})));
|
|
80311
80854
|
return yield* acp.start();
|
|
80312
|
-
}).pipe(Effect.mapError((error) => mapAcpToAdapterError(PROVIDER$
|
|
80855
|
+
}).pipe(Effect.mapError((error) => mapAcpToAdapterError(PROVIDER$2, input.threadId, "session/start", error)));
|
|
80313
80856
|
const requestedStartModelId = grokModelSelection?.model ? resolveGrokAcpBaseModelId(grokModelSelection.model) : void 0;
|
|
80314
80857
|
const boundModelId = yield* applyGrokAcpModelSelection({
|
|
80315
80858
|
runtime: acp,
|
|
80316
80859
|
currentModelId: currentGrokModelIdFromSessionSetup(started.sessionSetupResult),
|
|
80317
80860
|
requestedModelId: requestedStartModelId,
|
|
80318
|
-
mapError: (cause) => mapAcpToAdapterError(PROVIDER$
|
|
80861
|
+
mapError: (cause) => mapAcpToAdapterError(PROVIDER$2, input.threadId, "session/set_model", cause)
|
|
80319
80862
|
});
|
|
80320
80863
|
const now = yield* nowIso;
|
|
80321
80864
|
const session = {
|
|
80322
|
-
provider: PROVIDER$
|
|
80865
|
+
provider: PROVIDER$2,
|
|
80323
80866
|
providerInstanceId: boundInstanceId,
|
|
80324
80867
|
status: "ready",
|
|
80325
80868
|
runtimeMode: input.runtimeMode,
|
|
@@ -80364,7 +80907,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80364
80907
|
case "AssistantItemStarted":
|
|
80365
80908
|
yield* offerRuntimeEvent(makeAcpAssistantItemEvent({
|
|
80366
80909
|
stamp,
|
|
80367
|
-
provider: PROVIDER$
|
|
80910
|
+
provider: PROVIDER$2,
|
|
80368
80911
|
threadId: ctx.threadId,
|
|
80369
80912
|
turnId: notificationTurnId,
|
|
80370
80913
|
itemId: event.itemId,
|
|
@@ -80374,7 +80917,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80374
80917
|
case "AssistantItemCompleted":
|
|
80375
80918
|
yield* offerRuntimeEvent(makeAcpAssistantItemEvent({
|
|
80376
80919
|
stamp,
|
|
80377
|
-
provider: PROVIDER$
|
|
80920
|
+
provider: PROVIDER$2,
|
|
80378
80921
|
threadId: ctx.threadId,
|
|
80379
80922
|
turnId: notificationTurnId,
|
|
80380
80923
|
itemId: event.itemId,
|
|
@@ -80387,7 +80930,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80387
80930
|
case "ToolCallUpdated":
|
|
80388
80931
|
yield* offerRuntimeEvent(makeAcpToolCallEvent({
|
|
80389
80932
|
stamp,
|
|
80390
|
-
provider: PROVIDER$
|
|
80933
|
+
provider: PROVIDER$2,
|
|
80391
80934
|
threadId: ctx.threadId,
|
|
80392
80935
|
turnId: notificationTurnId,
|
|
80393
80936
|
toolCall: event.toolCall,
|
|
@@ -80397,7 +80940,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80397
80940
|
case "ContentDelta":
|
|
80398
80941
|
yield* offerRuntimeEvent(makeAcpContentDeltaEvent({
|
|
80399
80942
|
stamp,
|
|
80400
|
-
provider: PROVIDER$
|
|
80943
|
+
provider: PROVIDER$2,
|
|
80401
80944
|
threadId: ctx.threadId,
|
|
80402
80945
|
turnId: notificationTurnId,
|
|
80403
80946
|
...event.itemId ? { itemId: event.itemId } : {},
|
|
@@ -80412,14 +80955,14 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80412
80955
|
yield* offerRuntimeEvent({
|
|
80413
80956
|
type: "session.started",
|
|
80414
80957
|
...yield* makeEventStamp(),
|
|
80415
|
-
provider: PROVIDER$
|
|
80958
|
+
provider: PROVIDER$2,
|
|
80416
80959
|
threadId: input.threadId,
|
|
80417
80960
|
payload: { resume: started.initializeResult }
|
|
80418
80961
|
});
|
|
80419
80962
|
yield* offerRuntimeEvent({
|
|
80420
80963
|
type: "session.state.changed",
|
|
80421
80964
|
...yield* makeEventStamp(),
|
|
80422
|
-
provider: PROVIDER$
|
|
80965
|
+
provider: PROVIDER$2,
|
|
80423
80966
|
threadId: input.threadId,
|
|
80424
80967
|
payload: {
|
|
80425
80968
|
state: "ready",
|
|
@@ -80429,7 +80972,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80429
80972
|
yield* offerRuntimeEvent({
|
|
80430
80973
|
type: "thread.started",
|
|
80431
80974
|
...yield* makeEventStamp(),
|
|
80432
|
-
provider: PROVIDER$
|
|
80975
|
+
provider: PROVIDER$2,
|
|
80433
80976
|
threadId: input.threadId,
|
|
80434
80977
|
payload: { providerThreadId: started.sessionId }
|
|
80435
80978
|
});
|
|
@@ -80455,7 +80998,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80455
80998
|
runtime: ctx.acp,
|
|
80456
80999
|
currentModelId: ctx.currentModelId,
|
|
80457
81000
|
requestedModelId: requestedTurnModelId,
|
|
80458
|
-
mapError: (cause) => mapAcpToAdapterError(PROVIDER$
|
|
81001
|
+
mapError: (cause) => mapAcpToAdapterError(PROVIDER$2, input.threadId, "session/set_model", cause)
|
|
80459
81002
|
});
|
|
80460
81003
|
const text = input.input?.trim();
|
|
80461
81004
|
const imagePromptParts = yield* Effect.forEach(input.attachments ?? [], (attachment) => Effect.gen(function* () {
|
|
@@ -80464,12 +81007,12 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80464
81007
|
attachment
|
|
80465
81008
|
});
|
|
80466
81009
|
if (!attachmentPath) return yield* new ProviderAdapterRequestError({
|
|
80467
|
-
provider: PROVIDER$
|
|
81010
|
+
provider: PROVIDER$2,
|
|
80468
81011
|
method: "session/prompt",
|
|
80469
81012
|
detail: `Invalid attachment id '${attachment.id}'.`
|
|
80470
81013
|
});
|
|
80471
81014
|
const bytes = yield* fileSystem.readFile(attachmentPath).pipe(Effect.mapError((cause) => new ProviderAdapterRequestError({
|
|
80472
|
-
provider: PROVIDER$
|
|
81015
|
+
provider: PROVIDER$2,
|
|
80473
81016
|
method: "session/prompt",
|
|
80474
81017
|
detail: cause.message,
|
|
80475
81018
|
cause
|
|
@@ -80485,7 +81028,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80485
81028
|
text
|
|
80486
81029
|
}] : [], ...imagePromptParts];
|
|
80487
81030
|
if (promptParts.length === 0) return yield* new ProviderAdapterValidationError({
|
|
80488
|
-
provider: PROVIDER$
|
|
81031
|
+
provider: PROVIDER$2,
|
|
80489
81032
|
operation: "sendTurn",
|
|
80490
81033
|
issue: "Turn requires non-empty text or attachments."
|
|
80491
81034
|
});
|
|
@@ -80499,7 +81042,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80499
81042
|
settleAllPrompts: true
|
|
80500
81043
|
});
|
|
80501
81044
|
return yield* new ProviderAdapterRequestError({
|
|
80502
|
-
provider: PROVIDER$
|
|
81045
|
+
provider: PROVIDER$2,
|
|
80503
81046
|
method: "session/prompt",
|
|
80504
81047
|
detail: "Grok prompt was interrupted during preparation."
|
|
80505
81048
|
});
|
|
@@ -80515,7 +81058,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80515
81058
|
if (steeringTurnId === void 0) yield* offerRuntimeEvent({
|
|
80516
81059
|
type: "turn.started",
|
|
80517
81060
|
...yield* makeEventStamp(),
|
|
80518
|
-
provider: PROVIDER$
|
|
81061
|
+
provider: PROVIDER$2,
|
|
80519
81062
|
threadId: input.threadId,
|
|
80520
81063
|
turnId,
|
|
80521
81064
|
payload: displayModel ? { model: displayModel } : {}
|
|
@@ -80541,7 +81084,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80541
81084
|
const promptResultRef = yield* Ref.make(void 0);
|
|
80542
81085
|
const promptFailureMessageRef = yield* Ref.make(void 0);
|
|
80543
81086
|
return yield* Effect.gen(function* () {
|
|
80544
|
-
const result = yield* prepared.acp.prompt({ prompt: prepared.promptParts }).pipe(Effect.tap((promptResult) => Effect.all([Ref.set(promptRpcSucceeded, true), Ref.set(promptResultRef, promptResult)])), Effect.tapError((error) => Ref.set(promptFailureMessageRef, mapAcpToAdapterError(PROVIDER$
|
|
81087
|
+
const result = yield* prepared.acp.prompt({ prompt: prepared.promptParts }).pipe(Effect.tap((promptResult) => Effect.all([Ref.set(promptRpcSucceeded, true), Ref.set(promptResultRef, promptResult)])), Effect.tapError((error) => Ref.set(promptFailureMessageRef, mapAcpToAdapterError(PROVIDER$2, input.threadId, "session/prompt", error).message).pipe(Effect.andThen(prepared.acp.drainEvents))), Effect.mapError((error) => mapAcpToAdapterError(PROVIDER$2, input.threadId, "session/prompt", error)));
|
|
80545
81088
|
return yield* withThreadLock(input.threadId, Effect.gen(function* () {
|
|
80546
81089
|
const ctx = yield* requireSession(input.threadId);
|
|
80547
81090
|
if (ctx.acpSessionId !== prepared.acpSessionId) {
|
|
@@ -80551,7 +81094,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80551
81094
|
});
|
|
80552
81095
|
yield* Ref.set(promptSettled, true);
|
|
80553
81096
|
return yield* new ProviderAdapterRequestError({
|
|
80554
|
-
provider: PROVIDER$
|
|
81097
|
+
provider: PROVIDER$2,
|
|
80555
81098
|
method: "session/prompt",
|
|
80556
81099
|
detail: "Grok session changed before the turn completed."
|
|
80557
81100
|
});
|
|
@@ -80606,7 +81149,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80606
81149
|
yield* offerRuntimeEvent({
|
|
80607
81150
|
type: "turn.completed",
|
|
80608
81151
|
...yield* makeEventStamp(),
|
|
80609
|
-
provider: PROVIDER$
|
|
81152
|
+
provider: PROVIDER$2,
|
|
80610
81153
|
threadId: input.threadId,
|
|
80611
81154
|
turnId: prepared.turnId,
|
|
80612
81155
|
payload: {
|
|
@@ -80676,7 +81219,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80676
81219
|
const interruptedTurnId = observed.interruptedTurnId ?? turnId ?? activeTurnId ?? ctx.session.activeTurnId;
|
|
80677
81220
|
yield* settlePendingApprovalsAsCancelled(ctx.pendingApprovals);
|
|
80678
81221
|
yield* settlePendingUserInputsAsCancelled(ctx.pendingUserInputs);
|
|
80679
|
-
yield* Effect.ignore(ctx.acp.cancel.pipe(Effect.mapError((error) => mapAcpToAdapterError(PROVIDER$
|
|
81222
|
+
yield* Effect.ignore(ctx.acp.cancel.pipe(Effect.mapError((error) => mapAcpToAdapterError(PROVIDER$2, threadId, "session/cancel", error))));
|
|
80680
81223
|
if (interruptedTurnId) {
|
|
80681
81224
|
ctx.interruptedTurnIds.add(interruptedTurnId);
|
|
80682
81225
|
yield* settlePromptInFlight(threadId, interruptedTurnId, ctx.acpSessionId, {
|
|
@@ -80699,7 +81242,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80699
81242
|
const respondToRequest = (threadId, requestId, decision) => Effect.gen(function* () {
|
|
80700
81243
|
const pending = (yield* requireSession(threadId)).pendingApprovals.get(requestId);
|
|
80701
81244
|
if (!pending) return yield* new ProviderAdapterRequestError({
|
|
80702
|
-
provider: PROVIDER$
|
|
81245
|
+
provider: PROVIDER$2,
|
|
80703
81246
|
method: "session/request_permission",
|
|
80704
81247
|
detail: `Unknown pending approval request: ${requestId}`
|
|
80705
81248
|
});
|
|
@@ -80708,7 +81251,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80708
81251
|
const respondToUserInput = (threadId, requestId, answers) => Effect.gen(function* () {
|
|
80709
81252
|
const pending = (yield* requireSession(threadId)).pendingUserInputs.get(requestId);
|
|
80710
81253
|
if (!pending) return yield* new ProviderAdapterRequestError({
|
|
80711
|
-
provider: PROVIDER$
|
|
81254
|
+
provider: PROVIDER$2,
|
|
80712
81255
|
method: "_x.ai/ask_user_question",
|
|
80713
81256
|
detail: `Unknown pending user-input request: ${requestId}`
|
|
80714
81257
|
});
|
|
@@ -80726,12 +81269,12 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80726
81269
|
const rollbackThread = (threadId, numTurns) => Effect.gen(function* () {
|
|
80727
81270
|
yield* requireSession(threadId);
|
|
80728
81271
|
if (!Number.isInteger(numTurns) || numTurns < 1) return yield* new ProviderAdapterValidationError({
|
|
80729
|
-
provider: PROVIDER$
|
|
81272
|
+
provider: PROVIDER$2,
|
|
80730
81273
|
operation: "rollbackThread",
|
|
80731
81274
|
issue: "numTurns must be an integer >= 1."
|
|
80732
81275
|
});
|
|
80733
81276
|
return yield* new ProviderAdapterRequestError({
|
|
80734
|
-
provider: PROVIDER$
|
|
81277
|
+
provider: PROVIDER$2,
|
|
80735
81278
|
method: "thread/rollback",
|
|
80736
81279
|
detail: "Grok ACP sessions do not support provider-side rollback yet."
|
|
80737
81280
|
});
|
|
@@ -80749,7 +81292,7 @@ function makeGrokAdapter(grokSettings, options) {
|
|
|
80749
81292
|
yield* Effect.addFinalizer(() => Effect.ignore(stopAll()).pipe(Effect.tap(() => PubSub.shutdown(runtimeEventPubSub)), Effect.tap(() => managedNativeEventLogger?.close() ?? Effect.void)));
|
|
80750
81293
|
const streamEvents = Stream.fromPubSub(runtimeEventPubSub);
|
|
80751
81294
|
return {
|
|
80752
|
-
provider: PROVIDER$
|
|
81295
|
+
provider: PROVIDER$2,
|
|
80753
81296
|
capabilities: { sessionModelSwitch: "in-session" },
|
|
80754
81297
|
startSession,
|
|
80755
81298
|
sendTurn,
|
|
@@ -80774,14 +81317,14 @@ const GROK_PRESENTATION = {
|
|
|
80774
81317
|
showInteractionModeToggle: false,
|
|
80775
81318
|
requiresNewThreadForModelChange: true
|
|
80776
81319
|
};
|
|
80777
|
-
const EMPTY_CAPABILITIES = createModelCapabilities({ optionDescriptors: [] });
|
|
80778
|
-
const VERSION_PROBE_TIMEOUT_MS = 4e3;
|
|
81320
|
+
const EMPTY_CAPABILITIES$1 = createModelCapabilities({ optionDescriptors: [] });
|
|
81321
|
+
const VERSION_PROBE_TIMEOUT_MS$1 = 4e3;
|
|
80779
81322
|
const GROK_ACP_MODEL_DISCOVERY_TIMEOUT_MS = 15e3;
|
|
80780
81323
|
const GROK_BUILT_IN_MODELS = [{
|
|
80781
81324
|
slug: "grok-build",
|
|
80782
81325
|
name: "Grok Build",
|
|
80783
81326
|
isCustom: false,
|
|
80784
|
-
capabilities: EMPTY_CAPABILITIES
|
|
81327
|
+
capabilities: EMPTY_CAPABILITIES$1
|
|
80785
81328
|
}];
|
|
80786
81329
|
function buildInitialGrokProviderSnapshot(grokSettings) {
|
|
80787
81330
|
return Effect.gen(function* () {
|
|
@@ -80816,7 +81359,7 @@ function buildInitialGrokProviderSnapshot(grokSettings) {
|
|
|
80816
81359
|
});
|
|
80817
81360
|
}
|
|
80818
81361
|
function grokModelsFromSettings(customModels, builtInModels = GROK_BUILT_IN_MODELS) {
|
|
80819
|
-
return providerModelsFromSettings(builtInModels, customModels ?? [], EMPTY_CAPABILITIES);
|
|
81362
|
+
return providerModelsFromSettings(builtInModels, customModels ?? [], EMPTY_CAPABILITIES$1);
|
|
80820
81363
|
}
|
|
80821
81364
|
function buildGrokDiscoveredModelsFromSessionModelState(modelState) {
|
|
80822
81365
|
if (!modelState || modelState.availableModels.length === 0) return [];
|
|
@@ -80829,7 +81372,7 @@ function buildGrokDiscoveredModelsFromSessionModelState(modelState) {
|
|
|
80829
81372
|
slug,
|
|
80830
81373
|
name: model.name.trim() || slug,
|
|
80831
81374
|
isCustom: false,
|
|
80832
|
-
capabilities: EMPTY_CAPABILITIES
|
|
81375
|
+
capabilities: EMPTY_CAPABILITIES$1
|
|
80833
81376
|
};
|
|
80834
81377
|
}).filter((model) => model !== void 0);
|
|
80835
81378
|
}
|
|
@@ -80869,7 +81412,7 @@ const checkGrokProviderStatus = Effect.fn("checkGrokProviderStatus")(function* (
|
|
|
80869
81412
|
message: "Grok is disabled in P4Code settings."
|
|
80870
81413
|
}
|
|
80871
81414
|
});
|
|
80872
|
-
const versionResult = yield* runGrokVersionCommand(grokSettings, environment).pipe(Effect.timeoutOption(VERSION_PROBE_TIMEOUT_MS), Effect.result);
|
|
81415
|
+
const versionResult = yield* runGrokVersionCommand(grokSettings, environment).pipe(Effect.timeoutOption(VERSION_PROBE_TIMEOUT_MS$1), Effect.result);
|
|
80873
81416
|
if (Result.isFailure(versionResult)) {
|
|
80874
81417
|
const error = versionResult.failure;
|
|
80875
81418
|
const missing = isCommandMissingCause(error);
|
|
@@ -80978,22 +81521,22 @@ const enrichGrokSnapshot = (input) => {
|
|
|
80978
81521
|
//#endregion
|
|
80979
81522
|
//#region src/provider/Drivers/GrokDriver.ts
|
|
80980
81523
|
const decodeGrokSettings = Schema$1.decodeSync(GrokSettings);
|
|
80981
|
-
const DRIVER_KIND$
|
|
80982
|
-
const SNAPSHOT_REFRESH_INTERVAL$
|
|
80983
|
-
const UPDATE$
|
|
80984
|
-
provider: DRIVER_KIND$
|
|
81524
|
+
const DRIVER_KIND$2 = ProviderDriverKind.make("grok");
|
|
81525
|
+
const SNAPSHOT_REFRESH_INTERVAL$2 = Duration.minutes(5);
|
|
81526
|
+
const UPDATE$2 = makeStaticProviderMaintenanceResolver(makeManualOnlyProviderMaintenanceCapabilities({
|
|
81527
|
+
provider: DRIVER_KIND$2,
|
|
80985
81528
|
packageName: null
|
|
80986
81529
|
}));
|
|
80987
|
-
const withInstanceIdentity$
|
|
81530
|
+
const withInstanceIdentity$2 = (input) => (snapshot) => ({
|
|
80988
81531
|
...snapshot,
|
|
80989
81532
|
instanceId: input.instanceId,
|
|
80990
|
-
driver: DRIVER_KIND$
|
|
81533
|
+
driver: DRIVER_KIND$2,
|
|
80991
81534
|
...input.displayName ? { displayName: input.displayName } : {},
|
|
80992
81535
|
...input.accentColor ? { accentColor: input.accentColor } : {},
|
|
80993
81536
|
continuation: { groupKey: input.continuationGroupKey }
|
|
80994
81537
|
});
|
|
80995
81538
|
const GrokDriver = {
|
|
80996
|
-
driverKind: DRIVER_KIND$
|
|
81539
|
+
driverKind: DRIVER_KIND$2,
|
|
80997
81540
|
metadata: {
|
|
80998
81541
|
displayName: "Grok",
|
|
80999
81542
|
supportsMultipleInstances: true
|
|
@@ -81008,10 +81551,10 @@ const GrokDriver = {
|
|
|
81008
81551
|
const eventLoggers = yield* ProviderEventLoggers;
|
|
81009
81552
|
const processEnv = mergeProviderInstanceEnvironment(environment);
|
|
81010
81553
|
const continuationIdentity = defaultProviderContinuationIdentity({
|
|
81011
|
-
driverKind: DRIVER_KIND$
|
|
81554
|
+
driverKind: DRIVER_KIND$2,
|
|
81012
81555
|
instanceId
|
|
81013
81556
|
});
|
|
81014
|
-
const stampIdentity = withInstanceIdentity$
|
|
81557
|
+
const stampIdentity = withInstanceIdentity$2({
|
|
81015
81558
|
instanceId,
|
|
81016
81559
|
displayName,
|
|
81017
81560
|
accentColor,
|
|
@@ -81021,7 +81564,7 @@ const GrokDriver = {
|
|
|
81021
81564
|
...config,
|
|
81022
81565
|
enabled
|
|
81023
81566
|
};
|
|
81024
|
-
const maintenanceCapabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(UPDATE$
|
|
81567
|
+
const maintenanceCapabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(UPDATE$2, {
|
|
81025
81568
|
binaryPath: effectiveConfig.binaryPath,
|
|
81026
81569
|
env: processEnv
|
|
81027
81570
|
});
|
|
@@ -81047,11 +81590,1467 @@ const GrokDriver = {
|
|
|
81047
81590
|
publishSnapshot,
|
|
81048
81591
|
httpClient
|
|
81049
81592
|
}),
|
|
81593
|
+
refreshInterval: SNAPSHOT_REFRESH_INTERVAL$2
|
|
81594
|
+
}).pipe(Effect.mapError((cause) => new ProviderDriverError({
|
|
81595
|
+
driver: DRIVER_KIND$2,
|
|
81596
|
+
instanceId,
|
|
81597
|
+
detail: `Failed to build Grok snapshot: ${cause.message ?? String(cause)}`,
|
|
81598
|
+
cause
|
|
81599
|
+
})));
|
|
81600
|
+
return {
|
|
81601
|
+
instanceId,
|
|
81602
|
+
driverKind: DRIVER_KIND$2,
|
|
81603
|
+
continuationIdentity,
|
|
81604
|
+
displayName,
|
|
81605
|
+
accentColor,
|
|
81606
|
+
enabled,
|
|
81607
|
+
snapshot,
|
|
81608
|
+
adapter,
|
|
81609
|
+
textGeneration
|
|
81610
|
+
};
|
|
81611
|
+
})
|
|
81612
|
+
};
|
|
81613
|
+
/**
|
|
81614
|
+
* Build the argument vector for one turn.
|
|
81615
|
+
*
|
|
81616
|
+
* Approval policy is the load-bearing decision here. A headless Muse run has
|
|
81617
|
+
* no channel to answer an approval prompt — the CLI only offers
|
|
81618
|
+
* `--disable-approval` (keep the OS sandbox) and `--yolo` (drop approval and
|
|
81619
|
+
* the sandbox, and trust the workspace). P4Code owns the user-facing runtime
|
|
81620
|
+
* mode, so it maps onto those two postures:
|
|
81621
|
+
*
|
|
81622
|
+
* - `full-access` → `--yolo`
|
|
81623
|
+
* - everything else → `--disable-approval --trust-workspace`, leaving the
|
|
81624
|
+
* sandbox to contain what runs.
|
|
81625
|
+
*
|
|
81626
|
+
* `approval-required` cannot be honoured; the adapter emits a
|
|
81627
|
+
* `config.warning` for it rather than silently pretending otherwise.
|
|
81628
|
+
*/
|
|
81629
|
+
function buildMuseExecArgs(input) {
|
|
81630
|
+
const args = [
|
|
81631
|
+
"exec",
|
|
81632
|
+
"--json",
|
|
81633
|
+
"--session-id",
|
|
81634
|
+
input.sessionId
|
|
81635
|
+
];
|
|
81636
|
+
if (input.model) args.push("--model", input.model);
|
|
81637
|
+
if (input.reasoningEffort) args.push("--reasoning-effort", input.reasoningEffort);
|
|
81638
|
+
if (input.baseUrl) args.push("--base-url", input.baseUrl);
|
|
81639
|
+
if (input.runtimeMode === "full-access") args.push("--yolo");
|
|
81640
|
+
else {
|
|
81641
|
+
args.push("--disable-approval", "--trust-workspace");
|
|
81642
|
+
switch (input.sandboxMode) {
|
|
81643
|
+
case "danger-full-access":
|
|
81644
|
+
args.push("--disable-sandbox");
|
|
81645
|
+
break;
|
|
81646
|
+
case "read-only":
|
|
81647
|
+
args.push("--disable-write", "--disable-shell");
|
|
81648
|
+
break;
|
|
81649
|
+
default: break;
|
|
81650
|
+
}
|
|
81651
|
+
}
|
|
81652
|
+
for (const imagePath of input.imagePaths ?? []) args.push("--image", imagePath);
|
|
81653
|
+
args.push(input.prompt);
|
|
81654
|
+
return args;
|
|
81655
|
+
}
|
|
81656
|
+
/** `muse --version` argv, used by the provider status probe. */
|
|
81657
|
+
const MUSE_VERSION_ARGS = ["--version"];
|
|
81658
|
+
/**
|
|
81659
|
+
* Whether a runtime mode asks for interactive approvals that Muse Code
|
|
81660
|
+
* cannot deliver headlessly.
|
|
81661
|
+
*/
|
|
81662
|
+
function museApprovalsUnsupported(runtimeMode) {
|
|
81663
|
+
return runtimeMode === "approval-required";
|
|
81664
|
+
}
|
|
81665
|
+
//#endregion
|
|
81666
|
+
//#region src/provider/muse/MuseExecEvents.ts
|
|
81667
|
+
/**
|
|
81668
|
+
* MuseExecEvents — pure translation of `muse exec --json` JSONL records into
|
|
81669
|
+
* canonical `ProviderRuntimeEvent`s.
|
|
81670
|
+
*
|
|
81671
|
+
* Muse Code has no ACP or app-server surface (verified against CLI
|
|
81672
|
+
* 0.1.0-R708.1): the only scriptable path is `muse exec --json`, which runs
|
|
81673
|
+
* one prompt to completion and prints one JSON object per line. Multi-turn
|
|
81674
|
+
* threads are rebuilt by re-invoking with `--session-id`.
|
|
81675
|
+
*
|
|
81676
|
+
* Everything here is a pure function over a caller-owned state record so the
|
|
81677
|
+
* mapping can be tested against captured CLI output without spawning a
|
|
81678
|
+
* process. Process ownership lives in `MuseExecRuntime`.
|
|
81679
|
+
*
|
|
81680
|
+
* @module provider/muse/MuseExecEvents
|
|
81681
|
+
*/
|
|
81682
|
+
/** Longest tool result text carried on an `item.completed` detail. */
|
|
81683
|
+
const MAX_TOOL_DETAIL_CHARS = 2e3;
|
|
81684
|
+
function makeMuseExecTurnState() {
|
|
81685
|
+
return {
|
|
81686
|
+
modelId: void 0,
|
|
81687
|
+
assistantItemStarted: false,
|
|
81688
|
+
assistantText: "",
|
|
81689
|
+
toolNameByTaskId: /* @__PURE__ */ new Map(),
|
|
81690
|
+
taskIdByCallId: /* @__PURE__ */ new Map(),
|
|
81691
|
+
settledToolTaskIds: /* @__PURE__ */ new Set(),
|
|
81692
|
+
terminal: void 0
|
|
81693
|
+
};
|
|
81694
|
+
}
|
|
81695
|
+
/**
|
|
81696
|
+
* Decode one line of `muse exec --json`. Returns `undefined` for blank lines
|
|
81697
|
+
* and for anything that is not a JSON object with a string `payload_type`, so
|
|
81698
|
+
* a stray line can never abort a turn.
|
|
81699
|
+
*/
|
|
81700
|
+
function parseMuseExecLine(line) {
|
|
81701
|
+
const trimmed = line.trim();
|
|
81702
|
+
if (trimmed.length === 0) return void 0;
|
|
81703
|
+
let decoded;
|
|
81704
|
+
try {
|
|
81705
|
+
decoded = JSON.parse(trimmed);
|
|
81706
|
+
} catch {
|
|
81707
|
+
return;
|
|
81708
|
+
}
|
|
81709
|
+
if (!isRecord$1(decoded)) return void 0;
|
|
81710
|
+
const payloadType = decoded["payload_type"];
|
|
81711
|
+
if (typeof payloadType !== "string") return void 0;
|
|
81712
|
+
const payload = isRecord$1(decoded["payload"]) ? decoded["payload"] : {};
|
|
81713
|
+
const stream = isRecord$1(decoded["stream"]) ? decoded["stream"] : void 0;
|
|
81714
|
+
const streamId = stream?.["id"];
|
|
81715
|
+
const sequence = decoded["sequence"];
|
|
81716
|
+
return {
|
|
81717
|
+
payloadType,
|
|
81718
|
+
payload,
|
|
81719
|
+
sequence: typeof sequence === "number" ? sequence : void 0,
|
|
81720
|
+
sessionId: stream?.["kind"] === "session" && typeof streamId === "string" ? streamId : void 0
|
|
81721
|
+
};
|
|
81722
|
+
}
|
|
81723
|
+
/**
|
|
81724
|
+
* Map one CLI record onto zero or more event drafts, mutating `state`.
|
|
81725
|
+
*
|
|
81726
|
+
* The CLI stream is deliberately lossy compared with the persisted session
|
|
81727
|
+
* log: tool call *arguments* and token usage never appear on stdout. Those
|
|
81728
|
+
* are recovered separately from `session.jsonl` (see `MuseSessionLog`).
|
|
81729
|
+
*/
|
|
81730
|
+
function mapMuseExecRecord(input) {
|
|
81731
|
+
const { record, state, context } = input;
|
|
81732
|
+
const payload = record.payload;
|
|
81733
|
+
switch (record.payloadType) {
|
|
81734
|
+
case "run.model.configured": {
|
|
81735
|
+
const modelId = stringField(payload, "model_id");
|
|
81736
|
+
if (modelId) state.modelId = modelId;
|
|
81737
|
+
return [(stamp) => ({
|
|
81738
|
+
type: "session.configured",
|
|
81739
|
+
...stamp,
|
|
81740
|
+
...base(context),
|
|
81741
|
+
payload: { config: payload },
|
|
81742
|
+
raw: rawFrom(record)
|
|
81743
|
+
})];
|
|
81744
|
+
}
|
|
81745
|
+
case "run.lifecycle.started": return [(stamp) => ({
|
|
81746
|
+
type: "turn.started",
|
|
81747
|
+
...stamp,
|
|
81748
|
+
...base(context),
|
|
81749
|
+
payload: state.modelId ? { model: state.modelId } : {},
|
|
81750
|
+
raw: rawFrom(record)
|
|
81751
|
+
})];
|
|
81752
|
+
case "run.output.delta": {
|
|
81753
|
+
const text = stringField(payload, "text");
|
|
81754
|
+
if (text === void 0 || text.length === 0) return [];
|
|
81755
|
+
return appendAssistantText({
|
|
81756
|
+
text,
|
|
81757
|
+
state,
|
|
81758
|
+
context,
|
|
81759
|
+
record
|
|
81760
|
+
});
|
|
81761
|
+
}
|
|
81762
|
+
case "task.lifecycle.proposed": {
|
|
81763
|
+
const event = eventField(payload);
|
|
81764
|
+
const taskId = stringField(event, "task_id");
|
|
81765
|
+
const toolName = toolNameFromTaskKind(stringField(event, "task_kind"));
|
|
81766
|
+
if (!taskId || !toolName) return [];
|
|
81767
|
+
state.toolNameByTaskId.set(taskId, toolName);
|
|
81768
|
+
return [(stamp) => ({
|
|
81769
|
+
type: "item.started",
|
|
81770
|
+
...stamp,
|
|
81771
|
+
...base(context),
|
|
81772
|
+
itemId: RuntimeItemId.make(taskId),
|
|
81773
|
+
payload: {
|
|
81774
|
+
itemType: itemTypeForToolName(toolName),
|
|
81775
|
+
status: "inProgress",
|
|
81776
|
+
title: toolName
|
|
81777
|
+
},
|
|
81778
|
+
raw: rawFrom(record)
|
|
81779
|
+
})];
|
|
81780
|
+
}
|
|
81781
|
+
case "task.lifecycle.side_effect_intent": {
|
|
81782
|
+
const event = eventField(payload);
|
|
81783
|
+
const taskId = stringField(event, "task_id");
|
|
81784
|
+
const callId = toolCallIdFromIdempotencyKey(stringField(event, "idempotency_key"));
|
|
81785
|
+
if (taskId && callId) state.taskIdByCallId.set(callId, taskId);
|
|
81786
|
+
return [];
|
|
81787
|
+
}
|
|
81788
|
+
case "task.lifecycle.output": {
|
|
81789
|
+
const event = eventField(payload);
|
|
81790
|
+
const taskId = stringField(event, "task_id");
|
|
81791
|
+
const chunk = stringField(event, "chunk");
|
|
81792
|
+
if (!taskId || !chunk) return [];
|
|
81793
|
+
const toolName = state.toolNameByTaskId.get(taskId);
|
|
81794
|
+
if (!toolName) return [];
|
|
81795
|
+
const streamKind = itemTypeForToolName(toolName) === "command_execution" ? "command_output" : "file_change_output";
|
|
81796
|
+
return [(stamp) => ({
|
|
81797
|
+
type: "content.delta",
|
|
81798
|
+
...stamp,
|
|
81799
|
+
...base(context),
|
|
81800
|
+
itemId: RuntimeItemId.make(taskId),
|
|
81801
|
+
payload: {
|
|
81802
|
+
streamKind,
|
|
81803
|
+
delta: chunk
|
|
81804
|
+
},
|
|
81805
|
+
raw: rawFrom(record)
|
|
81806
|
+
})];
|
|
81807
|
+
}
|
|
81808
|
+
case "tool.result": {
|
|
81809
|
+
const callId = stringField(payload, "call_id");
|
|
81810
|
+
const taskId = callId ? state.taskIdByCallId.get(callId) : void 0;
|
|
81811
|
+
if (!taskId) return [];
|
|
81812
|
+
const toolName = state.toolNameByTaskId.get(taskId);
|
|
81813
|
+
state.settledToolTaskIds.add(taskId);
|
|
81814
|
+
const failed = stringField(recordField(payload, "correlation_facts"), "outcome") === "error";
|
|
81815
|
+
const detail = truncate(stringField(payload, "text"), MAX_TOOL_DETAIL_CHARS);
|
|
81816
|
+
const editFacts = recordField(payload, "edit_facts");
|
|
81817
|
+
return [(stamp) => ({
|
|
81818
|
+
type: "item.completed",
|
|
81819
|
+
...stamp,
|
|
81820
|
+
...base(context),
|
|
81821
|
+
itemId: RuntimeItemId.make(taskId),
|
|
81822
|
+
payload: {
|
|
81823
|
+
itemType: itemTypeForToolName(toolName),
|
|
81824
|
+
status: failed ? "failed" : "completed",
|
|
81825
|
+
...toolName ? { title: toolName } : {},
|
|
81826
|
+
...detail ? { detail } : {},
|
|
81827
|
+
...editFacts ? { data: editFacts } : {}
|
|
81828
|
+
},
|
|
81829
|
+
raw: rawFrom(record)
|
|
81830
|
+
})];
|
|
81831
|
+
}
|
|
81832
|
+
case "task.lifecycle.failed": {
|
|
81833
|
+
const event = eventField(payload);
|
|
81834
|
+
const taskId = stringField(event, "task_id");
|
|
81835
|
+
if (!taskId) return [];
|
|
81836
|
+
const toolName = state.toolNameByTaskId.get(taskId);
|
|
81837
|
+
if (!toolName || state.settledToolTaskIds.has(taskId)) return [];
|
|
81838
|
+
state.settledToolTaskIds.add(taskId);
|
|
81839
|
+
const detail = truncate(stringField(event, "reason"), MAX_TOOL_DETAIL_CHARS);
|
|
81840
|
+
return [(stamp) => ({
|
|
81841
|
+
type: "item.completed",
|
|
81842
|
+
...stamp,
|
|
81843
|
+
...base(context),
|
|
81844
|
+
itemId: RuntimeItemId.make(taskId),
|
|
81845
|
+
payload: {
|
|
81846
|
+
itemType: itemTypeForToolName(toolName),
|
|
81847
|
+
status: "failed",
|
|
81848
|
+
title: toolName,
|
|
81849
|
+
...detail ? { detail } : {}
|
|
81850
|
+
},
|
|
81851
|
+
raw: rawFrom(record)
|
|
81852
|
+
})];
|
|
81853
|
+
}
|
|
81854
|
+
default: break;
|
|
81855
|
+
}
|
|
81856
|
+
if (record.payloadType.startsWith("run.terminal.")) return mapTerminalRecord({
|
|
81857
|
+
record,
|
|
81858
|
+
state,
|
|
81859
|
+
context
|
|
81860
|
+
});
|
|
81861
|
+
return [];
|
|
81862
|
+
}
|
|
81863
|
+
/**
|
|
81864
|
+
* Terminal record handling. The final `text` is the authoritative assistant
|
|
81865
|
+
* message and is a superset of the streamed deltas (Muse streams the visible
|
|
81866
|
+
* prefix only), so any remainder is flushed as one last delta before the
|
|
81867
|
+
* assistant item closes.
|
|
81868
|
+
*/
|
|
81869
|
+
function mapTerminalRecord(input) {
|
|
81870
|
+
const { record, state, context } = input;
|
|
81871
|
+
const terminalState = turnStateFromTerminal(stringField(record.payload, "terminal") ?? record.payloadType.slice(13));
|
|
81872
|
+
state.terminal = terminalState;
|
|
81873
|
+
const drafts = [];
|
|
81874
|
+
const remainder = assistantTextRemainder(state.assistantText, stringField(record.payload, "text"));
|
|
81875
|
+
if (remainder) drafts.push(...appendAssistantText({
|
|
81876
|
+
text: remainder,
|
|
81877
|
+
state,
|
|
81878
|
+
context,
|
|
81879
|
+
record
|
|
81880
|
+
}));
|
|
81881
|
+
if (state.assistantItemStarted) drafts.push((stamp) => ({
|
|
81882
|
+
type: "item.completed",
|
|
81883
|
+
...stamp,
|
|
81884
|
+
...base(context),
|
|
81885
|
+
itemId: RuntimeItemId.make(context.assistantItemId),
|
|
81886
|
+
payload: {
|
|
81887
|
+
itemType: "assistant_message",
|
|
81888
|
+
status: "completed"
|
|
81889
|
+
}
|
|
81890
|
+
}));
|
|
81891
|
+
const reason = stringField(record.payload, "reason");
|
|
81892
|
+
drafts.push((stamp) => ({
|
|
81893
|
+
type: "turn.completed",
|
|
81894
|
+
...stamp,
|
|
81895
|
+
...base(context),
|
|
81896
|
+
payload: {
|
|
81897
|
+
state: terminalState,
|
|
81898
|
+
...reason ? { stopReason: reason } : {},
|
|
81899
|
+
...terminalState === "failed" && reason ? { errorMessage: reason } : {}
|
|
81900
|
+
},
|
|
81901
|
+
raw: rawFrom(record)
|
|
81902
|
+
}));
|
|
81903
|
+
return drafts;
|
|
81904
|
+
}
|
|
81905
|
+
function appendAssistantText(input) {
|
|
81906
|
+
const { text, state, context, record } = input;
|
|
81907
|
+
const drafts = [];
|
|
81908
|
+
if (!state.assistantItemStarted) {
|
|
81909
|
+
state.assistantItemStarted = true;
|
|
81910
|
+
drafts.push((stamp) => ({
|
|
81911
|
+
type: "item.started",
|
|
81912
|
+
...stamp,
|
|
81913
|
+
...base(context),
|
|
81914
|
+
itemId: RuntimeItemId.make(context.assistantItemId),
|
|
81915
|
+
payload: {
|
|
81916
|
+
itemType: "assistant_message",
|
|
81917
|
+
status: "inProgress"
|
|
81918
|
+
}
|
|
81919
|
+
}));
|
|
81920
|
+
}
|
|
81921
|
+
state.assistantText += text;
|
|
81922
|
+
drafts.push((stamp) => ({
|
|
81923
|
+
type: "content.delta",
|
|
81924
|
+
...stamp,
|
|
81925
|
+
...base(context),
|
|
81926
|
+
itemId: RuntimeItemId.make(context.assistantItemId),
|
|
81927
|
+
payload: {
|
|
81928
|
+
streamKind: "assistant_text",
|
|
81929
|
+
delta: text
|
|
81930
|
+
},
|
|
81931
|
+
raw: rawFrom(record)
|
|
81932
|
+
}));
|
|
81933
|
+
return drafts;
|
|
81934
|
+
}
|
|
81935
|
+
/**
|
|
81936
|
+
* The tail of `finalText` that was never streamed. Returns `undefined` when
|
|
81937
|
+
* the streamed text already covers it, or when the two diverge — a divergent
|
|
81938
|
+
* final text is dropped rather than duplicated into the transcript.
|
|
81939
|
+
*/
|
|
81940
|
+
function assistantTextRemainder(streamed, finalText) {
|
|
81941
|
+
if (finalText === void 0 || finalText.length === 0) return void 0;
|
|
81942
|
+
if (streamed.length === 0) return finalText;
|
|
81943
|
+
if (!finalText.startsWith(streamed)) return void 0;
|
|
81944
|
+
const remainder = finalText.slice(streamed.length);
|
|
81945
|
+
return remainder.length > 0 ? remainder : void 0;
|
|
81946
|
+
}
|
|
81947
|
+
function turnStateFromTerminal(terminal) {
|
|
81948
|
+
switch (terminal) {
|
|
81949
|
+
case "completed": return "completed";
|
|
81950
|
+
case "cancelled":
|
|
81951
|
+
case "canceled": return "cancelled";
|
|
81952
|
+
case "interrupted": return "interrupted";
|
|
81953
|
+
default: return "failed";
|
|
81954
|
+
}
|
|
81955
|
+
}
|
|
81956
|
+
/** `tool.write_file` → `write_file`; anything else is not a tool task. */
|
|
81957
|
+
function toolNameFromTaskKind(taskKind) {
|
|
81958
|
+
if (!taskKind || !taskKind.startsWith("tool.")) return void 0;
|
|
81959
|
+
const name = taskKind.slice(5).trim();
|
|
81960
|
+
return name.length > 0 ? name : void 0;
|
|
81961
|
+
}
|
|
81962
|
+
/** `tool:call_019f…` → `call_019f…`. */
|
|
81963
|
+
function toolCallIdFromIdempotencyKey(key) {
|
|
81964
|
+
if (!key || !key.startsWith("tool:")) return void 0;
|
|
81965
|
+
const callId = key.slice(5).trim();
|
|
81966
|
+
return callId.length > 0 ? callId : void 0;
|
|
81967
|
+
}
|
|
81968
|
+
function itemTypeForToolName(toolName) {
|
|
81969
|
+
switch (toolName) {
|
|
81970
|
+
case "bash":
|
|
81971
|
+
case "shell":
|
|
81972
|
+
case "run_command":
|
|
81973
|
+
case "unified_exec": return "command_execution";
|
|
81974
|
+
case "write_file":
|
|
81975
|
+
case "edit_file":
|
|
81976
|
+
case "multi_edit":
|
|
81977
|
+
case "apply_patch":
|
|
81978
|
+
case "delete_file":
|
|
81979
|
+
case "move_file": return "file_change";
|
|
81980
|
+
case "web_search":
|
|
81981
|
+
case "web_fetch": return "web_search";
|
|
81982
|
+
case "view_image": return "image_view";
|
|
81983
|
+
default:
|
|
81984
|
+
if (toolName?.startsWith("subagent_")) return "collab_agent_tool_call";
|
|
81985
|
+
if (toolName?.startsWith("mcp__")) return "mcp_tool_call";
|
|
81986
|
+
return "dynamic_tool_call";
|
|
81987
|
+
}
|
|
81988
|
+
}
|
|
81989
|
+
function base(context) {
|
|
81990
|
+
return {
|
|
81991
|
+
provider: context.provider,
|
|
81992
|
+
...context.providerInstanceId ? { providerInstanceId: context.providerInstanceId } : {},
|
|
81993
|
+
threadId: context.threadId,
|
|
81994
|
+
turnId: context.turnId
|
|
81995
|
+
};
|
|
81996
|
+
}
|
|
81997
|
+
function rawFrom(record) {
|
|
81998
|
+
return {
|
|
81999
|
+
source: "muse.exec.jsonl",
|
|
82000
|
+
method: record.payloadType,
|
|
82001
|
+
payload: record.payload
|
|
82002
|
+
};
|
|
82003
|
+
}
|
|
82004
|
+
function isRecord$1(value) {
|
|
82005
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
82006
|
+
}
|
|
82007
|
+
function stringField(source, key) {
|
|
82008
|
+
const value = source?.[key];
|
|
82009
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
82010
|
+
}
|
|
82011
|
+
function recordField(source, key) {
|
|
82012
|
+
const value = source?.[key];
|
|
82013
|
+
return isRecord$1(value) ? value : void 0;
|
|
82014
|
+
}
|
|
82015
|
+
function eventField(payload) {
|
|
82016
|
+
return recordField(payload, "event");
|
|
82017
|
+
}
|
|
82018
|
+
function truncate(value, max) {
|
|
82019
|
+
if (value === void 0) return void 0;
|
|
82020
|
+
const trimmed = value.trim();
|
|
82021
|
+
if (trimmed.length === 0) return void 0;
|
|
82022
|
+
return trimmed.length <= max ? trimmed : `${trimmed.slice(0, max)}…`;
|
|
82023
|
+
}
|
|
82024
|
+
//#endregion
|
|
82025
|
+
//#region src/textGeneration/MuseTextGeneration.ts
|
|
82026
|
+
/**
|
|
82027
|
+
* MuseTextGeneration — commit messages, PR text, branch names and thread
|
|
82028
|
+
* titles through Muse Code.
|
|
82029
|
+
*
|
|
82030
|
+
* Muse has no lightweight completion endpoint behind the CLI, so each request
|
|
82031
|
+
* is one headless `muse exec --json` run with the session log disabled: a
|
|
82032
|
+
* throwaway agent turn whose final text we parse as JSON. Reasoning effort is
|
|
82033
|
+
* pinned low because these are short structured-output tasks and Muse Spark
|
|
82034
|
+
* bills reasoning as output tokens.
|
|
82035
|
+
*
|
|
82036
|
+
* @module textGeneration/MuseTextGeneration
|
|
82037
|
+
*/
|
|
82038
|
+
const MUSE_TIMEOUT_MS = 18e4;
|
|
82039
|
+
const MUSE_TEXT_REASONING_EFFORT = "low";
|
|
82040
|
+
/** A structured-output turn should never need to loop; cap it cheaply. */
|
|
82041
|
+
const MUSE_TEXT_MAX_MODEL_STEPS = "8";
|
|
82042
|
+
const isTextGenerationError = Schema$1.is(TextGenerationError);
|
|
82043
|
+
/** Final assistant text from a `muse exec --json` stdout stream. */
|
|
82044
|
+
function museTerminalTextFromJsonl(stdout) {
|
|
82045
|
+
let terminalText;
|
|
82046
|
+
let streamed = "";
|
|
82047
|
+
for (const line of stdout.split("\n")) {
|
|
82048
|
+
const record = parseMuseExecLine(line);
|
|
82049
|
+
if (!record) continue;
|
|
82050
|
+
if (record.payloadType === "run.output.delta") {
|
|
82051
|
+
const delta = record.payload["text"];
|
|
82052
|
+
if (typeof delta === "string") streamed += delta;
|
|
82053
|
+
continue;
|
|
82054
|
+
}
|
|
82055
|
+
if (record.payloadType.startsWith("run.terminal.")) {
|
|
82056
|
+
const text = record.payload["text"];
|
|
82057
|
+
if (typeof text === "string" && text.length > 0) terminalText = text;
|
|
82058
|
+
}
|
|
82059
|
+
}
|
|
82060
|
+
const resolved = terminalText ?? streamed;
|
|
82061
|
+
return resolved.trim().length > 0 ? resolved : void 0;
|
|
82062
|
+
}
|
|
82063
|
+
const makeMuseTextGeneration = (museSettings, environment = process.env) => Effect.sync(() => {
|
|
82064
|
+
const runMuseJson = ({ operation, cwd, prompt, outputSchemaJson, modelSelection }) => Effect.gen(function* () {
|
|
82065
|
+
const binaryPath = museSettings.binaryPath || "muse";
|
|
82066
|
+
const spawnCommand = yield* resolveSpawnCommand(binaryPath, [
|
|
82067
|
+
"exec",
|
|
82068
|
+
"--json",
|
|
82069
|
+
"--no-session-log",
|
|
82070
|
+
"--reasoning-effort",
|
|
82071
|
+
MUSE_TEXT_REASONING_EFFORT,
|
|
82072
|
+
"--max-model-steps",
|
|
82073
|
+
MUSE_TEXT_MAX_MODEL_STEPS,
|
|
82074
|
+
"--disable-approval",
|
|
82075
|
+
"--disable-write",
|
|
82076
|
+
"--disable-shell",
|
|
82077
|
+
...modelSelection.model ? ["--model", modelSelection.model] : [],
|
|
82078
|
+
...museSettings.baseUrl ? ["--base-url", museSettings.baseUrl] : [],
|
|
82079
|
+
prompt
|
|
82080
|
+
], { env: environment });
|
|
82081
|
+
const result = yield* spawnAndCollect(binaryPath, ChildProcess.make(spawnCommand.command, spawnCommand.args, {
|
|
82082
|
+
cwd,
|
|
82083
|
+
env: environment,
|
|
82084
|
+
shell: spawnCommand.shell
|
|
82085
|
+
})).pipe(Effect.timeoutOption(MUSE_TIMEOUT_MS), Effect.flatMap(Option.match({
|
|
82086
|
+
onNone: () => Effect.fail(new TextGenerationError({
|
|
82087
|
+
operation,
|
|
82088
|
+
detail: "Muse Code request timed out."
|
|
82089
|
+
})),
|
|
82090
|
+
onSome: Effect.succeed
|
|
82091
|
+
})), Effect.mapError((cause) => isTextGenerationError(cause) ? cause : new TextGenerationError({
|
|
82092
|
+
operation,
|
|
82093
|
+
detail: "Muse Code request failed.",
|
|
82094
|
+
cause
|
|
82095
|
+
})));
|
|
82096
|
+
const output = museTerminalTextFromJsonl(result.stdout);
|
|
82097
|
+
if (!output) return yield* new TextGenerationError({
|
|
82098
|
+
operation,
|
|
82099
|
+
detail: result.code === 0 ? "Muse Code returned empty output." : `Muse Code exited with code ${result.code}.`
|
|
82100
|
+
});
|
|
82101
|
+
return yield* Schema$1.decodeEffect(Schema$1.fromJsonString(outputSchemaJson))(extractJsonObject(output)).pipe(Effect.catchTags({ SchemaError: (cause) => Effect.fail(new TextGenerationError({
|
|
82102
|
+
operation,
|
|
82103
|
+
detail: "Muse Code returned invalid structured output.",
|
|
82104
|
+
cause
|
|
82105
|
+
})) }));
|
|
82106
|
+
}).pipe(Effect.mapError((cause) => isTextGenerationError(cause) ? cause : new TextGenerationError({
|
|
82107
|
+
operation,
|
|
82108
|
+
detail: "Muse Code text generation failed.",
|
|
82109
|
+
cause
|
|
82110
|
+
})));
|
|
82111
|
+
return {
|
|
82112
|
+
generateCommitMessage: Effect.fn("MuseTextGeneration.generateCommitMessage")(function* (input) {
|
|
82113
|
+
const { prompt, outputSchema } = buildCommitMessagePrompt({
|
|
82114
|
+
branch: input.branch,
|
|
82115
|
+
stagedSummary: input.stagedSummary,
|
|
82116
|
+
stagedPatch: input.stagedPatch,
|
|
82117
|
+
includeBranch: input.includeBranch === true,
|
|
82118
|
+
policy: input.policy
|
|
82119
|
+
});
|
|
82120
|
+
const generated = yield* runMuseJson({
|
|
82121
|
+
operation: "generateCommitMessage",
|
|
82122
|
+
cwd: input.cwd,
|
|
82123
|
+
prompt,
|
|
82124
|
+
outputSchemaJson: outputSchema,
|
|
82125
|
+
modelSelection: input.modelSelection
|
|
82126
|
+
});
|
|
82127
|
+
return {
|
|
82128
|
+
subject: sanitizeCommitSubject(generated.subject),
|
|
82129
|
+
body: generated.body.trim(),
|
|
82130
|
+
..."branch" in generated && typeof generated.branch === "string" ? { branch: sanitizeFeatureBranchName(generated.branch) } : {}
|
|
82131
|
+
};
|
|
82132
|
+
}),
|
|
82133
|
+
generatePrContent: Effect.fn("MuseTextGeneration.generatePrContent")(function* (input) {
|
|
82134
|
+
const { prompt, outputSchema } = buildPrContentPrompt({
|
|
82135
|
+
baseBranch: input.baseBranch,
|
|
82136
|
+
headBranch: input.headBranch,
|
|
82137
|
+
commitSummary: input.commitSummary,
|
|
82138
|
+
diffSummary: input.diffSummary,
|
|
82139
|
+
diffPatch: input.diffPatch,
|
|
82140
|
+
policy: input.policy,
|
|
82141
|
+
changeRequestTemplate: input.changeRequestTemplate
|
|
82142
|
+
});
|
|
82143
|
+
const generated = yield* runMuseJson({
|
|
82144
|
+
operation: "generatePrContent",
|
|
82145
|
+
cwd: input.cwd,
|
|
82146
|
+
prompt,
|
|
82147
|
+
outputSchemaJson: outputSchema,
|
|
82148
|
+
modelSelection: input.modelSelection
|
|
82149
|
+
});
|
|
82150
|
+
return {
|
|
82151
|
+
title: sanitizePrTitle(generated.title),
|
|
82152
|
+
body: generated.body.trim()
|
|
82153
|
+
};
|
|
82154
|
+
}),
|
|
82155
|
+
generateBranchName: Effect.fn("MuseTextGeneration.generateBranchName")(function* (input) {
|
|
82156
|
+
const { prompt, outputSchema } = buildBranchNamePrompt({
|
|
82157
|
+
message: input.message,
|
|
82158
|
+
attachments: input.attachments
|
|
82159
|
+
});
|
|
82160
|
+
return { branch: sanitizeBranchFragment((yield* runMuseJson({
|
|
82161
|
+
operation: "generateBranchName",
|
|
82162
|
+
cwd: input.cwd,
|
|
82163
|
+
prompt,
|
|
82164
|
+
outputSchemaJson: outputSchema,
|
|
82165
|
+
modelSelection: input.modelSelection
|
|
82166
|
+
})).branch) };
|
|
82167
|
+
}),
|
|
82168
|
+
generateThreadTitle: Effect.fn("MuseTextGeneration.generateThreadTitle")(function* (input) {
|
|
82169
|
+
const { prompt, outputSchema } = buildThreadTitlePrompt({
|
|
82170
|
+
message: input.message,
|
|
82171
|
+
attachments: input.attachments
|
|
82172
|
+
});
|
|
82173
|
+
return { title: sanitizeThreadTitle((yield* runMuseJson({
|
|
82174
|
+
operation: "generateThreadTitle",
|
|
82175
|
+
cwd: input.cwd,
|
|
82176
|
+
prompt,
|
|
82177
|
+
outputSchemaJson: outputSchema,
|
|
82178
|
+
modelSelection: input.modelSelection
|
|
82179
|
+
})).title) };
|
|
82180
|
+
})
|
|
82181
|
+
};
|
|
82182
|
+
});
|
|
82183
|
+
//#endregion
|
|
82184
|
+
//#region src/provider/muse/MuseExecRuntime.ts
|
|
82185
|
+
/**
|
|
82186
|
+
* MuseExecRuntime — one `muse exec --json` child process per turn.
|
|
82187
|
+
*
|
|
82188
|
+
* Muse Code has no long-lived protocol surface: a turn is a process, and the
|
|
82189
|
+
* thread is rebuilt from the CLI's own event-sourced session log when the
|
|
82190
|
+
* next turn passes `--session-id`. This module owns spawning, line framing,
|
|
82191
|
+
* interruption, and exit classification; it knows nothing about canonical
|
|
82192
|
+
* runtime events.
|
|
82193
|
+
*
|
|
82194
|
+
* @module provider/muse/MuseExecRuntime
|
|
82195
|
+
*/
|
|
82196
|
+
/** Exit code Muse uses for a turn that failed or was cancelled. */
|
|
82197
|
+
const MUSE_EXIT_FAILED = 1;
|
|
82198
|
+
/** Exit codes produced by SIGINT / SIGTERM. */
|
|
82199
|
+
const MUSE_EXIT_SIGINT = 130;
|
|
82200
|
+
const MUSE_EXIT_SIGTERM = 143;
|
|
82201
|
+
/**
|
|
82202
|
+
* Spawn one turn. The child is bound to the caller's scope, so closing the
|
|
82203
|
+
* scope kills it; `awaitOutcome` is how a caller waits for a clean end.
|
|
82204
|
+
*/
|
|
82205
|
+
const runMuseExec = Effect.fn("runMuseExec")(function* (input) {
|
|
82206
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
82207
|
+
const runScope = yield* Scope.Scope;
|
|
82208
|
+
const spawnCommand = yield* resolveSpawnCommand(input.binaryPath, [...input.args], { env: input.environment }).pipe(Effect.mapError((cause) => new ProviderAdapterProcessError({
|
|
82209
|
+
provider: "muse",
|
|
82210
|
+
threadId: input.threadId,
|
|
82211
|
+
detail: `Failed to resolve the Muse Code binary (${input.binaryPath}).`,
|
|
82212
|
+
cause
|
|
82213
|
+
})));
|
|
82214
|
+
const child = yield* spawner.spawn(ChildProcess.make(spawnCommand.command, spawnCommand.args, {
|
|
82215
|
+
cwd: input.cwd,
|
|
82216
|
+
env: input.environment,
|
|
82217
|
+
shell: spawnCommand.shell
|
|
82218
|
+
})).pipe(Effect.provideService(Scope.Scope, runScope), Effect.mapError((cause) => new ProviderAdapterProcessError({
|
|
82219
|
+
provider: "muse",
|
|
82220
|
+
threadId: input.threadId,
|
|
82221
|
+
detail: "Failed to spawn `muse exec`.",
|
|
82222
|
+
cause
|
|
82223
|
+
})));
|
|
82224
|
+
const stderrRef = yield* Ref.make("");
|
|
82225
|
+
const interruptedRef = yield* Ref.make(false);
|
|
82226
|
+
const stdoutFiber = yield* child.stdout.pipe(Stream.decodeText(), Stream.splitLines, Stream.runForEach(input.onLine), Effect.ignore, Effect.forkIn(runScope));
|
|
82227
|
+
const stderrFiber = yield* child.stderr.pipe(Stream.decodeText(), Stream.runForEach((chunk) => Ref.update(stderrRef, (stderr) => `${stderr}${chunk}`.slice(-4e3))), Effect.ignore, Effect.forkIn(runScope));
|
|
82228
|
+
return {
|
|
82229
|
+
awaitOutcome: Effect.gen(function* () {
|
|
82230
|
+
const code = yield* child.exitCode.pipe(Effect.orElseSucceed(() => MUSE_EXIT_FAILED));
|
|
82231
|
+
yield* Fiber.await(stdoutFiber).pipe(Effect.ignore);
|
|
82232
|
+
yield* Fiber.await(stderrFiber).pipe(Effect.ignore);
|
|
82233
|
+
const exitCode = Number(code);
|
|
82234
|
+
const interrupted = yield* Ref.get(interruptedRef);
|
|
82235
|
+
if (exitCode === 0) return { kind: "completed" };
|
|
82236
|
+
if (interrupted || exitCode === MUSE_EXIT_SIGINT || exitCode === MUSE_EXIT_SIGTERM) return { kind: "interrupted" };
|
|
82237
|
+
return {
|
|
82238
|
+
kind: "failed",
|
|
82239
|
+
exitCode,
|
|
82240
|
+
stderr: (yield* Ref.get(stderrRef)).trim()
|
|
82241
|
+
};
|
|
82242
|
+
}),
|
|
82243
|
+
interrupt: Ref.set(interruptedRef, true).pipe(Effect.andThen(child.kill({
|
|
82244
|
+
killSignal: "SIGINT",
|
|
82245
|
+
forceKillAfter: "5 seconds"
|
|
82246
|
+
}).pipe(Effect.ignore)), Effect.asVoid)
|
|
82247
|
+
};
|
|
82248
|
+
});
|
|
82249
|
+
//#endregion
|
|
82250
|
+
//#region src/provider/muse/MuseSessionLog.ts
|
|
82251
|
+
/** Root directory holding Muse Code's per-session event logs. */
|
|
82252
|
+
const museDataRoot = Effect.fn("museDataRoot")(function* (environment = process.env) {
|
|
82253
|
+
const path = yield* Path.Path;
|
|
82254
|
+
const xdgDataHome = environment["XDG_DATA_HOME"]?.trim();
|
|
82255
|
+
const base = xdgDataHome && xdgDataHome.length > 0 ? xdgDataHome : path.join(NodeOS.homedir(), ".local", "share");
|
|
82256
|
+
return path.join(base, "muse");
|
|
82257
|
+
});
|
|
82258
|
+
/**
|
|
82259
|
+
* Locate a session log by id. The CLI files logs under the date the session
|
|
82260
|
+
* was created, which we do not know for a resumed session, so the year /
|
|
82261
|
+
* month / day levels are walked. Returns `undefined` when the session has no
|
|
82262
|
+
* log yet (a `--no-session-log` run, or a run that died before its first
|
|
82263
|
+
* flush).
|
|
82264
|
+
*/
|
|
82265
|
+
const findMuseSessionLog = Effect.fn("findMuseSessionLog")(function* (input) {
|
|
82266
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
82267
|
+
const path = yield* Path.Path;
|
|
82268
|
+
const sessionsRoot = path.join(yield* museDataRoot(input.environment ?? process.env), "sessions");
|
|
82269
|
+
const listing = (directory) => fileSystem.readDirectory(directory).pipe(Effect.orElseSucceed(() => []));
|
|
82270
|
+
const descending = (entries) => [...entries].sort().reverse();
|
|
82271
|
+
for (const year of descending(yield* listing(sessionsRoot))) {
|
|
82272
|
+
const yearPath = path.join(sessionsRoot, year);
|
|
82273
|
+
for (const month of descending(yield* listing(yearPath))) {
|
|
82274
|
+
const monthPath = path.join(yearPath, month);
|
|
82275
|
+
for (const day of descending(yield* listing(monthPath))) {
|
|
82276
|
+
const candidate = path.join(monthPath, day, input.sessionId, "session.jsonl");
|
|
82277
|
+
if (yield* fileSystem.exists(candidate).pipe(Effect.orElseSucceed(() => false))) return candidate;
|
|
82278
|
+
}
|
|
82279
|
+
}
|
|
82280
|
+
}
|
|
82281
|
+
});
|
|
82282
|
+
/**
|
|
82283
|
+
* Sum the token usage recorded in a session log.
|
|
82284
|
+
*
|
|
82285
|
+
* Muse attributes usage per model call (`goal_usage_attribution`), including
|
|
82286
|
+
* the background observer agents, so the totals here cover everything the
|
|
82287
|
+
* session spent — which is what a user reading a token counter expects.
|
|
82288
|
+
*/
|
|
82289
|
+
function parseMuseTokenUsage(contents) {
|
|
82290
|
+
let total = {
|
|
82291
|
+
inputTokens: 0,
|
|
82292
|
+
outputTokens: 0,
|
|
82293
|
+
cachedTokens: 0,
|
|
82294
|
+
reasoningTokens: 0
|
|
82295
|
+
};
|
|
82296
|
+
let last;
|
|
82297
|
+
let seen = false;
|
|
82298
|
+
for (const line of contents.split("\n")) {
|
|
82299
|
+
const quantity = usageQuantityFromLine(line);
|
|
82300
|
+
if (!quantity) continue;
|
|
82301
|
+
seen = true;
|
|
82302
|
+
last = quantity;
|
|
82303
|
+
total = {
|
|
82304
|
+
inputTokens: total.inputTokens + quantity.inputTokens,
|
|
82305
|
+
outputTokens: total.outputTokens + quantity.outputTokens,
|
|
82306
|
+
cachedTokens: total.cachedTokens + quantity.cachedTokens,
|
|
82307
|
+
reasoningTokens: total.reasoningTokens + quantity.reasoningTokens
|
|
82308
|
+
};
|
|
82309
|
+
}
|
|
82310
|
+
if (!seen) return void 0;
|
|
82311
|
+
return {
|
|
82312
|
+
usedTokens: total.inputTokens + total.outputTokens,
|
|
82313
|
+
inputTokens: total.inputTokens,
|
|
82314
|
+
cachedInputTokens: total.cachedTokens,
|
|
82315
|
+
outputTokens: total.outputTokens,
|
|
82316
|
+
reasoningOutputTokens: total.reasoningTokens,
|
|
82317
|
+
...last ? {
|
|
82318
|
+
lastUsedTokens: last.inputTokens + last.outputTokens,
|
|
82319
|
+
lastInputTokens: last.inputTokens,
|
|
82320
|
+
lastCachedInputTokens: last.cachedTokens,
|
|
82321
|
+
lastOutputTokens: last.outputTokens,
|
|
82322
|
+
lastReasoningOutputTokens: last.reasoningTokens
|
|
82323
|
+
} : {},
|
|
82324
|
+
compactsAutomatically: true
|
|
82325
|
+
};
|
|
82326
|
+
}
|
|
82327
|
+
function usageQuantityFromLine(line) {
|
|
82328
|
+
const trimmed = line.trim();
|
|
82329
|
+
if (trimmed.length === 0 || !trimmed.includes("goal_usage_attribution")) return void 0;
|
|
82330
|
+
let decoded;
|
|
82331
|
+
try {
|
|
82332
|
+
decoded = JSON.parse(trimmed);
|
|
82333
|
+
} catch {
|
|
82334
|
+
return;
|
|
82335
|
+
}
|
|
82336
|
+
const quantity = readPath(decoded, [
|
|
82337
|
+
"payload",
|
|
82338
|
+
"event",
|
|
82339
|
+
"record",
|
|
82340
|
+
"quantity"
|
|
82341
|
+
]);
|
|
82342
|
+
if (!isRecord(quantity)) return void 0;
|
|
82343
|
+
return {
|
|
82344
|
+
inputTokens: nonNegativeInt(quantity["input_tokens"]),
|
|
82345
|
+
outputTokens: nonNegativeInt(quantity["output_tokens"]),
|
|
82346
|
+
cachedTokens: nonNegativeInt(quantity["cached_tokens"]),
|
|
82347
|
+
reasoningTokens: nonNegativeInt(quantity["reasoning_tokens"])
|
|
82348
|
+
};
|
|
82349
|
+
}
|
|
82350
|
+
/** Read a session's cumulative usage, or `undefined` when unavailable. */
|
|
82351
|
+
const readMuseSessionUsage = Effect.fn("readMuseSessionUsage")(function* (input) {
|
|
82352
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
82353
|
+
const logPath = yield* findMuseSessionLog(input);
|
|
82354
|
+
if (logPath === void 0) return void 0;
|
|
82355
|
+
const contents = yield* fileSystem.readFileString(logPath).pipe(Effect.orElseSucceed(() => void 0));
|
|
82356
|
+
return contents === void 0 ? void 0 : parseMuseTokenUsage(contents);
|
|
82357
|
+
});
|
|
82358
|
+
/**
|
|
82359
|
+
* Parse one `model-catalog/*.json` document. Hidden rows are dropped: the CLI
|
|
82360
|
+
* marks a row `visibility: "visible"` when it should appear in `/models`.
|
|
82361
|
+
*/
|
|
82362
|
+
function parseMuseModelCatalog(contents) {
|
|
82363
|
+
let decoded;
|
|
82364
|
+
try {
|
|
82365
|
+
decoded = JSON.parse(contents);
|
|
82366
|
+
} catch {
|
|
82367
|
+
return [];
|
|
82368
|
+
}
|
|
82369
|
+
const rows = isRecord(decoded) ? decoded["rows"] : void 0;
|
|
82370
|
+
if (!Array.isArray(rows)) return [];
|
|
82371
|
+
const models = [];
|
|
82372
|
+
for (const row of rows) {
|
|
82373
|
+
if (!isRecord(row)) continue;
|
|
82374
|
+
const slug = typeof row["model_id"] === "string" ? row["model_id"].trim() : "";
|
|
82375
|
+
if (slug.length === 0) continue;
|
|
82376
|
+
if (typeof row["visibility"] === "string" && row["visibility"] !== "visible") continue;
|
|
82377
|
+
const label = typeof row["display_label"] === "string" ? row["display_label"].trim() : "";
|
|
82378
|
+
const contextLimit = row["context_limit"];
|
|
82379
|
+
models.push({
|
|
82380
|
+
slug,
|
|
82381
|
+
name: label.length > 0 ? label : slug,
|
|
82382
|
+
contextLimit: typeof contextLimit === "number" && contextLimit > 0 ? contextLimit : void 0,
|
|
82383
|
+
isDefault: row["is_default"] === true
|
|
82384
|
+
});
|
|
82385
|
+
}
|
|
82386
|
+
return models;
|
|
82387
|
+
}
|
|
82388
|
+
/** Read every model row the CLI has cached on disk. */
|
|
82389
|
+
const readMuseModelCatalog = Effect.fn("readMuseModelCatalog")(function* (environment = process.env) {
|
|
82390
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
82391
|
+
const path = yield* Path.Path;
|
|
82392
|
+
const catalogRoot = path.join(yield* museDataRoot(environment), "model-catalog");
|
|
82393
|
+
const entries = yield* fileSystem.readDirectory(catalogRoot).pipe(Effect.orElseSucceed(() => []));
|
|
82394
|
+
const bySlug = /* @__PURE__ */ new Map();
|
|
82395
|
+
for (const entry of [...entries].sort()) {
|
|
82396
|
+
if (!entry.endsWith(".json")) continue;
|
|
82397
|
+
const contents = yield* fileSystem.readFileString(path.join(catalogRoot, entry)).pipe(Effect.orElseSucceed(() => void 0));
|
|
82398
|
+
if (contents === void 0) continue;
|
|
82399
|
+
for (const model of parseMuseModelCatalog(contents)) if (!bySlug.has(model.slug)) bySlug.set(model.slug, model);
|
|
82400
|
+
}
|
|
82401
|
+
return [...bySlug.values()];
|
|
82402
|
+
});
|
|
82403
|
+
function readPath(source, keys) {
|
|
82404
|
+
let current = source;
|
|
82405
|
+
for (const key of keys) {
|
|
82406
|
+
if (!isRecord(current)) return void 0;
|
|
82407
|
+
current = current[key];
|
|
82408
|
+
}
|
|
82409
|
+
return current;
|
|
82410
|
+
}
|
|
82411
|
+
function isRecord(value) {
|
|
82412
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
82413
|
+
}
|
|
82414
|
+
function nonNegativeInt(value) {
|
|
82415
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
|
|
82416
|
+
}
|
|
82417
|
+
//#endregion
|
|
82418
|
+
//#region src/provider/Layers/MuseAdapter.ts
|
|
82419
|
+
/**
|
|
82420
|
+
* MuseAdapter — Meta Muse Code as a P4Code provider.
|
|
82421
|
+
*
|
|
82422
|
+
* Muse Code (beta, CLI 0.1.0) exposes no ACP or app-server protocol; the only
|
|
82423
|
+
* scriptable surface is `muse exec --json`, which runs one prompt to
|
|
82424
|
+
* completion and streams JSONL events on stdout. A P4Code thread therefore
|
|
82425
|
+
* maps onto a Muse *session id*: every turn spawns a fresh `muse exec` with
|
|
82426
|
+
* `--session-id`, and Muse replays its own event-sourced session log to
|
|
82427
|
+
* recover the conversation.
|
|
82428
|
+
*
|
|
82429
|
+
* Consequences that are visible to users, and deliberate:
|
|
82430
|
+
*
|
|
82431
|
+
* - **No interactive approvals.** Headless Muse has no channel to answer an
|
|
82432
|
+
* approval prompt, so the adapter runs with approval disabled and lets
|
|
82433
|
+
* Muse's OS sandbox do the containing. `respondToRequest` fails rather
|
|
82434
|
+
* than pretending to route a decision, and `approval-required` threads get
|
|
82435
|
+
* a `config.warning`.
|
|
82436
|
+
* - **No provider-side rollback.** Muse can fork a session interactively but
|
|
82437
|
+
* exposes nothing headless.
|
|
82438
|
+
* - **Tool arguments are not streamed.** They exist only in the on-disk
|
|
82439
|
+
* session log; items carry the tool name, its result text, and Muse's
|
|
82440
|
+
* `edit_facts`.
|
|
82441
|
+
*
|
|
82442
|
+
* @module provider/Layers/MuseAdapter
|
|
82443
|
+
*/
|
|
82444
|
+
const PROVIDER$1 = ProviderDriverKind.make("muse");
|
|
82445
|
+
const MUSE_RESUME_VERSION = 1;
|
|
82446
|
+
function museResumeCursor(museSessionId) {
|
|
82447
|
+
return {
|
|
82448
|
+
version: MUSE_RESUME_VERSION,
|
|
82449
|
+
sessionId: museSessionId
|
|
82450
|
+
};
|
|
82451
|
+
}
|
|
82452
|
+
/** Recover a Muse session id from a persisted resume cursor. */
|
|
82453
|
+
function parseMuseResume(raw) {
|
|
82454
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
82455
|
+
const candidate = raw;
|
|
82456
|
+
if (candidate.version !== MUSE_RESUME_VERSION) return void 0;
|
|
82457
|
+
return typeof candidate.sessionId === "string" && candidate.sessionId.length > 0 ? { sessionId: candidate.sessionId } : void 0;
|
|
82458
|
+
}
|
|
82459
|
+
function makeMuseAdapter(museSettings, options) {
|
|
82460
|
+
return Effect.gen(function* () {
|
|
82461
|
+
const boundInstanceId = options?.instanceId ?? ProviderInstanceId.make("muse");
|
|
82462
|
+
const environment = options?.environment ?? process.env;
|
|
82463
|
+
const crypto = yield* Crypto.Crypto;
|
|
82464
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
82465
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
82466
|
+
const path = yield* Path.Path;
|
|
82467
|
+
const serverConfig = yield* Effect.service(ServerConfig$1);
|
|
82468
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
82469
|
+
const runtimeEventPubSub = yield* PubSub.unbounded();
|
|
82470
|
+
const nowIso = Effect.map(DateTime.now, DateTime.formatIso);
|
|
82471
|
+
const randomUUIDv4 = crypto.randomUUIDv4.pipe(Effect.mapError((cause) => new ProviderAdapterRequestError({
|
|
82472
|
+
provider: PROVIDER$1,
|
|
82473
|
+
method: "crypto/randomUUIDv4",
|
|
82474
|
+
detail: "Failed to generate a Muse runtime identifier.",
|
|
82475
|
+
cause
|
|
82476
|
+
})));
|
|
82477
|
+
const makeEventStamp = () => Effect.all({
|
|
82478
|
+
eventId: Effect.map(randomUUIDv4, EventId.make),
|
|
82479
|
+
createdAt: nowIso
|
|
82480
|
+
});
|
|
82481
|
+
const publish = (event) => PubSub.publish(runtimeEventPubSub, event).pipe(Effect.asVoid);
|
|
82482
|
+
const requireSession = (threadId) => Effect.suspend(() => {
|
|
82483
|
+
const ctx = sessions.get(threadId);
|
|
82484
|
+
return ctx === void 0 || ctx.stopped ? Effect.fail(new ProviderAdapterSessionNotFoundError({
|
|
82485
|
+
provider: PROVIDER$1,
|
|
82486
|
+
threadId
|
|
82487
|
+
})) : Effect.succeed(ctx);
|
|
82488
|
+
});
|
|
82489
|
+
const startSession = (input) => Effect.gen(function* () {
|
|
82490
|
+
const existing = sessions.get(input.threadId);
|
|
82491
|
+
if (existing && !existing.stopped) return { ...existing.session };
|
|
82492
|
+
const resumed = parseMuseResume(input.resumeCursor);
|
|
82493
|
+
const museSessionId = resumed?.sessionId ?? (yield* randomUUIDv4);
|
|
82494
|
+
const cwd = input.cwd ?? process.cwd();
|
|
82495
|
+
const createdAt = yield* nowIso;
|
|
82496
|
+
const session = {
|
|
82497
|
+
provider: PROVIDER$1,
|
|
82498
|
+
providerInstanceId: boundInstanceId,
|
|
82499
|
+
status: "ready",
|
|
82500
|
+
runtimeMode: input.runtimeMode,
|
|
82501
|
+
cwd,
|
|
82502
|
+
...input.modelSelection?.model ? { model: input.modelSelection.model } : {},
|
|
82503
|
+
threadId: input.threadId,
|
|
82504
|
+
resumeCursor: museResumeCursor(museSessionId),
|
|
82505
|
+
createdAt,
|
|
82506
|
+
updatedAt: createdAt
|
|
82507
|
+
};
|
|
82508
|
+
const ctx = {
|
|
82509
|
+
threadId: input.threadId,
|
|
82510
|
+
museSessionId,
|
|
82511
|
+
cwd,
|
|
82512
|
+
session,
|
|
82513
|
+
activeTurn: void 0,
|
|
82514
|
+
turns: [],
|
|
82515
|
+
stopped: false
|
|
82516
|
+
};
|
|
82517
|
+
sessions.set(input.threadId, ctx);
|
|
82518
|
+
yield* publish({
|
|
82519
|
+
type: "session.started",
|
|
82520
|
+
...yield* makeEventStamp(),
|
|
82521
|
+
provider: PROVIDER$1,
|
|
82522
|
+
providerInstanceId: boundInstanceId,
|
|
82523
|
+
threadId: input.threadId,
|
|
82524
|
+
payload: resumed ? { resume: input.resumeCursor } : {}
|
|
82525
|
+
});
|
|
82526
|
+
yield* publish({
|
|
82527
|
+
type: "thread.started",
|
|
82528
|
+
...yield* makeEventStamp(),
|
|
82529
|
+
provider: PROVIDER$1,
|
|
82530
|
+
providerInstanceId: boundInstanceId,
|
|
82531
|
+
threadId: input.threadId,
|
|
82532
|
+
payload: { providerThreadId: museSessionId }
|
|
82533
|
+
});
|
|
82534
|
+
if (museApprovalsUnsupported(input.runtimeMode)) yield* publish({
|
|
82535
|
+
type: "config.warning",
|
|
82536
|
+
...yield* makeEventStamp(),
|
|
82537
|
+
provider: PROVIDER$1,
|
|
82538
|
+
providerInstanceId: boundInstanceId,
|
|
82539
|
+
threadId: input.threadId,
|
|
82540
|
+
payload: {
|
|
82541
|
+
summary: "Muse Code cannot ask for approvals in headless runs.",
|
|
82542
|
+
details: "This thread runs with approvals disabled and Muse's OS sandbox enabled; `muse exec` has no channel to answer an approval prompt."
|
|
82543
|
+
}
|
|
82544
|
+
});
|
|
82545
|
+
return { ...session };
|
|
82546
|
+
});
|
|
82547
|
+
const sendTurn = (input) => Effect.gen(function* () {
|
|
82548
|
+
const ctx = yield* requireSession(input.threadId);
|
|
82549
|
+
if (ctx.activeTurn) return yield* new ProviderAdapterRequestError({
|
|
82550
|
+
provider: PROVIDER$1,
|
|
82551
|
+
method: "muse/exec",
|
|
82552
|
+
detail: "A Muse turn is already running on this thread. `muse exec` runs one prompt per process, so turns cannot overlap."
|
|
82553
|
+
});
|
|
82554
|
+
const prompt = input.input?.trim();
|
|
82555
|
+
if (!prompt) return yield* new ProviderAdapterValidationError({
|
|
82556
|
+
provider: PROVIDER$1,
|
|
82557
|
+
operation: "sendTurn",
|
|
82558
|
+
issue: "Muse Code requires prompt text; attachments alone cannot start a turn."
|
|
82559
|
+
});
|
|
82560
|
+
const imagePaths = [];
|
|
82561
|
+
for (const attachment of input.attachments ?? []) {
|
|
82562
|
+
const attachmentPath = resolveAttachmentPath({
|
|
82563
|
+
attachmentsDir: serverConfig.attachmentsDir,
|
|
82564
|
+
attachment
|
|
82565
|
+
});
|
|
82566
|
+
if (!attachmentPath) return yield* new ProviderAdapterRequestError({
|
|
82567
|
+
provider: PROVIDER$1,
|
|
82568
|
+
method: "muse/exec",
|
|
82569
|
+
detail: `Invalid attachment id '${attachment.id}'.`
|
|
82570
|
+
});
|
|
82571
|
+
imagePaths.push(attachmentPath);
|
|
82572
|
+
}
|
|
82573
|
+
const turnId = TurnId.make(yield* randomUUIDv4);
|
|
82574
|
+
const model = input.modelSelection?.model ?? ctx.session.model;
|
|
82575
|
+
const args = buildMuseExecArgs({
|
|
82576
|
+
sessionId: ctx.museSessionId,
|
|
82577
|
+
prompt,
|
|
82578
|
+
model,
|
|
82579
|
+
baseUrl: museSettings.baseUrl || void 0,
|
|
82580
|
+
runtimeMode: ctx.session.runtimeMode,
|
|
82581
|
+
imagePaths
|
|
82582
|
+
});
|
|
82583
|
+
const state = makeMuseExecTurnState();
|
|
82584
|
+
const mappingContext = {
|
|
82585
|
+
provider: PROVIDER$1,
|
|
82586
|
+
providerInstanceId: boundInstanceId,
|
|
82587
|
+
threadId: input.threadId,
|
|
82588
|
+
turnId,
|
|
82589
|
+
assistantItemId: `${turnId}:assistant`
|
|
82590
|
+
};
|
|
82591
|
+
const handleLine = (line) => Effect.gen(function* () {
|
|
82592
|
+
const record = parseMuseExecLine(line);
|
|
82593
|
+
if (!record) return;
|
|
82594
|
+
const drafts = mapMuseExecRecord({
|
|
82595
|
+
record,
|
|
82596
|
+
state,
|
|
82597
|
+
context: mappingContext
|
|
82598
|
+
});
|
|
82599
|
+
for (const draft of drafts) yield* publish(draft(yield* makeEventStamp()));
|
|
82600
|
+
}).pipe(Effect.orElseSucceed(() => void 0));
|
|
82601
|
+
ctx.turns.push({
|
|
82602
|
+
id: turnId,
|
|
82603
|
+
items: []
|
|
82604
|
+
});
|
|
82605
|
+
ctx.session = {
|
|
82606
|
+
...ctx.session,
|
|
82607
|
+
status: "running",
|
|
82608
|
+
activeTurnId: turnId,
|
|
82609
|
+
updatedAt: yield* nowIso,
|
|
82610
|
+
...model ? { model } : {}
|
|
82611
|
+
};
|
|
82612
|
+
const turnScope = yield* Scope.make();
|
|
82613
|
+
const run = yield* runMuseExec({
|
|
82614
|
+
threadId: input.threadId,
|
|
82615
|
+
binaryPath: museSettings.binaryPath || "muse",
|
|
82616
|
+
args,
|
|
82617
|
+
cwd: ctx.cwd,
|
|
82618
|
+
environment,
|
|
82619
|
+
onLine: handleLine
|
|
82620
|
+
}).pipe(Effect.provideService(Scope.Scope, turnScope), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.tapError(() => Scope.close(turnScope, Exit.void)));
|
|
82621
|
+
ctx.activeTurn = {
|
|
82622
|
+
turnId,
|
|
82623
|
+
run,
|
|
82624
|
+
state
|
|
82625
|
+
};
|
|
82626
|
+
const outcome = yield* run.awaitOutcome;
|
|
82627
|
+
yield* Scope.close(turnScope, Exit.void);
|
|
82628
|
+
ctx.activeTurn = void 0;
|
|
82629
|
+
if (state.terminal === void 0) if (outcome.kind === "interrupted") yield* publish({
|
|
82630
|
+
type: "turn.aborted",
|
|
82631
|
+
...yield* makeEventStamp(),
|
|
82632
|
+
provider: PROVIDER$1,
|
|
82633
|
+
providerInstanceId: boundInstanceId,
|
|
82634
|
+
threadId: input.threadId,
|
|
82635
|
+
turnId,
|
|
82636
|
+
payload: { reason: "interrupted" }
|
|
82637
|
+
});
|
|
82638
|
+
else {
|
|
82639
|
+
const detail = outcome.kind === "failed" ? outcome.stderr || `\`muse exec\` exited with code ${outcome.exitCode}.` : "`muse exec` ended without a terminal event.";
|
|
82640
|
+
yield* publish({
|
|
82641
|
+
type: "turn.completed",
|
|
82642
|
+
...yield* makeEventStamp(),
|
|
82643
|
+
provider: PROVIDER$1,
|
|
82644
|
+
providerInstanceId: boundInstanceId,
|
|
82645
|
+
threadId: input.threadId,
|
|
82646
|
+
turnId,
|
|
82647
|
+
payload: {
|
|
82648
|
+
state: "failed",
|
|
82649
|
+
errorMessage: detail
|
|
82650
|
+
}
|
|
82651
|
+
});
|
|
82652
|
+
}
|
|
82653
|
+
yield* publishTokenUsage(ctx, turnId);
|
|
82654
|
+
const updatedAt = yield* nowIso;
|
|
82655
|
+
const { activeTurnId: _activeTurnId, ...readySession } = ctx.session;
|
|
82656
|
+
ctx.session = {
|
|
82657
|
+
...readySession,
|
|
82658
|
+
status: "ready",
|
|
82659
|
+
updatedAt
|
|
82660
|
+
};
|
|
82661
|
+
return {
|
|
82662
|
+
threadId: input.threadId,
|
|
82663
|
+
turnId,
|
|
82664
|
+
resumeCursor: museResumeCursor(ctx.museSessionId)
|
|
82665
|
+
};
|
|
82666
|
+
});
|
|
82667
|
+
/**
|
|
82668
|
+
* Token usage never reaches stdout; it is written to the session log. One
|
|
82669
|
+
* read after the turn keeps the counter honest without tailing the file.
|
|
82670
|
+
*/
|
|
82671
|
+
const publishTokenUsage = (ctx, turnId) => Effect.gen(function* () {
|
|
82672
|
+
const usage = yield* readMuseSessionUsage({
|
|
82673
|
+
sessionId: ctx.museSessionId,
|
|
82674
|
+
environment
|
|
82675
|
+
}).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path), Effect.orElseSucceed(() => void 0));
|
|
82676
|
+
if (!usage) return;
|
|
82677
|
+
yield* publish({
|
|
82678
|
+
type: "thread.token-usage.updated",
|
|
82679
|
+
...yield* makeEventStamp(),
|
|
82680
|
+
provider: PROVIDER$1,
|
|
82681
|
+
providerInstanceId: boundInstanceId,
|
|
82682
|
+
threadId: ctx.threadId,
|
|
82683
|
+
turnId,
|
|
82684
|
+
payload: { usage }
|
|
82685
|
+
});
|
|
82686
|
+
}).pipe(Effect.orElseSucceed(() => void 0));
|
|
82687
|
+
const interruptTurn = (threadId, turnId) => Effect.gen(function* () {
|
|
82688
|
+
const active = (yield* requireSession(threadId)).activeTurn;
|
|
82689
|
+
if (!active) return;
|
|
82690
|
+
if (turnId !== void 0 && turnId !== active.turnId) return;
|
|
82691
|
+
yield* active.run.interrupt;
|
|
82692
|
+
});
|
|
82693
|
+
const respondToRequest = (threadId) => Effect.gen(function* () {
|
|
82694
|
+
yield* requireSession(threadId);
|
|
82695
|
+
return yield* new ProviderAdapterRequestError({
|
|
82696
|
+
provider: PROVIDER$1,
|
|
82697
|
+
method: "muse/approval",
|
|
82698
|
+
detail: "Muse Code has no approval channel in headless runs, so it never opens an approval request."
|
|
82699
|
+
});
|
|
82700
|
+
});
|
|
82701
|
+
const respondToUserInput = (threadId) => Effect.gen(function* () {
|
|
82702
|
+
yield* requireSession(threadId);
|
|
82703
|
+
return yield* new ProviderAdapterRequestError({
|
|
82704
|
+
provider: PROVIDER$1,
|
|
82705
|
+
method: "muse/user-input",
|
|
82706
|
+
detail: "Muse Code does not ask structured questions in headless runs."
|
|
82707
|
+
});
|
|
82708
|
+
});
|
|
82709
|
+
const readThread = (threadId) => Effect.gen(function* () {
|
|
82710
|
+
return {
|
|
82711
|
+
threadId,
|
|
82712
|
+
turns: (yield* requireSession(threadId)).turns
|
|
82713
|
+
};
|
|
82714
|
+
});
|
|
82715
|
+
const rollbackThread = (threadId, numTurns) => Effect.gen(function* () {
|
|
82716
|
+
yield* requireSession(threadId);
|
|
82717
|
+
if (!Number.isInteger(numTurns) || numTurns < 1) return yield* new ProviderAdapterValidationError({
|
|
82718
|
+
provider: PROVIDER$1,
|
|
82719
|
+
operation: "rollbackThread",
|
|
82720
|
+
issue: "numTurns must be an integer >= 1."
|
|
82721
|
+
});
|
|
82722
|
+
return yield* new ProviderAdapterRequestError({
|
|
82723
|
+
provider: PROVIDER$1,
|
|
82724
|
+
method: "muse/rollback",
|
|
82725
|
+
detail: "Muse Code exposes no headless rollback for a session."
|
|
82726
|
+
});
|
|
82727
|
+
});
|
|
82728
|
+
const stopSessionInternal = (ctx) => Effect.gen(function* () {
|
|
82729
|
+
if (ctx.stopped) return;
|
|
82730
|
+
ctx.stopped = true;
|
|
82731
|
+
if (ctx.activeTurn) {
|
|
82732
|
+
yield* ctx.activeTurn.run.interrupt.pipe(Effect.ignore);
|
|
82733
|
+
ctx.activeTurn = void 0;
|
|
82734
|
+
}
|
|
82735
|
+
const updatedAt = yield* nowIso;
|
|
82736
|
+
ctx.session = {
|
|
82737
|
+
...ctx.session,
|
|
82738
|
+
status: "closed",
|
|
82739
|
+
updatedAt
|
|
82740
|
+
};
|
|
82741
|
+
sessions.delete(ctx.threadId);
|
|
82742
|
+
yield* publish({
|
|
82743
|
+
type: "session.exited",
|
|
82744
|
+
...yield* makeEventStamp(),
|
|
82745
|
+
provider: PROVIDER$1,
|
|
82746
|
+
providerInstanceId: boundInstanceId,
|
|
82747
|
+
threadId: ctx.threadId,
|
|
82748
|
+
payload: {
|
|
82749
|
+
reason: "stopped",
|
|
82750
|
+
exitKind: "graceful"
|
|
82751
|
+
}
|
|
82752
|
+
});
|
|
82753
|
+
});
|
|
82754
|
+
const stopSession = (threadId) => Effect.flatMap(requireSession(threadId), stopSessionInternal);
|
|
82755
|
+
const listSessions = () => Effect.sync(() => Array.from(sessions.values(), (ctx) => ({ ...ctx.session })));
|
|
82756
|
+
const hasSession = (threadId) => Effect.sync(() => {
|
|
82757
|
+
const ctx = sessions.get(threadId);
|
|
82758
|
+
return ctx !== void 0 && !ctx.stopped;
|
|
82759
|
+
});
|
|
82760
|
+
const stopAll = () => Effect.forEach(Array.from(sessions.values()), stopSessionInternal, { discard: true });
|
|
82761
|
+
yield* Effect.addFinalizer(() => Effect.ignore(stopAll()).pipe(Effect.tap(() => PubSub.shutdown(runtimeEventPubSub))));
|
|
82762
|
+
return {
|
|
82763
|
+
provider: PROVIDER$1,
|
|
82764
|
+
capabilities: { sessionModelSwitch: "in-session" },
|
|
82765
|
+
startSession,
|
|
82766
|
+
sendTurn,
|
|
82767
|
+
interruptTurn,
|
|
82768
|
+
readThread,
|
|
82769
|
+
rollbackThread,
|
|
82770
|
+
respondToRequest,
|
|
82771
|
+
respondToUserInput,
|
|
82772
|
+
stopSession,
|
|
82773
|
+
listSessions,
|
|
82774
|
+
hasSession,
|
|
82775
|
+
stopAll,
|
|
82776
|
+
streamEvents: Stream.fromPubSub(runtimeEventPubSub)
|
|
82777
|
+
};
|
|
82778
|
+
});
|
|
82779
|
+
}
|
|
82780
|
+
//#endregion
|
|
82781
|
+
//#region src/provider/Layers/MuseProvider.ts
|
|
82782
|
+
const MUSE_PRESENTATION = {
|
|
82783
|
+
displayName: "Muse Code",
|
|
82784
|
+
badgeLabel: "Early Access",
|
|
82785
|
+
showInteractionModeToggle: false,
|
|
82786
|
+
requiresNewThreadForModelChange: false,
|
|
82787
|
+
supportedRuntimeModes: ["auto", "full-access"]
|
|
82788
|
+
};
|
|
82789
|
+
const EMPTY_CAPABILITIES = createModelCapabilities({ optionDescriptors: [] });
|
|
82790
|
+
const VERSION_PROBE_TIMEOUT_MS = 4e3;
|
|
82791
|
+
/** Models Meta documents for Muse Code; used until the CLI caches a catalog. */
|
|
82792
|
+
const MUSE_BUILT_IN_MODELS = [{
|
|
82793
|
+
slug: "muse-spark-1.2",
|
|
82794
|
+
name: "Muse Spark 1.2",
|
|
82795
|
+
isCustom: false,
|
|
82796
|
+
capabilities: EMPTY_CAPABILITIES
|
|
82797
|
+
}, {
|
|
82798
|
+
slug: "muse-spark-1.2-contributor",
|
|
82799
|
+
name: "Muse Spark 1.2 (contributor)",
|
|
82800
|
+
isCustom: false,
|
|
82801
|
+
isDefault: true,
|
|
82802
|
+
capabilities: EMPTY_CAPABILITIES
|
|
82803
|
+
}];
|
|
82804
|
+
function museModelsFromSettings(customModels, builtInModels = MUSE_BUILT_IN_MODELS) {
|
|
82805
|
+
return providerModelsFromSettings(builtInModels, customModels ?? [], EMPTY_CAPABILITIES);
|
|
82806
|
+
}
|
|
82807
|
+
const discoverMuseModels = Effect.fn("discoverMuseModels")(function* (environment) {
|
|
82808
|
+
return (yield* readMuseModelCatalog(environment)).map((model) => ({
|
|
82809
|
+
slug: model.slug,
|
|
82810
|
+
name: model.name,
|
|
82811
|
+
isCustom: false,
|
|
82812
|
+
...model.isDefault ? { isDefault: true } : {},
|
|
82813
|
+
capabilities: EMPTY_CAPABILITIES
|
|
82814
|
+
}));
|
|
82815
|
+
});
|
|
82816
|
+
/**
|
|
82817
|
+
* What P4Code can tell about Muse credentials without running the CLI.
|
|
82818
|
+
*
|
|
82819
|
+
* `META_API_KEY` takes priority inside Muse itself, then a stored key or
|
|
82820
|
+
* browser session in `~/.config/muse/auth.json`. Neither says whether the
|
|
82821
|
+
* credential is still valid, so a present credential reports
|
|
82822
|
+
* `authenticated` with the source as its label and a missing one reports
|
|
82823
|
+
* `unauthenticated`.
|
|
82824
|
+
*/
|
|
82825
|
+
const checkMuseAuth = Effect.fn("checkMuseAuth")(function* (environment) {
|
|
82826
|
+
if (environment["META_API_KEY"]?.trim()) return {
|
|
82827
|
+
status: "authenticated",
|
|
82828
|
+
type: "api-key",
|
|
82829
|
+
label: "META_API_KEY"
|
|
82830
|
+
};
|
|
82831
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
82832
|
+
const path = yield* Path.Path;
|
|
82833
|
+
const configHome = environment["XDG_CONFIG_HOME"]?.trim();
|
|
82834
|
+
const authPath = path.join(configHome && configHome.length > 0 ? configHome : path.join(NodeOS.homedir(), ".config"), "muse", "auth.json");
|
|
82835
|
+
return (yield* fileSystem.exists(authPath).pipe(Effect.orElseSucceed(() => false))) ? {
|
|
82836
|
+
status: "authenticated",
|
|
82837
|
+
type: "cli",
|
|
82838
|
+
label: "muse login"
|
|
82839
|
+
} : { status: "unauthenticated" };
|
|
82840
|
+
});
|
|
82841
|
+
function buildInitialMuseProviderSnapshot(museSettings) {
|
|
82842
|
+
return Effect.gen(function* () {
|
|
82843
|
+
const checkedAt = yield* Effect.map(DateTime.now, DateTime.formatIso);
|
|
82844
|
+
const models = museModelsFromSettings(museSettings.customModels);
|
|
82845
|
+
return buildServerProvider({
|
|
82846
|
+
presentation: MUSE_PRESENTATION,
|
|
82847
|
+
enabled: museSettings.enabled,
|
|
82848
|
+
checkedAt,
|
|
82849
|
+
models,
|
|
82850
|
+
probe: museSettings.enabled ? {
|
|
82851
|
+
installed: true,
|
|
82852
|
+
version: null,
|
|
82853
|
+
status: "warning",
|
|
82854
|
+
auth: { status: "unknown" },
|
|
82855
|
+
message: "Checking Muse Code CLI availability..."
|
|
82856
|
+
} : {
|
|
82857
|
+
installed: false,
|
|
82858
|
+
version: null,
|
|
82859
|
+
status: "warning",
|
|
82860
|
+
auth: { status: "unknown" },
|
|
82861
|
+
message: "Muse Code is disabled in P4Code settings."
|
|
82862
|
+
}
|
|
82863
|
+
});
|
|
82864
|
+
});
|
|
82865
|
+
}
|
|
82866
|
+
const runMuseVersionCommand = (museSettings, environment) => Effect.gen(function* () {
|
|
82867
|
+
const command = museSettings.binaryPath || "muse";
|
|
82868
|
+
const spawnCommand = yield* resolveSpawnCommand(command, [...MUSE_VERSION_ARGS], { env: environment });
|
|
82869
|
+
return yield* spawnAndCollect(command, ChildProcess.make(spawnCommand.command, spawnCommand.args, {
|
|
82870
|
+
env: environment,
|
|
82871
|
+
shell: spawnCommand.shell
|
|
82872
|
+
}));
|
|
82873
|
+
});
|
|
82874
|
+
const checkMuseProviderStatus = Effect.fn("checkMuseProviderStatus")(function* (museSettings, environment = process.env) {
|
|
82875
|
+
const checkedAt = DateTime.formatIso(yield* DateTime.now);
|
|
82876
|
+
const fallbackModels = museModelsFromSettings(museSettings.customModels);
|
|
82877
|
+
if (!museSettings.enabled) return buildServerProvider({
|
|
82878
|
+
presentation: MUSE_PRESENTATION,
|
|
82879
|
+
enabled: false,
|
|
82880
|
+
checkedAt,
|
|
82881
|
+
models: fallbackModels,
|
|
82882
|
+
probe: {
|
|
82883
|
+
installed: false,
|
|
82884
|
+
version: null,
|
|
82885
|
+
status: "warning",
|
|
82886
|
+
auth: { status: "unknown" },
|
|
82887
|
+
message: "Muse Code is disabled in P4Code settings."
|
|
82888
|
+
}
|
|
82889
|
+
});
|
|
82890
|
+
const versionResult = yield* runMuseVersionCommand(museSettings, environment).pipe(Effect.timeoutOption(VERSION_PROBE_TIMEOUT_MS), Effect.result);
|
|
82891
|
+
if (Result.isFailure(versionResult)) {
|
|
82892
|
+
const error = versionResult.failure;
|
|
82893
|
+
const missing = isCommandMissingCause(error);
|
|
82894
|
+
yield* (missing ? Effect.logDebug : Effect.logWarning)("Muse Code CLI health check failed.", { errorTag: error._tag });
|
|
82895
|
+
return buildServerProvider({
|
|
82896
|
+
presentation: MUSE_PRESENTATION,
|
|
82897
|
+
enabled: true,
|
|
82898
|
+
checkedAt,
|
|
82899
|
+
models: fallbackModels,
|
|
82900
|
+
probe: {
|
|
82901
|
+
installed: !missing,
|
|
82902
|
+
version: null,
|
|
82903
|
+
status: "error",
|
|
82904
|
+
auth: { status: "unknown" },
|
|
82905
|
+
message: missing ? "Muse Code CLI (`muse`) is not installed or not on PATH. Install it with `curl -fsSL https://dev.meta.ai/install.sh | sh`." : "Failed to execute the Muse Code CLI health check."
|
|
82906
|
+
}
|
|
82907
|
+
});
|
|
82908
|
+
}
|
|
82909
|
+
if (Option.isNone(versionResult.success)) return buildServerProvider({
|
|
82910
|
+
presentation: MUSE_PRESENTATION,
|
|
82911
|
+
enabled: true,
|
|
82912
|
+
checkedAt,
|
|
82913
|
+
models: fallbackModels,
|
|
82914
|
+
probe: {
|
|
82915
|
+
installed: true,
|
|
82916
|
+
version: null,
|
|
82917
|
+
status: "error",
|
|
82918
|
+
auth: { status: "unknown" },
|
|
82919
|
+
message: "Muse Code CLI is installed but timed out while running `muse --version`."
|
|
82920
|
+
}
|
|
82921
|
+
});
|
|
82922
|
+
const versionOutput = versionResult.success.value;
|
|
82923
|
+
const version = parseGenericCliVersion(`${versionOutput.stdout}\n${versionOutput.stderr}`);
|
|
82924
|
+
const auth = yield* checkMuseAuth(environment);
|
|
82925
|
+
if (versionOutput.code !== 0) {
|
|
82926
|
+
yield* Effect.logWarning("Muse Code CLI version probe exited with a non-zero status.", {
|
|
82927
|
+
exitCode: versionOutput.code,
|
|
82928
|
+
stdoutLength: versionOutput.stdout.length,
|
|
82929
|
+
stderrLength: versionOutput.stderr.length
|
|
82930
|
+
});
|
|
82931
|
+
return buildServerProvider({
|
|
82932
|
+
presentation: MUSE_PRESENTATION,
|
|
82933
|
+
enabled: true,
|
|
82934
|
+
checkedAt,
|
|
82935
|
+
models: fallbackModels,
|
|
82936
|
+
probe: {
|
|
82937
|
+
installed: true,
|
|
82938
|
+
version,
|
|
82939
|
+
status: "error",
|
|
82940
|
+
auth,
|
|
82941
|
+
message: "Muse Code CLI is installed but failed to run."
|
|
82942
|
+
}
|
|
82943
|
+
});
|
|
82944
|
+
}
|
|
82945
|
+
const discovered = yield* discoverMuseModels(environment);
|
|
82946
|
+
const models = discovered.length > 0 ? museModelsFromSettings(museSettings.customModels, discovered) : fallbackModels;
|
|
82947
|
+
if (auth.status !== "authenticated") return buildServerProvider({
|
|
82948
|
+
presentation: MUSE_PRESENTATION,
|
|
82949
|
+
enabled: true,
|
|
82950
|
+
checkedAt,
|
|
82951
|
+
models,
|
|
82952
|
+
probe: {
|
|
82953
|
+
installed: true,
|
|
82954
|
+
version,
|
|
82955
|
+
status: "warning",
|
|
82956
|
+
auth,
|
|
82957
|
+
message: "Muse Code is installed but not signed in. Run `muse login`, or set META_API_KEY."
|
|
82958
|
+
}
|
|
82959
|
+
});
|
|
82960
|
+
return buildServerProvider({
|
|
82961
|
+
presentation: MUSE_PRESENTATION,
|
|
82962
|
+
enabled: true,
|
|
82963
|
+
checkedAt,
|
|
82964
|
+
models,
|
|
82965
|
+
probe: {
|
|
82966
|
+
installed: true,
|
|
82967
|
+
version,
|
|
82968
|
+
status: "ready",
|
|
82969
|
+
auth
|
|
82970
|
+
}
|
|
82971
|
+
});
|
|
82972
|
+
});
|
|
82973
|
+
const enrichMuseSnapshot = (input) => enrichProviderSnapshotWithVersionAdvisory(input.snapshot, input.maintenanceCapabilities, { enableProviderUpdateChecks: input.enableProviderUpdateChecks }).pipe(Effect.provideService(HttpClient.HttpClient, input.httpClient), Effect.flatMap((enrichedSnapshot) => input.publishSnapshot(enrichedSnapshot)), Effect.catchCause((cause) => Effect.logWarning("Muse version advisory enrichment failed", { errorTag: causeErrorTag(cause) })), Effect.asVoid);
|
|
82974
|
+
//#endregion
|
|
82975
|
+
//#region src/provider/Drivers/MuseDriver.ts
|
|
82976
|
+
/**
|
|
82977
|
+
* MuseDriver — first-party driver for Meta's Muse Code CLI.
|
|
82978
|
+
*
|
|
82979
|
+
* @module provider/Drivers/MuseDriver
|
|
82980
|
+
*/
|
|
82981
|
+
const decodeMuseSettings = Schema$1.decodeSync(MuseSettings);
|
|
82982
|
+
const DRIVER_KIND$1 = ProviderDriverKind.make("muse");
|
|
82983
|
+
const SNAPSHOT_REFRESH_INTERVAL$1 = Duration.minutes(5);
|
|
82984
|
+
const UPDATE$1 = makeStaticProviderMaintenanceResolver(makeManualOnlyProviderMaintenanceCapabilities({
|
|
82985
|
+
provider: DRIVER_KIND$1,
|
|
82986
|
+
packageName: null
|
|
82987
|
+
}));
|
|
82988
|
+
const withInstanceIdentity$1 = (input) => (snapshot) => ({
|
|
82989
|
+
...snapshot,
|
|
82990
|
+
instanceId: input.instanceId,
|
|
82991
|
+
driver: DRIVER_KIND$1,
|
|
82992
|
+
...input.displayName ? { displayName: input.displayName } : {},
|
|
82993
|
+
...input.accentColor ? { accentColor: input.accentColor } : {},
|
|
82994
|
+
continuation: { groupKey: input.continuationGroupKey }
|
|
82995
|
+
});
|
|
82996
|
+
const MuseDriver = {
|
|
82997
|
+
driverKind: DRIVER_KIND$1,
|
|
82998
|
+
metadata: {
|
|
82999
|
+
displayName: "Muse Code",
|
|
83000
|
+
supportsMultipleInstances: true
|
|
83001
|
+
},
|
|
83002
|
+
configSchema: MuseSettings,
|
|
83003
|
+
defaultConfig: () => decodeMuseSettings({}),
|
|
83004
|
+
create: ({ instanceId, displayName, accentColor, environment, enabled, config }) => Effect.gen(function* () {
|
|
83005
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
83006
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
83007
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
83008
|
+
const path = yield* Path.Path;
|
|
83009
|
+
const serverSettings = yield* ServerSettingsService;
|
|
83010
|
+
const processEnv = mergeProviderInstanceEnvironment(environment);
|
|
83011
|
+
const continuationIdentity = defaultProviderContinuationIdentity({
|
|
83012
|
+
driverKind: DRIVER_KIND$1,
|
|
83013
|
+
instanceId
|
|
83014
|
+
});
|
|
83015
|
+
const stampIdentity = withInstanceIdentity$1({
|
|
83016
|
+
instanceId,
|
|
83017
|
+
displayName,
|
|
83018
|
+
accentColor,
|
|
83019
|
+
continuationGroupKey: continuationIdentity.continuationKey
|
|
83020
|
+
});
|
|
83021
|
+
const effectiveConfig = {
|
|
83022
|
+
...config,
|
|
83023
|
+
enabled
|
|
83024
|
+
};
|
|
83025
|
+
const maintenanceCapabilities = yield* resolveProviderMaintenanceCapabilitiesEffect(UPDATE$1, {
|
|
83026
|
+
binaryPath: effectiveConfig.binaryPath,
|
|
83027
|
+
env: processEnv
|
|
83028
|
+
});
|
|
83029
|
+
const adapter = yield* makeMuseAdapter(effectiveConfig, {
|
|
83030
|
+
environment: processEnv,
|
|
83031
|
+
instanceId
|
|
83032
|
+
});
|
|
83033
|
+
const textGeneration = yield* makeMuseTextGeneration(effectiveConfig, processEnv);
|
|
83034
|
+
const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, serverSettings);
|
|
83035
|
+
const snapshot = yield* makeManagedServerProvider({
|
|
83036
|
+
maintenanceCapabilities,
|
|
83037
|
+
getSettings: snapshotSettings.getSettings,
|
|
83038
|
+
streamSettings: snapshotSettings.streamSettings,
|
|
83039
|
+
haveSettingsChanged: haveProviderSnapshotSettingsChanged,
|
|
83040
|
+
initialSnapshot: (settings) => buildInitialMuseProviderSnapshot(settings.provider).pipe(Effect.map(stampIdentity)),
|
|
83041
|
+
checkProvider: checkMuseProviderStatus(effectiveConfig, processEnv).pipe(Effect.map(stampIdentity), Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path)),
|
|
83042
|
+
enrichSnapshot: ({ settings, snapshot: currentSnapshot, publishSnapshot }) => enrichMuseSnapshot({
|
|
83043
|
+
snapshot: currentSnapshot,
|
|
83044
|
+
maintenanceCapabilities,
|
|
83045
|
+
enableProviderUpdateChecks: settings.enableProviderUpdateChecks,
|
|
83046
|
+
publishSnapshot,
|
|
83047
|
+
httpClient
|
|
83048
|
+
}),
|
|
81050
83049
|
refreshInterval: SNAPSHOT_REFRESH_INTERVAL$1
|
|
81051
83050
|
}).pipe(Effect.mapError((cause) => new ProviderDriverError({
|
|
81052
83051
|
driver: DRIVER_KIND$1,
|
|
81053
83052
|
instanceId,
|
|
81054
|
-
detail: `Failed to build
|
|
83053
|
+
detail: `Failed to build Muse snapshot: ${cause.message ?? String(cause)}`,
|
|
81055
83054
|
cause
|
|
81056
83055
|
})));
|
|
81057
83056
|
return {
|
|
@@ -82737,6 +84736,7 @@ const BUILT_IN_DRIVERS = [
|
|
|
82737
84736
|
ClaudeDriver,
|
|
82738
84737
|
CursorDriver,
|
|
82739
84738
|
GrokDriver,
|
|
84739
|
+
MuseDriver,
|
|
82740
84740
|
{
|
|
82741
84741
|
driverKind: DRIVER_KIND,
|
|
82742
84742
|
metadata: {
|
|
@@ -84135,6 +86135,24 @@ function runtimeEventToActivities(event, taskTitle) {
|
|
|
84135
86135
|
turnId: toTurnId$1(event.turnId) ?? null,
|
|
84136
86136
|
...maybeSequence
|
|
84137
86137
|
}];
|
|
86138
|
+
case "code.published": {
|
|
86139
|
+
const label = event.payload.repo ? event.payload.identifier ? `${event.payload.repo}#${event.payload.identifier}` : event.payload.repo : event.payload.url;
|
|
86140
|
+
return [{
|
|
86141
|
+
id: event.eventId,
|
|
86142
|
+
createdAt: event.createdAt,
|
|
86143
|
+
tone: "info",
|
|
86144
|
+
kind: "code.published",
|
|
86145
|
+
summary: `Published ${label}`,
|
|
86146
|
+
payload: {
|
|
86147
|
+
hostingProvider: event.payload.hostingProvider,
|
|
86148
|
+
url: event.payload.url,
|
|
86149
|
+
...event.payload.repo ? { repo: event.payload.repo } : {},
|
|
86150
|
+
...event.payload.identifier ? { identifier: event.payload.identifier } : {}
|
|
86151
|
+
},
|
|
86152
|
+
turnId: toTurnId$1(event.turnId) ?? null,
|
|
86153
|
+
...maybeSequence
|
|
86154
|
+
}];
|
|
86155
|
+
}
|
|
84138
86156
|
case "turn.plan.updated": return [{
|
|
84139
86157
|
id: event.eventId,
|
|
84140
86158
|
createdAt: event.createdAt,
|
|
@@ -87266,6 +89284,7 @@ const reportUpdateAvailable = Effect.gen(function* () {
|
|
|
87266
89284
|
const makeCli = () => Command.make("p4c", { ...sharedServerCommandFlags }).pipe(Command.withDescription("Run the P4Code server."), Command.withHandler((flags) => runServerCommand(flags)), Command.withSubcommands([
|
|
87267
89285
|
startCommand,
|
|
87268
89286
|
serveCommand,
|
|
89287
|
+
agentsCommand,
|
|
87269
89288
|
authCommand,
|
|
87270
89289
|
hubCommand,
|
|
87271
89290
|
mcpCommand,
|