@akanjs/cli 3.0.0-beta.16 → 3.0.0-beta.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/.build-stamp +1 -1
- package/{CodeAgent-zgccwajm.js → CodeAgent-0035exh3.js} +2 -2
- package/{CodeTui-993tx7gf.js → CodeTui-eweg1hep.js} +384 -28
- package/{agent.command-n664dtcv.js → agent.command-65zqfkg0.js} +6 -6
- package/akanCode.js +11 -9
- package/{application.command-t5542jte.js → application.command-2wcj5da3.js} +18 -11
- package/{applicationBuildRunner-x4gbfn7y.js → applicationBuildRunner-m0hhgwsx.js} +6 -5
- package/{applicationReleasePackager-z61ep017.js → applicationReleasePackager-vykfzgx3.js} +1 -1
- package/buildBatch.proc.js +2 -2
- package/{capacitorApp-shv3ajh5.js → capacitorApp-2daqhb5h.js} +2 -2
- package/{cloud.command-4x35k1z6.js → cloud.command-960bgbf4.js} +8 -8
- package/{cloud.script-3k8nbbpn.js → cloud.script-qhcnqt1n.js} +8 -8
- package/{code.command-mrfb7a1g.js → code.command-4feh3ef7.js} +7 -7
- package/{context.command-pqsm5jrk.js → context.command-kzehkx48.js} +14 -14
- package/context.runner-z0rrcz7b.js +22 -0
- package/{devTui-gy6mendg.js → devTui-ffjsarqa.js} +3 -3
- package/{devTuiView-y1548wy6.js → devTuiView-1q1mdgrv.js} +1 -1
- package/{guideline.command-rpc2emw8.js → guideline.command-3w2g20x4.js} +8 -13
- package/guidelines/conventions/conventions.instruction.md +37 -22
- package/guidelines/queryRule/queryRule.instruction.md +1 -1
- package/guidelines/ssrRule/ssrRule.instruction.md +4 -2
- package/guidelines/transportRule/transportRule.instruction.md +10 -8
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +4 -3
- package/incrementalBuilder.proc.js +1 -1
- package/{index-sxg87a8f.js → index-14zrtgfa.js} +164 -98
- package/{index-57p9p179.js → index-3hpxeys1.js} +6 -5
- package/{index-sn7dnzq4.js → index-4r42ext5.js} +1 -1
- package/{index-3mg4h788.js → index-6q589y9t.js} +2 -2
- package/index-9dp031by.js +12 -0
- package/{index-p4p9t45x.js → index-bwxpk2cj.js} +4 -18
- package/{index-g9zghs4h.js → index-cjw1sj1x.js} +12 -12
- package/{index-te0y1c0m.js → index-d9fjz06h.js} +12 -7
- package/{index-4n4821gp.js → index-dw943k93.js} +4 -4
- package/{index-4cvpzkq6.js → index-ef228gzf.js} +3 -3
- package/{index-3cymrzsb.js → index-erfc1x40.js} +17 -3
- package/{index-ns128c4q.js → index-h94faz96.js} +13 -20
- package/index-jd01kj5r.js +656 -0
- package/{index-5405kc0x.js → index-jwexzh42.js} +363 -247
- package/{index-wfwknnje.js → index-jwrpsrg3.js} +8 -6
- package/{index-kvzjda3v.js → index-m41ptvat.js} +5 -5
- package/{index-qh7brn20.js → index-mt2154s2.js} +6 -2
- package/{index-xjc7gg65.js → index-pn8mhpps.js} +23 -33
- package/{index-vpx68e4x.js → index-q9c3c0hy.js} +2 -2
- package/{index-v17gj504.js → index-rpgxs245.js} +2 -2
- package/{index-twq9p2sp.js → index-s5rhm04y.js} +0 -6
- package/{index-018dszb3.js → index-sbfk9daf.js} +10 -3
- package/{index-zg0v1zte.js → index-wcg582a8.js} +5 -5
- package/{index-6na0sd2b.js → index-ycy302an.js} +5 -2
- package/{index-qhtj9y6j.js → index-yw6g3bbv.js} +74 -21
- package/{index-2pm1j01n.js → index-zmj143x7.js} +4 -4
- package/{index-zn8qgeqx.js → index-zq0mr3hh.js} +1 -1
- package/index.js +21 -21
- package/{library.command-m8p2r1dh.js → library.command-fp0nhr62.js} +3 -3
- package/{localRegistry.command-tt00nwwg.js → localRegistry.command-8y0ecxb9.js} +7 -7
- package/{module.command-mfzvqt0n.js → module.command-kqkn48t2.js} +5 -5
- package/{package.command-8sy6jjwy.js → package.command-qverjh22.js} +4 -4
- package/package.json +8 -6
- package/{page.command-taenb02b.js → page.command-94bwhd0g.js} +3 -3
- package/{primitive.command-7kpncmkk.js → primitive.command-2ydvtah2.js} +6 -6
- package/{prompter-9mn3kp36.js → prompter-1f8d9pcc.js} +1 -1
- package/{quality.command-1ar94hrw.js → quality.command-fa0gth4p.js} +3 -4
- package/{repair.command-n08t6j8p.js → repair.command-4qng2hpm.js} +5 -5
- package/{routeSourceValidator-apyqw7wk.js → routeSourceValidator-7ycnfz0e.js} +0 -6
- package/{scalar.command-mq8qfp4t.js → scalar.command-pb2y95sh.js} +4 -4
- package/skills/akan-code/SKILL.md +122 -0
- package/skills/akan-module/SKILL.md +2 -2
- package/skills/akan-store/SKILL.md +6 -2
- package/{subspace.command-x9897gj4.js → subspace.command-vkxmwp74.js} +4 -4
- package/templates/__scalar/__model__/__model__.abstract.md.template +8 -0
- package/templates/appSample/lib/__scalar/workHistory/workHistory.abstract.ts +8 -39
- package/templates/appSample/lib/_noti/noti.abstract.ts +6 -29
- package/templates/appSample/lib/task/task.abstract.ts +12 -32
- package/templates/appSample/lib/task/task.signal.spec.ts +15 -18
- package/templates/appSample/lib/task/task.signal.test.ts +14 -0
- package/templates/appSample/lib/task/task.signal.ts +1 -1
- package/templates/appSample/srvkit/AuthGuard.ts +4 -1
- package/templates/appSample/srvkit/SessionInternalArg.ts +3 -1
- package/templates/crudPages/[__model__Id]/edit/page.tsx +2 -1
- package/templates/crudPages/[__model__Id]/page.tsx +2 -1
- package/templates/crudPages/new/page.tsx +2 -1
- package/templates/crudPages/page.tsx +2 -1
- package/templates/crudSinglePage/page.tsx +2 -1
- package/templates/module/__model__.signal.ts +12 -4
- package/templates/pkgRoot/tsconfig.json.template +2 -7
- package/templates/service/__model__.abstract.md.template +8 -0
- package/{tunnel.command-ht2es78c.js → tunnel.command-02cvp9nf.js} +2 -2
- package/{workflow.command-tvz4nm9n.js → workflow.command-132txnrh.js} +9 -9
- package/{workspace.command-ayqa1rgt.js → workspace.command-d5wkswek.js} +21 -21
- package/context.runner-bys6ykcp.js +0 -22
- package/guidelines/agentRule/agentRule.generate.json +0 -31
- package/guidelines/componentRule/componentRule.generate.json +0 -31
- package/guidelines/conventions/conventions.generate.json +0 -25
- package/guidelines/cssRule/cssRule.generate.json +0 -31
- package/guidelines/docPageRule/docPageRule.generate.json +0 -31
- package/guidelines/docPageRule/docPageRule.instruction.md +0 -25
- package/guidelines/docSyncRule/docSyncRule.generate.json +0 -31
- package/guidelines/docSyncRule/docSyncRule.instruction.md +0 -25
- package/guidelines/enumConstant/enumConstant.generate.json +0 -31
- package/guidelines/enumConstant/enumConstant.instruction.md +0 -28
- package/guidelines/fieldRule/fieldRule.generate.json +0 -31
- package/guidelines/framework/framework.generate.json +0 -32
- package/guidelines/mcpRule/mcpRule.generate.json +0 -35
- package/guidelines/modelConstant/modelConstant.generate.json +0 -32
- package/guidelines/modelDictionary/modelDictionary.generate.json +0 -32
- package/guidelines/modelDocument/modelDocument.generate.json +0 -31
- package/guidelines/modelService/modelService.generate.json +0 -31
- package/guidelines/modelSignal/modelSignal.generate.json +0 -31
- package/guidelines/modelStore/modelStore.generate.json +0 -31
- package/guidelines/modelTemplate/modelTemplate.generate.json +0 -31
- package/guidelines/modelUnit/modelUnit.generate.json +0 -31
- package/guidelines/modelUtil/modelUtil.generate.json +0 -31
- package/guidelines/modelView/modelView.generate.json +0 -31
- package/guidelines/modelZone/modelZone.generate.json +0 -31
- package/guidelines/moduleCodegen/moduleCodegen.generate.json +0 -31
- package/guidelines/moduleCodegen/moduleCodegen.instruction.md +0 -28
- package/guidelines/moduleOverview/moduleOverview.generate.json +0 -31
- package/guidelines/queryRule/queryRule.generate.json +0 -31
- package/guidelines/recipeRule/recipeRule.generate.json +0 -31
- package/guidelines/runtimeRule/runtimeRule.generate.json +0 -25
- package/guidelines/scalarConstant/scalarConstant.generate.json +0 -32
- package/guidelines/scalarDictionary/scalarDictionary.generate.json +0 -32
- package/guidelines/scalarModule/scalarModule.generate.json +0 -31
- package/guidelines/sharedUiUsage/sharedUiUsage.generate.json +0 -31
- package/guidelines/sharedUiUsage/sharedUiUsage.instruction.md +0 -25
- package/guidelines/ssrRule/ssrRule.generate.json +0 -31
- package/guidelines/transportRule/transportRule.generate.json +0 -31
- package/guidelines/utilUiUsage/utilUiUsage.generate.json +0 -31
- package/guidelines/utilUiUsage/utilUiUsage.instruction.md +0 -25
- package/guidelines/workspaceOnboarding/workspaceOnboarding.generate.json +0 -24
- package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +0 -34
- package/index-jgrnpt6y.js +0 -121
- package/templates/__scalar/__model__/__model__.abstract.md +0 -28
- package/templates/module/__model__.abstract.md +0 -28
- package/templates/service/__model__.abstract.md +0 -28
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
|
-
SubagentPool2
|
|
4
|
-
|
|
3
|
+
SubagentPool2,
|
|
4
|
+
akanCodePaths2,
|
|
5
|
+
McpServerConfig,
|
|
6
|
+
McpSignIn
|
|
7
|
+
} from "./index-jd01kj5r.js";
|
|
5
8
|
import {
|
|
6
9
|
stripAnsi
|
|
7
10
|
} from "./index-7md2g9vw.js";
|
|
@@ -9,7 +12,7 @@ import {
|
|
|
9
12
|
akanCodeModel2,
|
|
10
13
|
akanCodeModelSupportsImages2,
|
|
11
14
|
akanCodeModelWarnings2
|
|
12
|
-
} from "./index-
|
|
15
|
+
} from "./index-jwrpsrg3.js";
|
|
13
16
|
|
|
14
17
|
// pkgs/@akanjs/devkit/codeAgent/agent/CodeAgent.ts
|
|
15
18
|
import {
|
|
@@ -511,7 +514,7 @@ class AkanToolPack2 {
|
|
|
511
514
|
return { name: "akan-tools", factory: async (pi) => await this.#register(pi) };
|
|
512
515
|
}
|
|
513
516
|
async names() {
|
|
514
|
-
const { ContextRunner } = await import("./context.runner-
|
|
517
|
+
const { ContextRunner } = await import("./context.runner-z0rrcz7b.js");
|
|
515
518
|
return [
|
|
516
519
|
...new ContextRunner().listMcpTools(this.#options.mode).map((tool) => tool.name),
|
|
517
520
|
AkanToolPack2.verifyToolName
|
|
@@ -519,7 +522,7 @@ class AkanToolPack2 {
|
|
|
519
522
|
}
|
|
520
523
|
static verifyToolName = "akan_verify";
|
|
521
524
|
async#register(pi) {
|
|
522
|
-
const { ContextRunner } = await import("./context.runner-
|
|
525
|
+
const { ContextRunner } = await import("./context.runner-z0rrcz7b.js");
|
|
523
526
|
const runner = new ContextRunner;
|
|
524
527
|
const guidelineNames = await AkanToolPack2.#guidelineNames();
|
|
525
528
|
for (const tool of runner.listMcpTools(this.#options.mode, { guidelineNames })) {
|
|
@@ -561,7 +564,7 @@ class AkanToolPack2 {
|
|
|
561
564
|
}
|
|
562
565
|
static async#guidelineNames() {
|
|
563
566
|
try {
|
|
564
|
-
const { Prompter } = await import("./prompter-
|
|
567
|
+
const { Prompter } = await import("./prompter-1f8d9pcc.js");
|
|
565
568
|
return await Prompter.listGuidelines();
|
|
566
569
|
} catch {
|
|
567
570
|
return [];
|
|
@@ -574,9 +577,78 @@ class AkanToolPack2 {
|
|
|
574
577
|
}
|
|
575
578
|
}
|
|
576
579
|
|
|
577
|
-
// pkgs/@akanjs/devkit/codeAgent/tools/
|
|
580
|
+
// pkgs/@akanjs/devkit/codeAgent/tools/AskToolPack.ts
|
|
578
581
|
import { Type as Type2 } from "typebox";
|
|
579
582
|
|
|
583
|
+
class AskToolPack {
|
|
584
|
+
static toolName = "ask_user";
|
|
585
|
+
static maxQuestions = 4;
|
|
586
|
+
#options;
|
|
587
|
+
constructor(options) {
|
|
588
|
+
this.#options = options;
|
|
589
|
+
}
|
|
590
|
+
names() {
|
|
591
|
+
return this.#options.profile.ui.canPrompt ? [AskToolPack.toolName] : [];
|
|
592
|
+
}
|
|
593
|
+
extension() {
|
|
594
|
+
if (!this.#options.profile.ui.canPrompt)
|
|
595
|
+
return;
|
|
596
|
+
return { name: "akan-ask", factory: (pi) => this.#register(pi) };
|
|
597
|
+
}
|
|
598
|
+
#register(pi) {
|
|
599
|
+
pi.registerTool({
|
|
600
|
+
name: AskToolPack.toolName,
|
|
601
|
+
label: "Ask",
|
|
602
|
+
description: "Ask the user to decide something you cannot decide from the code or the request. Use it when two readings of the task would lead to materially different work, not for choices with an obvious default \u2014 and never to ask permission for work you were already asked to do. Offer concrete options whenever you can name them; mark one recommended when you have a view.",
|
|
603
|
+
promptSnippet: "ask_user: put a decision the user owns back to them, with options",
|
|
604
|
+
parameters: Type2.Object({
|
|
605
|
+
questions: Type2.Array(Type2.Object({
|
|
606
|
+
question: Type2.String({ description: "The whole question, as one sentence ending in a question mark." }),
|
|
607
|
+
header: Type2.Optional(Type2.String({ description: "Two or three words naming what is being chosen, e.g. 'Auth method'." })),
|
|
608
|
+
options: Type2.Optional(Type2.Array(Type2.Object({
|
|
609
|
+
label: Type2.String({ description: "The choice, in one to five words." }),
|
|
610
|
+
detail: Type2.Optional(Type2.String({ description: "What picking it means, or the trade-off it carries." })),
|
|
611
|
+
recommended: Type2.Optional(Type2.Boolean({ description: "Mark at most one. It is offered first and labelled as such." }))
|
|
612
|
+
}), { description: "Two to four distinct choices. Leave it out to ask for an answer in prose." })),
|
|
613
|
+
multiSelect: Type2.Optional(Type2.Boolean({ description: "Several options may be chosen together, rather than exactly one." })),
|
|
614
|
+
freeText: Type2.Optional(Type2.Boolean({
|
|
615
|
+
description: "Offer a further choice that lets the user type an answer of their own. Default true."
|
|
616
|
+
}))
|
|
617
|
+
}), { description: `One to ${AskToolPack.maxQuestions} questions, asked in order.` })
|
|
618
|
+
}),
|
|
619
|
+
execute: async (_id, params) => {
|
|
620
|
+
const questions = (params.questions ?? []).slice(0, AskToolPack.maxQuestions);
|
|
621
|
+
if (!questions.length)
|
|
622
|
+
return { content: [{ type: "text", text: "No question was asked." }], details: undefined, isError: true };
|
|
623
|
+
const answers = [];
|
|
624
|
+
for (const entry of questions) {
|
|
625
|
+
const options = (entry.options ?? []).map((option, at) => ({
|
|
626
|
+
key: `${at}`,
|
|
627
|
+
label: option.label,
|
|
628
|
+
...option.detail ? { detail: option.detail } : {},
|
|
629
|
+
...option.recommended ? { recommended: true } : {}
|
|
630
|
+
}));
|
|
631
|
+
const answer = await this.#options.ask({
|
|
632
|
+
prompt: entry.header ? `${entry.header} \u2014 ${entry.question}` : entry.question,
|
|
633
|
+
kind: options.length ? "select" : "text",
|
|
634
|
+
...options.length ? { options } : {},
|
|
635
|
+
...entry.multiSelect ? { multiSelect: true } : {},
|
|
636
|
+
freeText: entry.freeText ?? true
|
|
637
|
+
});
|
|
638
|
+
answers.push(`${entry.question}
|
|
639
|
+
\u2192 ${answer ?? "(skipped \u2014 decide this yourself and say what you chose)"}`);
|
|
640
|
+
}
|
|
641
|
+
return { content: [{ type: "text", text: answers.join(`
|
|
642
|
+
|
|
643
|
+
`) }], details: undefined };
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// pkgs/@akanjs/devkit/codeAgent/tools/MailToolPack.ts
|
|
650
|
+
import { Type as Type3 } from "typebox";
|
|
651
|
+
|
|
580
652
|
class MailToolPack2 {
|
|
581
653
|
static toolNames = ["list_peers", "send_peer_message"];
|
|
582
654
|
#options;
|
|
@@ -597,7 +669,7 @@ class MailToolPack2 {
|
|
|
597
669
|
label: "List peer sessions",
|
|
598
670
|
description: "List the other akan code sessions running in this workspace right now, with their name and working directory. Call it before send_peer_message so you name a session that is actually there.",
|
|
599
671
|
promptSnippet: "list_peers: the other akan code sessions running here",
|
|
600
|
-
parameters:
|
|
672
|
+
parameters: Type3.Object({}),
|
|
601
673
|
execute: async () => ({
|
|
602
674
|
content: [{ type: "text", text: MailToolPack2.#render(this.#options.mailbox()?.peers() ?? []) }],
|
|
603
675
|
details: undefined
|
|
@@ -608,9 +680,9 @@ class MailToolPack2 {
|
|
|
608
680
|
label: "Message a peer session",
|
|
609
681
|
description: "Hand a message to another akan code session in this workspace. It arrives there as a prompt, so it costs that session a turn \u2014 use it to hand over a piece of work or to warn it about a change you made, not to chat. Say what you want done and what you already did.",
|
|
610
682
|
promptSnippet: "send_peer_message: hand work to another akan code session",
|
|
611
|
-
parameters:
|
|
612
|
-
to:
|
|
613
|
-
message:
|
|
683
|
+
parameters: Type3.Object({
|
|
684
|
+
to: Type3.String({ description: "The peer's name, or the front of its id, from list_peers." }),
|
|
685
|
+
message: Type3.String({ description: "What that session should know or do." })
|
|
614
686
|
}),
|
|
615
687
|
execute: async (_id, params) => {
|
|
616
688
|
const mailbox = this.#options.mailbox();
|
|
@@ -639,43 +711,36 @@ class MailToolPack2 {
|
|
|
639
711
|
}
|
|
640
712
|
|
|
641
713
|
// pkgs/@akanjs/devkit/codeAgent/tools/McpToolPack.ts
|
|
642
|
-
import {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
import { existsSync as existsSync4 } from "fs";
|
|
648
|
-
import { homedir as homedir2 } from "os";
|
|
649
|
-
import path4 from "path";
|
|
650
|
-
var builtinSkillsDir = () => {
|
|
651
|
-
const candidates = [path4.join(import.meta.dir, "skills"), path4.join(import.meta.dir, "..", "skills")];
|
|
652
|
-
return candidates.find((candidate) => existsSync4(candidate));
|
|
653
|
-
};
|
|
654
|
-
var akanCodePaths2 = {
|
|
655
|
-
workspaceDir: (workspaceRoot) => path4.join(workspaceRoot, ".akan", "code"),
|
|
656
|
-
sessionsDir: (workspaceRoot) => path4.join(workspaceRoot, ".akan", "code", "sessions"),
|
|
657
|
-
extensionsDir: (workspaceRoot) => path4.join(workspaceRoot, ".akan", "code", "extensions"),
|
|
658
|
-
skillsDir: (workspaceRoot) => path4.join(workspaceRoot, ".akan", "code", "skills"),
|
|
659
|
-
mailDir: (workspaceRoot) => path4.join(workspaceRoot, ".akan", "code", "mail"),
|
|
660
|
-
builtinSkillsDir,
|
|
661
|
-
mcpFile: (workspaceRoot) => path4.join(workspaceRoot, ".akan", "code", "mcp.json"),
|
|
662
|
-
globalDir: () => path4.join(homedir2(), ".akan", "code"),
|
|
663
|
-
authFile: () => path4.join(homedir2(), ".akan", "code", "auth.json"),
|
|
664
|
-
modelsFile: () => path4.join(homedir2(), ".akan", "code", "models.json")
|
|
665
|
-
};
|
|
714
|
+
import {
|
|
715
|
+
codeAgentClip,
|
|
716
|
+
codeAgentOutputChars
|
|
717
|
+
} from "akanjs/common";
|
|
718
|
+
import { Type as Type4 } from "typebox";
|
|
666
719
|
|
|
667
720
|
// pkgs/@akanjs/devkit/codeAgent/tools/McpClient.ts
|
|
668
721
|
var protocolVersion = "2025-06-18";
|
|
669
722
|
|
|
723
|
+
class McpUnauthorized2 extends Error {
|
|
724
|
+
challenge;
|
|
725
|
+
constructor(name, challenge) {
|
|
726
|
+
super(`MCP server "${name}" requires authentication`);
|
|
727
|
+
this.name = "McpUnauthorized";
|
|
728
|
+
this.challenge = challenge;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
670
732
|
class McpClient2 {
|
|
733
|
+
static maxToolPages = 50;
|
|
671
734
|
ref;
|
|
672
735
|
#proc;
|
|
673
736
|
#nextId = 0;
|
|
674
737
|
#buffer = "";
|
|
675
738
|
#pending = new Map;
|
|
676
739
|
#sessionId;
|
|
677
|
-
|
|
740
|
+
#token;
|
|
741
|
+
constructor(ref, token) {
|
|
678
742
|
this.ref = ref;
|
|
743
|
+
this.#token = token;
|
|
679
744
|
}
|
|
680
745
|
async connect() {
|
|
681
746
|
if (this.ref.transport === "stdio")
|
|
@@ -691,10 +756,19 @@ class McpClient2 {
|
|
|
691
756
|
return this;
|
|
692
757
|
}
|
|
693
758
|
async listTools() {
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
759
|
+
const tools = [];
|
|
760
|
+
let cursor;
|
|
761
|
+
for (let page = 0;page < McpClient2.maxToolPages; page += 1) {
|
|
762
|
+
const response = await this.#call("tools/list", cursor ? { cursor } : {});
|
|
763
|
+
if (response.error)
|
|
764
|
+
throw new Error(response.error.message);
|
|
765
|
+
tools.push(...response.result?.tools ?? []);
|
|
766
|
+
const next = response.result?.nextCursor;
|
|
767
|
+
if (!next || next === cursor)
|
|
768
|
+
return tools;
|
|
769
|
+
cursor = next;
|
|
770
|
+
}
|
|
771
|
+
return tools;
|
|
698
772
|
}
|
|
699
773
|
async callTool(name, args) {
|
|
700
774
|
const response = await this.#call("tools/call", { name, arguments: args });
|
|
@@ -778,11 +852,16 @@ class McpClient2 {
|
|
|
778
852
|
headers: {
|
|
779
853
|
"content-type": "application/json",
|
|
780
854
|
accept: "application/json, text/event-stream",
|
|
855
|
+
"mcp-protocol-version": protocolVersion,
|
|
856
|
+
...this.ref.headers,
|
|
857
|
+
...this.#token ? { authorization: `Bearer ${this.#token}` } : {},
|
|
781
858
|
...this.#sessionId ? { "mcp-session-id": this.#sessionId } : {}
|
|
782
859
|
},
|
|
783
860
|
body: JSON.stringify(body),
|
|
784
861
|
signal: AbortSignal.timeout(30000)
|
|
785
862
|
});
|
|
863
|
+
if (response.status === 401)
|
|
864
|
+
throw new McpUnauthorized2(this.ref.name, response.headers.get("www-authenticate") ?? "");
|
|
786
865
|
this.#sessionId = response.headers.get("mcp-session-id") ?? this.#sessionId;
|
|
787
866
|
const text = await response.text();
|
|
788
867
|
if (!text.trim())
|
|
@@ -804,29 +883,44 @@ class McpClient2 {
|
|
|
804
883
|
|
|
805
884
|
// pkgs/@akanjs/devkit/codeAgent/tools/McpToolPack.ts
|
|
806
885
|
class McpToolPack2 {
|
|
807
|
-
#options;
|
|
808
886
|
#clients = [];
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
}
|
|
887
|
+
#status = [];
|
|
888
|
+
constructor() {}
|
|
812
889
|
static async connect(options) {
|
|
813
890
|
const refs = McpToolPack2.#refs(options);
|
|
814
891
|
if (!refs.length)
|
|
815
892
|
return;
|
|
816
|
-
const pack = new McpToolPack2
|
|
893
|
+
const pack = new McpToolPack2;
|
|
817
894
|
for (const ref of refs) {
|
|
895
|
+
const base = { name: ref.name, transport: ref.transport, target: McpServerConfig.targetOf(ref) };
|
|
896
|
+
const token = ref.transport === "http" ? await McpSignIn.token(ref, options.onNotice) : undefined;
|
|
818
897
|
try {
|
|
819
|
-
const client = await new McpClient2(ref).connect();
|
|
820
|
-
|
|
898
|
+
const client = await new McpClient2(ref, token).connect();
|
|
899
|
+
const tools = await client.listTools();
|
|
900
|
+
pack.#clients.push({ client, tools });
|
|
901
|
+
pack.#status.push({
|
|
902
|
+
...base,
|
|
903
|
+
tools: tools.map((tool) => McpToolPack2.#nameOf(ref.name, tool.name)),
|
|
904
|
+
auth: token ? "authorized" : "none"
|
|
905
|
+
});
|
|
821
906
|
} catch (error) {
|
|
907
|
+
if (error instanceof McpUnauthorized2) {
|
|
908
|
+
options.onNotice?.(`MCP server "${ref.name}" needs signing in \u2014 /mcp login ${ref.name}`);
|
|
909
|
+
pack.#status.push({ ...base, tools: [], auth: "required" });
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
822
912
|
options.onNotice?.(`MCP server "${ref.name}" is unavailable: ${String(error)}`);
|
|
913
|
+
pack.#status.push({ ...base, tools: [], error: String(error) });
|
|
823
914
|
}
|
|
824
915
|
}
|
|
825
|
-
return pack
|
|
916
|
+
return pack;
|
|
826
917
|
}
|
|
827
918
|
get toolNames() {
|
|
828
919
|
return this.#clients.flatMap(({ client, tools }) => tools.map((tool) => McpToolPack2.#nameOf(client.ref.name, tool.name)));
|
|
829
920
|
}
|
|
921
|
+
get status() {
|
|
922
|
+
return this.#status;
|
|
923
|
+
}
|
|
830
924
|
extension() {
|
|
831
925
|
return { name: "akan-mcp", factory: (pi) => this.#register(pi) };
|
|
832
926
|
}
|
|
@@ -844,7 +938,7 @@ class McpToolPack2 {
|
|
|
844
938
|
label: `${client.ref.name}: ${tool.name}`,
|
|
845
939
|
description,
|
|
846
940
|
promptSnippet: `${name}: ${description.split(".")[0] ?? description}`,
|
|
847
|
-
parameters:
|
|
941
|
+
parameters: Type4.Unsafe(tool.inputSchema),
|
|
848
942
|
execute: async (_id, params) => {
|
|
849
943
|
const result = await client.callTool(tool.name, params ?? {});
|
|
850
944
|
return {
|
|
@@ -863,36 +957,14 @@ class McpToolPack2 {
|
|
|
863
957
|
if (options.profile.tools.mcp === "off")
|
|
864
958
|
return [];
|
|
865
959
|
const declared = Array.isArray(options.profile.tools.mcp) ? options.profile.tools.mcp : [];
|
|
866
|
-
return [...
|
|
867
|
-
}
|
|
868
|
-
static #fromFile(workspaceRoot) {
|
|
869
|
-
const file = akanCodePaths2.mcpFile(workspaceRoot);
|
|
870
|
-
if (!existsSync5(file))
|
|
871
|
-
return [];
|
|
872
|
-
try {
|
|
873
|
-
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
874
|
-
const entries = Object.entries(parsed.mcpServers ?? parsed.servers ?? {});
|
|
875
|
-
return entries.filter(([, entry]) => !entry.disabled).map(([name, entry]) => {
|
|
876
|
-
const transport = entry.url ? "http" : "stdio";
|
|
877
|
-
return {
|
|
878
|
-
name,
|
|
879
|
-
transport,
|
|
880
|
-
...entry.command ? { command: entry.command } : {},
|
|
881
|
-
...entry.args ? { args: entry.args } : {},
|
|
882
|
-
...entry.url ? { url: entry.url } : {},
|
|
883
|
-
...entry.env ? { env: entry.env } : {}
|
|
884
|
-
};
|
|
885
|
-
});
|
|
886
|
-
} catch {
|
|
887
|
-
return [];
|
|
888
|
-
}
|
|
960
|
+
return [...McpServerConfig.read(options.workspaceRoot), ...declared];
|
|
889
961
|
}
|
|
890
962
|
}
|
|
891
963
|
|
|
892
964
|
// pkgs/@akanjs/devkit/codeAgent/tools/SessionToolPack.ts
|
|
893
965
|
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
894
966
|
import { codeAgentClip as codeAgentClip2, codeAgentOutputChars as codeAgentOutputChars2 } from "akanjs/common";
|
|
895
|
-
import { Type as
|
|
967
|
+
import { Type as Type5 } from "typebox";
|
|
896
968
|
var snippetChars = 240;
|
|
897
969
|
|
|
898
970
|
class SessionToolPack2 {
|
|
@@ -915,9 +987,9 @@ class SessionToolPack2 {
|
|
|
915
987
|
label: "Search sessions",
|
|
916
988
|
description: "Search earlier coding-agent conversations in this workspace for a phrase, and get back the matching sessions with a snippet. Use it when the user refers to something decided before, or when you suspect this problem has been solved here already.",
|
|
917
989
|
promptSnippet: "session_search: find earlier conversations in this workspace by phrase",
|
|
918
|
-
parameters:
|
|
919
|
-
query:
|
|
920
|
-
limit:
|
|
990
|
+
parameters: Type5.Object({
|
|
991
|
+
query: Type5.String({ description: "A phrase to look for, matched case-insensitively." }),
|
|
992
|
+
limit: Type5.Optional(Type5.Number({ description: "How many sessions to return, default 5." }))
|
|
921
993
|
}),
|
|
922
994
|
execute: async (_id, params) => {
|
|
923
995
|
const matches = await this.#search(params.query, params.limit ?? 5);
|
|
@@ -929,7 +1001,7 @@ class SessionToolPack2 {
|
|
|
929
1001
|
label: "Read session",
|
|
930
1002
|
description: "Read one earlier conversation, found with session_search, as plain text.",
|
|
931
1003
|
promptSnippet: "session_read: read one earlier conversation by id",
|
|
932
|
-
parameters:
|
|
1004
|
+
parameters: Type5.Object({ sessionId: Type5.String() }),
|
|
933
1005
|
execute: async (_id, params) => {
|
|
934
1006
|
const text = await this.#read(params.sessionId);
|
|
935
1007
|
return { content: [{ type: "text", text }], details: undefined, isError: text.startsWith("No session") };
|
|
@@ -962,7 +1034,7 @@ class SessionToolPack2 {
|
|
|
962
1034
|
|
|
963
1035
|
// pkgs/@akanjs/devkit/codeAgent/tools/WebToolPack.ts
|
|
964
1036
|
import { codeAgentClip as codeAgentClip3, codeAgentOutputChars as codeAgentOutputChars3 } from "akanjs/common";
|
|
965
|
-
import { Type as
|
|
1037
|
+
import { Type as Type6 } from "typebox";
|
|
966
1038
|
|
|
967
1039
|
class WebToolPack2 {
|
|
968
1040
|
#options;
|
|
@@ -1027,7 +1099,7 @@ class WebToolPack2 {
|
|
|
1027
1099
|
label: "Fetch",
|
|
1028
1100
|
description: "Fetch a URL and return its readable text. HTML is stripped to text; the result is truncated.",
|
|
1029
1101
|
promptSnippet: "web_fetch: fetch a URL and return its readable text",
|
|
1030
|
-
parameters:
|
|
1102
|
+
parameters: Type6.Object({ url: Type6.String({ description: "Absolute http(s) URL." }) }),
|
|
1031
1103
|
execute: async (_id, params) => {
|
|
1032
1104
|
const refusal = WebToolPack2.#refuseHost(params.url, allowHosts);
|
|
1033
1105
|
if (refusal)
|
|
@@ -1052,9 +1124,9 @@ ${codeAgentClip3(text, codeAgentOutputChars3)}` }
|
|
|
1052
1124
|
label: "Search",
|
|
1053
1125
|
description: `Search the web (${provider.name}) and return the top results as title, url and snippet.`,
|
|
1054
1126
|
promptSnippet: "web_search: search the web and return titles, urls and snippets",
|
|
1055
|
-
parameters:
|
|
1056
|
-
query:
|
|
1057
|
-
count:
|
|
1127
|
+
parameters: Type6.Object({
|
|
1128
|
+
query: Type6.String(),
|
|
1129
|
+
count: Type6.Optional(Type6.Number({ description: "How many results, default 5." }))
|
|
1058
1130
|
}),
|
|
1059
1131
|
execute: async (_id, params) => {
|
|
1060
1132
|
const key = process.env[provider.envKey] ?? "";
|
|
@@ -1098,6 +1170,7 @@ class AkanCodePlugins2 {
|
|
|
1098
1170
|
...options.onNotice ? { onNotice: options.onNotice } : {}
|
|
1099
1171
|
});
|
|
1100
1172
|
const dispose = () => mcp?.close();
|
|
1173
|
+
const mcpStatus = mcp?.status ?? [];
|
|
1101
1174
|
const web = new WebToolPack2({ profile: options.profile });
|
|
1102
1175
|
const webExtension = web.extension();
|
|
1103
1176
|
const sessions = new SessionToolPack2({
|
|
@@ -1109,10 +1182,20 @@ class AkanCodePlugins2 {
|
|
|
1109
1182
|
const sessionExtension = sessions.extension();
|
|
1110
1183
|
const mail = new MailToolPack2({ profile: options.profile, mailbox: options.mailbox });
|
|
1111
1184
|
const mailExtension = mail.extension();
|
|
1185
|
+
const onAsk = options.onAsk;
|
|
1186
|
+
const ask = onAsk ? new AskToolPack({ profile: options.profile, ask: onAsk }) : undefined;
|
|
1187
|
+
const askExtension = ask?.extension();
|
|
1112
1188
|
if (!options.profile.tools.akan)
|
|
1113
1189
|
return {
|
|
1114
|
-
extensions: [mcp?.extension(), webExtension, sessionExtension, mailExtension].filter((entry) => !!entry),
|
|
1115
|
-
toolNames: [
|
|
1190
|
+
extensions: [mcp?.extension(), webExtension, sessionExtension, mailExtension, askExtension].filter((entry) => !!entry),
|
|
1191
|
+
toolNames: [
|
|
1192
|
+
...mcp?.toolNames ?? [],
|
|
1193
|
+
...web.names(),
|
|
1194
|
+
...sessions.names(),
|
|
1195
|
+
...mail.names(),
|
|
1196
|
+
...ask?.names() ?? []
|
|
1197
|
+
],
|
|
1198
|
+
mcp: mcpStatus,
|
|
1116
1199
|
dispose
|
|
1117
1200
|
};
|
|
1118
1201
|
const readOnly = !options.profile.tools.builtin.includes("write");
|
|
@@ -1127,7 +1210,8 @@ class AkanCodePlugins2 {
|
|
|
1127
1210
|
cwd: options.cwd,
|
|
1128
1211
|
parent: options.profile,
|
|
1129
1212
|
depth: options.depth,
|
|
1130
|
-
...options.onNotice ? { onNotice: options.onNotice } : {}
|
|
1213
|
+
...options.onNotice ? { onNotice: options.onNotice } : {},
|
|
1214
|
+
...options.onAgents ? { onAgents: options.onAgents } : {}
|
|
1131
1215
|
});
|
|
1132
1216
|
if (subagent)
|
|
1133
1217
|
extensions.push(subagent);
|
|
@@ -1145,6 +1229,8 @@ class AkanCodePlugins2 {
|
|
|
1145
1229
|
extensions.push(sessionExtension);
|
|
1146
1230
|
if (mailExtension)
|
|
1147
1231
|
extensions.push(mailExtension);
|
|
1232
|
+
if (askExtension)
|
|
1233
|
+
extensions.push(askExtension);
|
|
1148
1234
|
return {
|
|
1149
1235
|
extensions,
|
|
1150
1236
|
toolNames: [
|
|
@@ -1153,8 +1239,10 @@ class AkanCodePlugins2 {
|
|
|
1153
1239
|
...mcp?.toolNames ?? [],
|
|
1154
1240
|
...web.names(),
|
|
1155
1241
|
...sessions.names(),
|
|
1156
|
-
...mail.names()
|
|
1242
|
+
...mail.names(),
|
|
1243
|
+
...ask?.names() ?? []
|
|
1157
1244
|
],
|
|
1245
|
+
mcp: mcpStatus,
|
|
1158
1246
|
dispose
|
|
1159
1247
|
};
|
|
1160
1248
|
}
|
|
@@ -1184,36 +1272,31 @@ class AkanCodePlugins2 {
|
|
|
1184
1272
|
}
|
|
1185
1273
|
|
|
1186
1274
|
// pkgs/@akanjs/devkit/codeAgent/agent/AkanCodeServices.ts
|
|
1187
|
-
import { existsSync as
|
|
1188
|
-
import {
|
|
1189
|
-
AuthStorage,
|
|
1190
|
-
ModelRegistry,
|
|
1191
|
-
SessionManager as SessionManager2,
|
|
1192
|
-
SettingsManager
|
|
1193
|
-
} from "@earendil-works/pi-coding-agent";
|
|
1275
|
+
import { existsSync as existsSync7, mkdirSync } from "fs";
|
|
1276
|
+
import { ModelRuntime, SessionManager as SessionManager2, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
1194
1277
|
|
|
1195
1278
|
// pkgs/@akanjs/devkit/codeAgent/tools/AkanEnvKeys.ts
|
|
1196
|
-
import { existsSync as
|
|
1197
|
-
import
|
|
1279
|
+
import { existsSync as existsSync4, readFileSync } from "fs";
|
|
1280
|
+
import path4 from "path";
|
|
1198
1281
|
|
|
1199
1282
|
class AkanEnvKeys2 {
|
|
1200
|
-
static apply(
|
|
1283
|
+
static async apply(runtime, workspaceRoot) {
|
|
1201
1284
|
const applied = [];
|
|
1202
1285
|
for (const [name, value] of AkanEnvKeys2.#entries(workspaceRoot)) {
|
|
1203
1286
|
const provider = AkanEnvKeys2.#providerOf(name);
|
|
1204
1287
|
if (!provider || !value)
|
|
1205
1288
|
continue;
|
|
1206
|
-
|
|
1289
|
+
await runtime.setRuntimeApiKey(provider, value);
|
|
1207
1290
|
applied.push(provider);
|
|
1208
1291
|
}
|
|
1209
1292
|
return applied;
|
|
1210
1293
|
}
|
|
1211
1294
|
static #entries(workspaceRoot) {
|
|
1212
1295
|
const fromProcess = Object.entries(process.env).filter(([, value]) => typeof value === "string");
|
|
1213
|
-
const file =
|
|
1214
|
-
if (!
|
|
1296
|
+
const file = path4.join(workspaceRoot, ".env");
|
|
1297
|
+
if (!existsSync4(file))
|
|
1215
1298
|
return fromProcess;
|
|
1216
|
-
const fromFile =
|
|
1299
|
+
const fromFile = readFileSync(file, "utf8").split(`
|
|
1217
1300
|
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && line.includes("=")).map((line) => {
|
|
1218
1301
|
const index = line.indexOf("=");
|
|
1219
1302
|
return [
|
|
@@ -1229,89 +1312,82 @@ class AkanEnvKeys2 {
|
|
|
1229
1312
|
}
|
|
1230
1313
|
}
|
|
1231
1314
|
|
|
1232
|
-
// pkgs/@akanjs/devkit/codeAgent/agent/
|
|
1233
|
-
import {
|
|
1234
|
-
|
|
1235
|
-
existsSync as existsSync7,
|
|
1236
|
-
mkdirSync,
|
|
1237
|
-
openSync,
|
|
1238
|
-
readFileSync as readFileSync3,
|
|
1239
|
-
renameSync,
|
|
1240
|
-
unlinkSync,
|
|
1241
|
-
writeFileSync
|
|
1242
|
-
} from "fs";
|
|
1243
|
-
import path6 from "path";
|
|
1315
|
+
// pkgs/@akanjs/devkit/codeAgent/agent/AkanContextFiles.ts
|
|
1316
|
+
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
1317
|
+
import path5 from "path";
|
|
1244
1318
|
|
|
1245
|
-
class
|
|
1246
|
-
#
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
this.#write(next);
|
|
1258
|
-
return result;
|
|
1259
|
-
} finally {
|
|
1260
|
-
this.#release();
|
|
1319
|
+
class AkanContextFiles {
|
|
1320
|
+
static #frontmatter = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
1321
|
+
static extend(base, options) {
|
|
1322
|
+
const seen = new Set(base.map((file) => file.path));
|
|
1323
|
+
const contents = new Set(base.map((file) => file.content.trim()));
|
|
1324
|
+
const added = [];
|
|
1325
|
+
for (const file of [...AkanContextFiles.#claude(base), ...AkanContextFiles.#cursor(options)]) {
|
|
1326
|
+
if (seen.has(file.path) || contents.has(file.content.trim()) || !file.content.trim())
|
|
1327
|
+
continue;
|
|
1328
|
+
seen.add(file.path);
|
|
1329
|
+
contents.add(file.content.trim());
|
|
1330
|
+
added.push(file);
|
|
1261
1331
|
}
|
|
1332
|
+
return [...base, ...added];
|
|
1262
1333
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1334
|
+
static #claude(base) {
|
|
1335
|
+
const files = [];
|
|
1336
|
+
for (const file of base) {
|
|
1337
|
+
if (!/^AGENTS\.(md|MD)$/.test(path5.basename(file.path)))
|
|
1338
|
+
continue;
|
|
1339
|
+
for (const name of ["CLAUDE.md", "CLAUDE.MD"]) {
|
|
1340
|
+
const candidate = path5.join(path5.dirname(file.path), name);
|
|
1341
|
+
const content = AkanContextFiles.#read(candidate);
|
|
1342
|
+
if (content !== undefined)
|
|
1343
|
+
files.push({ path: candidate, content });
|
|
1344
|
+
}
|
|
1272
1345
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
}
|
|
1286
|
-
#acquire() {
|
|
1287
|
-
mkdirSync(path6.dirname(this.#lockPath), { recursive: true, mode: 448 });
|
|
1288
|
-
const deadline = Date.now() + 5000;
|
|
1289
|
-
for (;; ) {
|
|
1290
|
-
try {
|
|
1291
|
-
closeSync(openSync(this.#lockPath, "wx"));
|
|
1292
|
-
return;
|
|
1293
|
-
} catch {
|
|
1294
|
-
if (this.#lockIsStale() || Date.now() > deadline) {
|
|
1295
|
-
try {
|
|
1296
|
-
unlinkSync(this.#lockPath);
|
|
1297
|
-
} catch {}
|
|
1346
|
+
return files;
|
|
1347
|
+
}
|
|
1348
|
+
static #cursor(options) {
|
|
1349
|
+
const files = [];
|
|
1350
|
+
const roots = [...new Set([options.workspaceRoot, options.cwd])];
|
|
1351
|
+
for (const root of roots) {
|
|
1352
|
+
const legacy = AkanContextFiles.#read(path5.join(root, ".cursorrules"));
|
|
1353
|
+
if (legacy !== undefined)
|
|
1354
|
+
files.push({ path: path5.join(root, ".cursorrules"), content: legacy });
|
|
1355
|
+
for (const file of AkanContextFiles.#mdcFiles(path5.join(root, ".cursor", "rules"))) {
|
|
1356
|
+
const raw = AkanContextFiles.#read(file);
|
|
1357
|
+
if (raw === undefined)
|
|
1298
1358
|
continue;
|
|
1299
|
-
|
|
1300
|
-
|
|
1359
|
+
const body = AkanContextFiles.#applied(raw);
|
|
1360
|
+
if (body !== undefined)
|
|
1361
|
+
files.push({ path: file, content: body });
|
|
1301
1362
|
}
|
|
1302
1363
|
}
|
|
1364
|
+
return files;
|
|
1303
1365
|
}
|
|
1304
|
-
#
|
|
1366
|
+
static #applied(raw) {
|
|
1367
|
+
const match = AkanContextFiles.#frontmatter.exec(raw);
|
|
1368
|
+
if (!match)
|
|
1369
|
+
return raw;
|
|
1370
|
+
if (!/^\s*alwaysApply\s*:\s*true\s*$/m.test(match[1] ?? ""))
|
|
1371
|
+
return;
|
|
1372
|
+
return raw.slice(match[0].length);
|
|
1373
|
+
}
|
|
1374
|
+
static #mdcFiles(dir) {
|
|
1375
|
+
if (!existsSync5(dir))
|
|
1376
|
+
return [];
|
|
1305
1377
|
try {
|
|
1306
|
-
return
|
|
1378
|
+
return readdirSync(dir, { recursive: true, withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".mdc")).map((entry) => path5.join(entry.parentPath ?? dir, entry.name)).sort();
|
|
1307
1379
|
} catch {
|
|
1308
|
-
return
|
|
1380
|
+
return [];
|
|
1309
1381
|
}
|
|
1310
1382
|
}
|
|
1311
|
-
#
|
|
1383
|
+
static #read(file) {
|
|
1312
1384
|
try {
|
|
1313
|
-
|
|
1314
|
-
|
|
1385
|
+
if (!existsSync5(file) || !statSync2(file).isFile())
|
|
1386
|
+
return;
|
|
1387
|
+
return readFileSync2(file, "utf8").replace(/^\uFEFF/, "");
|
|
1388
|
+
} catch {
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1315
1391
|
}
|
|
1316
1392
|
}
|
|
1317
1393
|
|
|
@@ -1323,6 +1399,7 @@ var akanSystemPrompt2 = (profile) => {
|
|
|
1323
1399
|
"- Prefer an akan workflow to a direct source edit. `list_workflows` / `explain_workflow` / `plan_workflow`, then `apply_workflow({ planPath })`. Editing source by hand is the fallback, not the default.",
|
|
1324
1400
|
"- Read context with `inspect_akan_context` before reading source bodies; it answers most structural questions without spending the file.",
|
|
1325
1401
|
"- Fetch the guideline for an area before a deep pass on it: `get_guideline`.",
|
|
1402
|
+
"- A question about `akan code` itself \u2014 its slash commands, MCP servers, sessions, models, keys, or where its files live \u2014 is answered by the `akan-code` skill. Read it rather than describing the tool from memory.",
|
|
1326
1403
|
"- Never hand-edit a generated file (`lib/cnst.ts`, `db.ts`, `st.ts`, any `*/index.ts` barrel). Fix the owning source and run `repair_generated` or `akan sync`."
|
|
1327
1404
|
];
|
|
1328
1405
|
if (profile.tools.builtin.includes("write"))
|
|
@@ -1331,6 +1408,8 @@ var akanSystemPrompt2 = (profile) => {
|
|
|
1331
1408
|
lines.push("- This session is read-only: there is no write, edit or bash tool. Report what should change instead of changing it.");
|
|
1332
1409
|
if (profile.tools.subagent)
|
|
1333
1410
|
lines.push('- Hand a self-contained search or read to `task` \u2014 `type: "explore"` reads and reports, `type: "code"` makes the change. Only its answer comes back, so give it everything it needs and ask for exactly what you want returned. Do it yourself when the reading is short or when you need the files in front of you.');
|
|
1411
|
+
if (profile.ui.canPrompt)
|
|
1412
|
+
lines.push("- Ask with `ask_user` when two readings of the request would lead to materially different work, and offer the options you can name. Do not ask for a choice that has an obvious default, and do not ask permission for work you were already asked to do \u2014 make the call, say the assumption, and carry on.");
|
|
1334
1413
|
if (profile.approval !== "never")
|
|
1335
1414
|
lines.push(`- Some tool calls need the user's approval (policy: ${profile.approval}). A refusal is an answer; do not retry it a different way.`);
|
|
1336
1415
|
lines.push("- Secrets are refused at the tool boundary. If you need a value from `.env`, ask the user for it rather than looking for another way to read the file.", "", "## How to answer", "", "- Answer in as few words as carry the answer. A short question gets one sentence with no heading.", "- Do not open with what you are about to do, and do not close with a summary of what you did. The tool calls are already on the user's screen.", "- Skip headings, bold labels and bullet lists unless the answer is genuinely a list. Prose is shorter.", "- State a file as `path:line`. Do not paste a file back that the user can open.", "- Say what you could not do, and why, in one line. Do not offer alternatives nobody asked for.");
|
|
@@ -1339,26 +1418,26 @@ var akanSystemPrompt2 = (profile) => {
|
|
|
1339
1418
|
};
|
|
1340
1419
|
|
|
1341
1420
|
// pkgs/@akanjs/devkit/codeAgent/agent/CodeSessionIndex.ts
|
|
1342
|
-
import { existsSync as
|
|
1343
|
-
import
|
|
1421
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
1422
|
+
import path6 from "path";
|
|
1344
1423
|
|
|
1345
1424
|
class CodeSessionIndex2 {
|
|
1346
1425
|
static readLimit = 512 * 1024;
|
|
1347
1426
|
static list(dir, limit = 30) {
|
|
1348
|
-
if (!
|
|
1427
|
+
if (!existsSync6(dir))
|
|
1349
1428
|
return [];
|
|
1350
|
-
const files =
|
|
1429
|
+
const files = readdirSync2(dir).filter((entry) => entry.endsWith(".jsonl")).map((entry) => path6.join(dir, entry)).map((file) => ({ file, updatedAt: CodeSessionIndex2.#mtime(file) })).sort((left, right) => right.updatedAt - left.updatedAt).slice(0, limit);
|
|
1351
1430
|
return files.map((entry) => CodeSessionIndex2.#read(entry.file, entry.updatedAt)).filter((entry) => !!entry);
|
|
1352
1431
|
}
|
|
1353
1432
|
static fileOf(dir, id) {
|
|
1354
|
-
if (!
|
|
1433
|
+
if (!existsSync6(dir))
|
|
1355
1434
|
return;
|
|
1356
|
-
const match =
|
|
1357
|
-
return match ?
|
|
1435
|
+
const match = readdirSync2(dir).find((entry) => entry.endsWith(`${id}.jsonl`) || entry === `${id}.jsonl`);
|
|
1436
|
+
return match ? path6.join(dir, match) : undefined;
|
|
1358
1437
|
}
|
|
1359
1438
|
static #mtime(file) {
|
|
1360
1439
|
try {
|
|
1361
|
-
return
|
|
1440
|
+
return statSync3(file).mtimeMs;
|
|
1362
1441
|
} catch {
|
|
1363
1442
|
return 0;
|
|
1364
1443
|
}
|
|
@@ -1389,9 +1468,9 @@ class CodeSessionIndex2 {
|
|
|
1389
1468
|
}
|
|
1390
1469
|
static #lines(file) {
|
|
1391
1470
|
try {
|
|
1392
|
-
if (
|
|
1471
|
+
if (statSync3(file).size > CodeSessionIndex2.readLimit)
|
|
1393
1472
|
return [];
|
|
1394
|
-
return
|
|
1473
|
+
return readFileSync3(file, "utf8").split(`
|
|
1395
1474
|
`).filter((line) => !!line.trim());
|
|
1396
1475
|
} catch {
|
|
1397
1476
|
return [];
|
|
@@ -1419,20 +1498,20 @@ class AkanCodeServices2 {
|
|
|
1419
1498
|
quietStartup: true
|
|
1420
1499
|
});
|
|
1421
1500
|
}
|
|
1422
|
-
static
|
|
1423
|
-
|
|
1424
|
-
const
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
return
|
|
1501
|
+
static async runtime(workspaceRoot) {
|
|
1502
|
+
mkdirSync(akanCodePaths2.globalDir(), { recursive: true, mode: 448 });
|
|
1503
|
+
const runtime = await ModelRuntime.create({
|
|
1504
|
+
authPath: akanCodePaths2.authFile(),
|
|
1505
|
+
modelsPath: akanCodePaths2.modelsFile()
|
|
1506
|
+
});
|
|
1507
|
+
await AkanEnvKeys2.apply(runtime, workspaceRoot);
|
|
1508
|
+
return runtime;
|
|
1430
1509
|
}
|
|
1431
1510
|
static sessions(workspaceRoot, cwd, profile, resume) {
|
|
1432
1511
|
if (profile.session.store !== "file")
|
|
1433
1512
|
return SessionManager2.inMemory(cwd);
|
|
1434
1513
|
const dir = akanCodePaths2.sessionsDir(workspaceRoot);
|
|
1435
|
-
|
|
1514
|
+
mkdirSync(dir, { recursive: true });
|
|
1436
1515
|
const manager = SessionManager2.create(cwd, dir);
|
|
1437
1516
|
if (!resume)
|
|
1438
1517
|
return manager;
|
|
@@ -1445,17 +1524,24 @@ class AkanCodeServices2 {
|
|
|
1445
1524
|
static #skillPaths(profile, workspaceRoot) {
|
|
1446
1525
|
if (!profile.context.skills)
|
|
1447
1526
|
return [];
|
|
1448
|
-
const
|
|
1449
|
-
|
|
1527
|
+
const dirs = [
|
|
1528
|
+
akanCodePaths2.builtinSkillsDir(),
|
|
1529
|
+
akanCodePaths2.globalSkillsDir(),
|
|
1530
|
+
akanCodePaths2.skillsDir(workspaceRoot)
|
|
1531
|
+
];
|
|
1532
|
+
return dirs.filter((dir) => !!dir && existsSync7(dir));
|
|
1450
1533
|
}
|
|
1451
1534
|
static resourceOptions(options) {
|
|
1452
|
-
const present = (dir) =>
|
|
1535
|
+
const present = (dir) => existsSync7(dir) ? [dir] : [];
|
|
1453
1536
|
return {
|
|
1454
1537
|
noExtensions: true,
|
|
1455
1538
|
noSkills: true,
|
|
1456
1539
|
noPromptTemplates: true,
|
|
1457
1540
|
noThemes: true,
|
|
1458
1541
|
noContextFiles: !options.profile.context.projectFiles,
|
|
1542
|
+
agentsFilesOverride: ({ agentsFiles }) => ({
|
|
1543
|
+
agentsFiles: options.profile.context.projectFiles ? AkanContextFiles.extend(agentsFiles, { workspaceRoot: options.workspaceRoot, cwd: options.cwd }) : agentsFiles
|
|
1544
|
+
}),
|
|
1459
1545
|
appendSystemPrompt: [akanSystemPrompt2(options.profile)],
|
|
1460
1546
|
additionalExtensionPaths: present(akanCodePaths2.extensionsDir(options.workspaceRoot)),
|
|
1461
1547
|
additionalSkillPaths: AkanCodeServices2.#skillPaths(options.profile, options.workspaceRoot),
|
|
@@ -1733,7 +1819,7 @@ class CodeAgentEventMapper2 {
|
|
|
1733
1819
|
}
|
|
1734
1820
|
|
|
1735
1821
|
// pkgs/@akanjs/devkit/codeAgent/agent/CodeAgentGate.ts
|
|
1736
|
-
import
|
|
1822
|
+
import path7 from "path";
|
|
1737
1823
|
var writeTools = new Set(["write", "edit"]);
|
|
1738
1824
|
var commandTools = new Set(["bash"]);
|
|
1739
1825
|
|
|
@@ -1772,9 +1858,9 @@ class CodeAgentGate2 {
|
|
|
1772
1858
|
return `The command names "${hit}", which the profile's deny list covers. Ask the user for the value instead of reading the file.`;
|
|
1773
1859
|
}
|
|
1774
1860
|
#deniedReason(target) {
|
|
1775
|
-
const absolute =
|
|
1776
|
-
const relative =
|
|
1777
|
-
if (relative.startsWith("..") ||
|
|
1861
|
+
const absolute = path7.isAbsolute(target) ? target : path7.resolve(this.#profile.paths.root, target);
|
|
1862
|
+
const relative = path7.relative(this.#profile.paths.root, absolute);
|
|
1863
|
+
if (relative.startsWith("..") || path7.isAbsolute(relative))
|
|
1778
1864
|
return `Path is outside the agent root (${this.#profile.paths.root}).`;
|
|
1779
1865
|
if (this.#deny.some((glob) => glob.match(absolute) || glob.match(relative)))
|
|
1780
1866
|
return "Path is on the profile's deny list (secrets and environment files are never readable).";
|
|
@@ -1864,15 +1950,15 @@ class CodeAgentUi2 {
|
|
|
1864
1950
|
// pkgs/@akanjs/devkit/codeAgent/agent/CodeMailbox.ts
|
|
1865
1951
|
import {
|
|
1866
1952
|
appendFileSync,
|
|
1867
|
-
existsSync as
|
|
1868
|
-
mkdirSync as
|
|
1869
|
-
readdirSync as
|
|
1870
|
-
readFileSync as
|
|
1953
|
+
existsSync as existsSync8,
|
|
1954
|
+
mkdirSync as mkdirSync2,
|
|
1955
|
+
readdirSync as readdirSync3,
|
|
1956
|
+
readFileSync as readFileSync4,
|
|
1871
1957
|
rmSync,
|
|
1872
|
-
statSync as
|
|
1873
|
-
writeFileSync
|
|
1958
|
+
statSync as statSync4,
|
|
1959
|
+
writeFileSync
|
|
1874
1960
|
} from "fs";
|
|
1875
|
-
import
|
|
1961
|
+
import path8 from "path";
|
|
1876
1962
|
|
|
1877
1963
|
class CodeMailbox2 {
|
|
1878
1964
|
static staleMs = 20000;
|
|
@@ -1892,11 +1978,11 @@ class CodeMailbox2 {
|
|
|
1892
1978
|
this.#name = name;
|
|
1893
1979
|
}
|
|
1894
1980
|
get inbox() {
|
|
1895
|
-
return
|
|
1981
|
+
return path8.join(this.#dir, "inbox", `${this.#id}.jsonl`);
|
|
1896
1982
|
}
|
|
1897
1983
|
open(cwd, onMail) {
|
|
1898
|
-
|
|
1899
|
-
|
|
1984
|
+
mkdirSync2(path8.join(this.#dir, "live"), { recursive: true });
|
|
1985
|
+
mkdirSync2(path8.join(this.#dir, "inbox"), { recursive: true });
|
|
1900
1986
|
this.#read = CodeMailbox2.#lines(this.inbox).length;
|
|
1901
1987
|
this.beat(cwd);
|
|
1902
1988
|
this.#beat = setInterval(() => this.beat(cwd), CodeMailbox2.beatMs);
|
|
@@ -1913,7 +1999,7 @@ class CodeMailbox2 {
|
|
|
1913
1999
|
this.#beat = null;
|
|
1914
2000
|
this.#poll = null;
|
|
1915
2001
|
try {
|
|
1916
|
-
rmSync(
|
|
2002
|
+
rmSync(path8.join(this.#dir, "live", `${this.#id}.json`), { force: true });
|
|
1917
2003
|
} catch {}
|
|
1918
2004
|
}
|
|
1919
2005
|
rename(name, cwd) {
|
|
@@ -1923,15 +2009,15 @@ class CodeMailbox2 {
|
|
|
1923
2009
|
beat(cwd) {
|
|
1924
2010
|
const peer = { id: this.#id, name: this.#name, cwd, pid: process.pid, at: Date.now() };
|
|
1925
2011
|
try {
|
|
1926
|
-
|
|
2012
|
+
writeFileSync(path8.join(this.#dir, "live", `${this.#id}.json`), JSON.stringify(peer));
|
|
1927
2013
|
} catch {}
|
|
1928
2014
|
}
|
|
1929
2015
|
peers() {
|
|
1930
|
-
const dir =
|
|
1931
|
-
if (!
|
|
2016
|
+
const dir = path8.join(this.#dir, "live");
|
|
2017
|
+
if (!existsSync8(dir))
|
|
1932
2018
|
return [];
|
|
1933
2019
|
const now = Date.now();
|
|
1934
|
-
return
|
|
2020
|
+
return readdirSync3(dir).filter((entry) => entry.endsWith(".json")).map((entry) => CodeMailbox2.#peer(path8.join(dir, entry))).filter((peer) => !!peer && peer.id !== this.#id && now - peer.at < CodeMailbox2.staleMs).sort((left, right) => right.at - left.at);
|
|
1935
2021
|
}
|
|
1936
2022
|
find(target) {
|
|
1937
2023
|
const needle = target.trim().toLowerCase();
|
|
@@ -1949,8 +2035,8 @@ class CodeMailbox2 {
|
|
|
1949
2035
|
throw new Error(`Too many messages: ${CodeMailbox2.sendsPerMinute} a minute is the ceiling.`);
|
|
1950
2036
|
this.#sent.push(now);
|
|
1951
2037
|
const mail = { at: now, from: this.#id, fromName: this.#name, text };
|
|
1952
|
-
|
|
1953
|
-
appendFileSync(
|
|
2038
|
+
mkdirSync2(path8.join(this.#dir, "inbox"), { recursive: true });
|
|
2039
|
+
appendFileSync(path8.join(this.#dir, "inbox", `${to}.jsonl`), `${JSON.stringify(mail)}
|
|
1954
2040
|
`);
|
|
1955
2041
|
}
|
|
1956
2042
|
#drain() {
|
|
@@ -1963,15 +2049,15 @@ class CodeMailbox2 {
|
|
|
1963
2049
|
}
|
|
1964
2050
|
static #peer(file) {
|
|
1965
2051
|
try {
|
|
1966
|
-
const peer = JSON.parse(
|
|
1967
|
-
return typeof peer.id === "string" ? { ...peer, at: peer.at ||
|
|
2052
|
+
const peer = JSON.parse(readFileSync4(file, "utf8"));
|
|
2053
|
+
return typeof peer.id === "string" ? { ...peer, at: peer.at || statSync4(file).mtimeMs } : undefined;
|
|
1968
2054
|
} catch {
|
|
1969
2055
|
return;
|
|
1970
2056
|
}
|
|
1971
2057
|
}
|
|
1972
2058
|
static #lines(file) {
|
|
1973
2059
|
try {
|
|
1974
|
-
return
|
|
2060
|
+
return readFileSync4(file, "utf8").split(`
|
|
1975
2061
|
`).filter((line) => !!line.trim());
|
|
1976
2062
|
} catch {
|
|
1977
2063
|
return [];
|
|
@@ -1988,15 +2074,15 @@ class CodeMailbox2 {
|
|
|
1988
2074
|
|
|
1989
2075
|
// pkgs/@akanjs/devkit/codeAgent/agent/CodeSessionFork.ts
|
|
1990
2076
|
import { randomBytes } from "crypto";
|
|
1991
|
-
import { readFileSync as
|
|
1992
|
-
import
|
|
2077
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
2078
|
+
import path9 from "path";
|
|
1993
2079
|
import { codeAgentSessionName } from "akanjs/common";
|
|
1994
2080
|
class CodeSessionFork2 {
|
|
1995
2081
|
static fork(dir, id, name) {
|
|
1996
2082
|
const source = CodeSessionIndex2.fileOf(dir, id);
|
|
1997
2083
|
if (!source)
|
|
1998
2084
|
throw new Error(`No stored session ${id} in ${dir}`);
|
|
1999
|
-
const lines =
|
|
2085
|
+
const lines = readFileSync5(source, "utf8").split(`
|
|
2000
2086
|
`).filter((line) => !!line.trim());
|
|
2001
2087
|
if (!lines.length)
|
|
2002
2088
|
throw new Error(`Session ${id} has nothing in it to fork`);
|
|
@@ -2009,8 +2095,8 @@ class CodeSessionFork2 {
|
|
|
2009
2095
|
first.id = forked;
|
|
2010
2096
|
first.timestamp = at.toISOString();
|
|
2011
2097
|
records.push(CodeSessionFork2.#named(records, name ?? CodeSessionFork2.#nameOf(records), at));
|
|
2012
|
-
const file =
|
|
2013
|
-
|
|
2098
|
+
const file = path9.join(dir, `${at.toISOString().replace(/[:.]/g, "-")}_${forked}.jsonl`);
|
|
2099
|
+
writeFileSync2(file, `${records.map((record) => JSON.stringify(record)).join(`
|
|
2014
2100
|
`)}
|
|
2015
2101
|
`);
|
|
2016
2102
|
return { id: forked, file };
|
|
@@ -2052,6 +2138,8 @@ class CodeAgent2 {
|
|
|
2052
2138
|
#disposed = false;
|
|
2053
2139
|
#sessionDir;
|
|
2054
2140
|
#mailbox;
|
|
2141
|
+
#mcp = [];
|
|
2142
|
+
#workspaceRoot = "";
|
|
2055
2143
|
#replay = [];
|
|
2056
2144
|
#replayFrom = 0;
|
|
2057
2145
|
#lastContextTokens;
|
|
@@ -2064,9 +2152,8 @@ class CodeAgent2 {
|
|
|
2064
2152
|
const cwd = options.cwd ?? workspaceRoot;
|
|
2065
2153
|
const agent = new CodeAgent2(options.profile);
|
|
2066
2154
|
const settingsManager = AkanCodeServices2.settings();
|
|
2067
|
-
const
|
|
2068
|
-
const
|
|
2069
|
-
const model = await akanCodeModel2(modelRegistry, options.model);
|
|
2155
|
+
const modelRuntime = await AkanCodeServices2.runtime(workspaceRoot);
|
|
2156
|
+
const model = await akanCodeModel2(modelRuntime, options.model);
|
|
2070
2157
|
const compaction = DEFAULT_COMPACTION_SETTINGS;
|
|
2071
2158
|
const modelWarnings = akanCodeModelWarnings2(model, compaction.reserveTokens + compaction.keepRecentTokens);
|
|
2072
2159
|
const akan = await AkanCodePlugins2.build({
|
|
@@ -2078,7 +2165,9 @@ class CodeAgent2 {
|
|
|
2078
2165
|
depth: options.depth ?? 0,
|
|
2079
2166
|
currentSessionId: () => agent.sessionId,
|
|
2080
2167
|
mailbox: () => agent.#mailbox,
|
|
2081
|
-
onNotice: (message) => agent.#emit({ type: "notice", level: "warning", message })
|
|
2168
|
+
onNotice: (message) => agent.#emit({ type: "notice", level: "warning", message }),
|
|
2169
|
+
onAgents: (agents) => agent.#emit({ type: "subagent", agents }),
|
|
2170
|
+
onAsk: (question) => agent.ask(question)
|
|
2082
2171
|
});
|
|
2083
2172
|
const resourceLoader = new DefaultResourceLoader({
|
|
2084
2173
|
cwd,
|
|
@@ -2095,8 +2184,7 @@ class CodeAgent2 {
|
|
|
2095
2184
|
const { session } = await createAgentSession({
|
|
2096
2185
|
cwd,
|
|
2097
2186
|
agentDir: akanCodePaths2.globalDir(),
|
|
2098
|
-
|
|
2099
|
-
modelRegistry,
|
|
2187
|
+
modelRuntime,
|
|
2100
2188
|
settingsManager,
|
|
2101
2189
|
resourceLoader,
|
|
2102
2190
|
model,
|
|
@@ -2106,6 +2194,8 @@ class CodeAgent2 {
|
|
|
2106
2194
|
customTools: options.customTools
|
|
2107
2195
|
});
|
|
2108
2196
|
agent.#disposePlugins = akan.dispose;
|
|
2197
|
+
agent.#mcp = akan.mcp;
|
|
2198
|
+
agent.#workspaceRoot = workspaceRoot;
|
|
2109
2199
|
if (options.profile.session.store === "file")
|
|
2110
2200
|
agent.#sessionDir = akanCodePaths2.sessionsDir(workspaceRoot);
|
|
2111
2201
|
await agent.#attach(session, options.mode ?? "print");
|
|
@@ -2125,6 +2215,12 @@ class CodeAgent2 {
|
|
|
2125
2215
|
sessions() {
|
|
2126
2216
|
return this.#sessionDir ? CodeSessionIndex2.list(this.#sessionDir) : [];
|
|
2127
2217
|
}
|
|
2218
|
+
get workspaceRoot() {
|
|
2219
|
+
return this.#workspaceRoot;
|
|
2220
|
+
}
|
|
2221
|
+
mcpServers() {
|
|
2222
|
+
return this.#mcp;
|
|
2223
|
+
}
|
|
2128
2224
|
peers() {
|
|
2129
2225
|
return this.#mailbox?.peers() ?? [];
|
|
2130
2226
|
}
|
|
@@ -2190,6 +2286,7 @@ ${mail.text}`).catch((error) => this.#emit({ type: "notice", level: "error", mes
|
|
|
2190
2286
|
if (text)
|
|
2191
2287
|
this.#emit({ type: "message", turnId, role: message.role, text });
|
|
2192
2288
|
}
|
|
2289
|
+
this.#emitContextNow();
|
|
2193
2290
|
}
|
|
2194
2291
|
static #textOf(content) {
|
|
2195
2292
|
if (typeof content === "string")
|
|
@@ -2279,9 +2376,26 @@ Continue the task.`);
|
|
|
2279
2376
|
session.setThinkingLevel(effort);
|
|
2280
2377
|
this.#emit({ type: "session", info: this.info });
|
|
2281
2378
|
}
|
|
2379
|
+
catalogue() {
|
|
2380
|
+
const runtime = this.#session?.modelRuntime;
|
|
2381
|
+
if (!runtime)
|
|
2382
|
+
return [];
|
|
2383
|
+
const current = this.#session?.model;
|
|
2384
|
+
return runtime.getProviders().map((provider) => ({
|
|
2385
|
+
id: provider.id,
|
|
2386
|
+
name: provider.name ?? provider.id,
|
|
2387
|
+
authorized: runtime.hasConfiguredAuth(provider.id),
|
|
2388
|
+
models: (runtime.getModels(provider.id) ?? []).map((model) => ({
|
|
2389
|
+
id: model.id,
|
|
2390
|
+
name: model.name,
|
|
2391
|
+
...model.contextWindow ? { contextWindow: model.contextWindow } : {},
|
|
2392
|
+
current: model.provider === current?.provider && model.id === current?.id
|
|
2393
|
+
}))
|
|
2394
|
+
})).sort((a, b) => a.id.localeCompare(b.id));
|
|
2395
|
+
}
|
|
2282
2396
|
async setModel(ref) {
|
|
2283
2397
|
const session = this.#require();
|
|
2284
|
-
const model = await akanCodeModel2(session.
|
|
2398
|
+
const model = await akanCodeModel2(session.modelRuntime, ref);
|
|
2285
2399
|
if (!model)
|
|
2286
2400
|
throw new Error(`Unknown model: ${ref.provider}/${ref.id}`);
|
|
2287
2401
|
await session.setModel(model);
|
|
@@ -2331,6 +2445,9 @@ Continue the task.`);
|
|
|
2331
2445
|
#emitContextUsage(eventType) {
|
|
2332
2446
|
if (eventType !== "agent_end" && eventType !== "compaction_end")
|
|
2333
2447
|
return;
|
|
2448
|
+
this.#emitContextNow();
|
|
2449
|
+
}
|
|
2450
|
+
#emitContextNow() {
|
|
2334
2451
|
const usage = this.#session?.getContextUsage();
|
|
2335
2452
|
if (usage?.tokens == null)
|
|
2336
2453
|
return;
|
|
@@ -2406,29 +2523,28 @@ Continue the task.`);
|
|
|
2406
2523
|
}
|
|
2407
2524
|
return await this.#asks.openApproval(approvalId);
|
|
2408
2525
|
}
|
|
2526
|
+
async ask(spec) {
|
|
2527
|
+
if (!this.#profile.ui.canPrompt)
|
|
2528
|
+
return;
|
|
2529
|
+
const question = { questionId: this.#asks.nextId("q"), ...spec };
|
|
2530
|
+
this.#emit({ type: "question", question });
|
|
2531
|
+
if (this.#profile.interaction.question === "suspend") {
|
|
2532
|
+
this.#mapper.noteOutcome("awaiting");
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
return await this.#asks.openQuestion(question) || undefined;
|
|
2536
|
+
}
|
|
2409
2537
|
#ui() {
|
|
2410
2538
|
return new CodeAgentUi2({
|
|
2411
|
-
ask: async (prompt, kind, choices) => {
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
prompt,
|
|
2418
|
-
kind,
|
|
2419
|
-
...choices ? { options: choices.map((label) => ({ key: label, label })) } : {},
|
|
2420
|
-
freeText: kind === "text"
|
|
2421
|
-
};
|
|
2422
|
-
this.#emit({ type: "question", question });
|
|
2423
|
-
if (this.#profile.interaction.question === "suspend") {
|
|
2424
|
-
this.#mapper.noteOutcome("awaiting");
|
|
2425
|
-
return;
|
|
2426
|
-
}
|
|
2427
|
-
return await this.#asks.openQuestion(question) || undefined;
|
|
2428
|
-
},
|
|
2539
|
+
ask: async (prompt, kind, choices) => await this.ask({
|
|
2540
|
+
prompt,
|
|
2541
|
+
kind,
|
|
2542
|
+
...choices ? { options: choices.map((label) => ({ key: label, label })) } : {},
|
|
2543
|
+
freeText: kind === "text"
|
|
2544
|
+
}),
|
|
2429
2545
|
notify: (level, message) => this.#emit({ type: "notice", level, message })
|
|
2430
2546
|
});
|
|
2431
2547
|
}
|
|
2432
2548
|
}
|
|
2433
2549
|
|
|
2434
|
-
export { AkanEditScope2, DevLogFeedback2, PreviewChrome2, PreviewView2, PreviewFeedback2, TurnFeedback2, AkanCli2, AkanVerifier2, VerifyFeedback2, AkanToolPack2, MailToolPack2,
|
|
2550
|
+
export { AkanEditScope2, DevLogFeedback2, PreviewChrome2, PreviewView2, PreviewFeedback2, TurnFeedback2, AkanCli2, AkanVerifier2, VerifyFeedback2, AkanToolPack2, MailToolPack2, McpUnauthorized2, McpClient2, McpToolPack2, SessionToolPack2, WebToolPack2, AkanCodePlugins2, AkanEnvKeys2, akanSystemPrompt2, CodeSessionIndex2, AkanCodeServices2, CodeAgentAsks2, CodeAgentEventMapper2, CodeAgentGate2, CodeAgentUi2, CodeMailbox2, CodeSessionFork2, CodeAgent2 };
|