@ashx-j/lunr 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin-extensions/lunr-todos.d.ts +7 -6
- package/dist/builtin-extensions/lunr-todos.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-todos.js +24 -11
- package/dist/builtin-extensions/lunr-todos.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js +9 -5
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +31 -3
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts +1 -0
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js +14 -4
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +4 -3
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +12 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +3 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +8 -0
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +22 -2
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.js +38 -230
- package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/render-search-chrome.d.ts +35 -0
- package/dist/builtin-extensions/pi-web-access/render-search-chrome.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/render-search-chrome.js +93 -0
- package/dist/builtin-extensions/pi-web-access/render-search-chrome.js.map +1 -0
- package/dist/catalog/models.json +1 -1
- package/dist/catalog/providers/cloudflare-workers-ai.json +1 -1
- package/dist/catalog/providers/huggingface.json +1 -1
- package/dist/catalog/providers/opencode-go.json +1 -1
- package/dist/catalog/providers/openrouter.json +1 -1
- package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
- package/dist/catalog/publication.json +3 -3
- package/dist/core/context-breakdown.d.ts +21 -1
- package/dist/core/context-breakdown.d.ts.map +1 -1
- package/dist/core/context-breakdown.js +58 -2
- package/dist/core/context-breakdown.js.map +1 -1
- package/dist/core/permission-mode-control.d.ts +27 -0
- package/dist/core/permission-mode-control.d.ts.map +1 -0
- package/dist/core/permission-mode-control.js +39 -0
- package/dist/core/permission-mode-control.js.map +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +0 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +14 -20
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +2 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +10 -0
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +1 -0
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/context-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/context-view.js +3 -1
- package/dist/modes/interactive/components/context-view.js.map +1 -1
- package/dist/modes/interactive/components/plan-message.d.ts +17 -0
- package/dist/modes/interactive/components/plan-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/plan-message.js +43 -0
- package/dist/modes/interactive/components/plan-message.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +3 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/usage-view.d.ts +4 -25
- package/dist/modes/interactive/components/usage-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/usage-view.js +17 -69
- package/dist/modes/interactive/components/usage-view.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +1 -0
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +1 -0
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +17 -6
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +196 -114
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/smooth-streaming.d.ts +39 -6
- package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
- package/dist/modes/interactive/smooth-streaming.js +160 -73
- package/dist/modes/interactive/smooth-streaming.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +7 -3
- package/dist/utils/clipboard-image.js.map +1 -1
- package/docs/terminal-setup.md +13 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -22,9 +22,20 @@ export interface ContextBreakdownInput {
|
|
|
22
22
|
messages: ReadonlyArray<AgentMessage>;
|
|
23
23
|
contextWindow: number;
|
|
24
24
|
}
|
|
25
|
+
export interface ContextFileBreakdown {
|
|
26
|
+
/** Basename of the context file (AGENTS.md, CLAUDE.md, …). */
|
|
27
|
+
label: string;
|
|
28
|
+
/** Path from the <project_instructions path="…"> wrapper, when present. */
|
|
29
|
+
path?: string;
|
|
30
|
+
tokens: number;
|
|
31
|
+
}
|
|
25
32
|
export interface ContextBreakdown {
|
|
26
|
-
/** System prompt
|
|
33
|
+
/** System prompt minus <project_context> and the skills block. */
|
|
27
34
|
systemPrompt: number;
|
|
35
|
+
/** Each AGENTS.md / context file from <project_instructions>. */
|
|
36
|
+
contextFiles: ContextFileBreakdown[];
|
|
37
|
+
/** formatSkillsForPrompt section, if present. */
|
|
38
|
+
skills: number;
|
|
28
39
|
/** Serialized tool schemas (name + description + JSON parameter schema). */
|
|
29
40
|
toolDefinitions: number;
|
|
30
41
|
/** User and extension ("custom") messages. */
|
|
@@ -54,6 +65,15 @@ export interface ContextBreakdown {
|
|
|
54
65
|
summaries: number;
|
|
55
66
|
};
|
|
56
67
|
}
|
|
68
|
+
/** Split a live system prompt into base / context-file / skills parts. */
|
|
69
|
+
export declare function splitSystemPromptSections(systemPrompt: string): {
|
|
70
|
+
base: string;
|
|
71
|
+
contextFiles: Array<{
|
|
72
|
+
path?: string;
|
|
73
|
+
content: string;
|
|
74
|
+
}>;
|
|
75
|
+
skills: string;
|
|
76
|
+
};
|
|
57
77
|
/**
|
|
58
78
|
* Estimate the token cost of the tool definitions sent to the model.
|
|
59
79
|
* The wire format is roughly `{ name, description, parameters(JSON schema) }` per tool.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-breakdown.d.ts","sourceRoot":"","sources":["../../src/core/context-breakdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAC3C,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAChC,
|
|
1
|
+
{"version":3,"file":"context-breakdown.d.ts","sourceRoot":"","sources":["../../src/core/context-breakdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAC3C,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACpC,gEAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,6EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAChC,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,YAAY,EAAE,oBAAoB,EAAE,CAAC;IACrC,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,MAAM,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KAClB,CAAC;CACF;AAmBD,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,MAAM,EAAE,MAAM,CAAC;CACf,CAsBA;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,aAAa,CAAC,oBAAoB,CAAC,GAAG,MAAM,CAY/F;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,qBAAqB,GAAG,gBAAgB,CAgGtF","sourcesContent":["/**\n * lunr: Context window breakdown estimation for the /context command.\n *\n * Pure functions that estimate (chars/4, same heuristic as `estimateTokens` in\n * compaction.ts) what consumes the model's context window: the system prompt\n * (project context files are baked into it), the registered tool definitions,\n * and the live session messages split into user / assistant text / thinking /\n * tool calls / tool results / summaries.\n *\n * Pass the same message list the context actually contains (post-compaction,\n * that is `AgentSession.messages`) so the breakdown matches reality.\n */\n\nimport type { AgentMessage } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage } from \"@ashx-j/lunr-ai/compat\";\nimport { estimateTokens } from \"./compaction/index.ts\";\n\nexport interface ContextBreakdownTool {\n\tname: string;\n\tdescription: string;\n\tparameters: unknown;\n}\n\nexport interface ContextBreakdownInput {\n\tsystemPrompt: string;\n\ttools: ReadonlyArray<ContextBreakdownTool>;\n\tmessages: ReadonlyArray<AgentMessage>;\n\tcontextWindow: number;\n}\n\nexport interface ContextFileBreakdown {\n\t/** Basename of the context file (AGENTS.md, CLAUDE.md, …). */\n\tlabel: string;\n\t/** Path from the <project_instructions path=\"…\"> wrapper, when present. */\n\tpath?: string;\n\ttokens: number;\n}\n\nexport interface ContextBreakdown {\n\t/** System prompt minus <project_context> and the skills block. */\n\tsystemPrompt: number;\n\t/** Each AGENTS.md / context file from <project_instructions>. */\n\tcontextFiles: ContextFileBreakdown[];\n\t/** formatSkillsForPrompt section, if present. */\n\tskills: number;\n\t/** Serialized tool schemas (name + description + JSON parameter schema). */\n\ttoolDefinitions: number;\n\t/** User and extension (\"custom\") messages. */\n\tuser: number;\n\t/** Assistant text blocks. */\n\tassistantText: number;\n\t/** Assistant thinking blocks. */\n\tthinking: number;\n\t/** Assistant tool call blocks (name + serialized arguments). */\n\ttoolCalls: number;\n\t/** Tool result messages and bash execution output. */\n\ttoolResults: number;\n\t/** Compaction and branch summaries. */\n\tsummaries: number;\n\t/** Sum of all categories above. */\n\ttotal: number;\n\tcontextWindow: number;\n\t/** max(0, contextWindow - total) */\n\tfree: number;\n\t/** Per-category message counts (not token counts). */\n\tcounts: {\n\t\tuser: number;\n\t\tassistantText: number;\n\t\tthinking: number;\n\t\ttoolCalls: number;\n\t\ttoolResults: number;\n\t\tsummaries: number;\n\t};\n}\n\nconst CHARS_PER_TOKEN = 4;\nconst PROJECT_CONTEXT_RE = /<project_context>[\\s\\S]*?<\\/project_context>/;\nconst PROJECT_INSTRUCTIONS_RE = /<project_instructions(?:\\s+path=\"([^\"]*)\")?>([\\s\\S]*?)<\\/project_instructions>/g;\nconst SKILLS_BLOCK_RE =\n\t/\\n\\nThe following skills provide specialized instructions for specific tasks\\.[\\s\\S]*<\\/available_skills>/;\n\nfunction estimateChars(text: string): number {\n\treturn text.length === 0 ? 0 : Math.ceil(text.length / CHARS_PER_TOKEN);\n}\n\nfunction contextFileLabel(filePath: string | undefined): string {\n\tif (!filePath) return \"AGENTS.md\";\n\tconst normalized = filePath.replace(/\\\\/g, \"/\");\n\tconst slash = normalized.lastIndexOf(\"/\");\n\treturn slash === -1 ? normalized : normalized.slice(slash + 1);\n}\n\n/** Split a live system prompt into base / context-file / skills parts. */\nexport function splitSystemPromptSections(systemPrompt: string): {\n\tbase: string;\n\tcontextFiles: Array<{ path?: string; content: string }>;\n\tskills: string;\n} {\n\tlet remaining = systemPrompt;\n\tconst contextFiles: Array<{ path?: string; content: string }> = [];\n\tconst contextMatch = remaining.match(PROJECT_CONTEXT_RE);\n\tif (contextMatch) {\n\t\tconst block = contextMatch[0];\n\t\tPROJECT_INSTRUCTIONS_RE.lastIndex = 0;\n\t\tfor (const match of block.matchAll(PROJECT_INSTRUCTIONS_RE)) {\n\t\t\tcontextFiles.push({\n\t\t\t\tpath: match[1],\n\t\t\t\tcontent: match[2] ?? \"\",\n\t\t\t});\n\t\t}\n\t\tremaining = remaining.replace(block, \"\");\n\t}\n\tlet skills = \"\";\n\tconst skillsMatch = remaining.match(SKILLS_BLOCK_RE);\n\tif (skillsMatch) {\n\t\tskills = skillsMatch[0];\n\t\tremaining = remaining.replace(skills, \"\");\n\t}\n\treturn { base: remaining, contextFiles, skills };\n}\n\n/**\n * Estimate the token cost of the tool definitions sent to the model.\n * The wire format is roughly `{ name, description, parameters(JSON schema) }` per tool.\n */\nexport function estimateToolDefinitionTokens(tools: ReadonlyArray<ContextBreakdownTool>): number {\n\tlet chars = 0;\n\tfor (const tool of tools) {\n\t\tchars += tool.name.length + tool.description.length;\n\t\ttry {\n\t\t\tconst schema = JSON.stringify(tool.parameters);\n\t\t\tif (schema) chars += schema.length;\n\t\t} catch {\n\t\t\t// Unserializable schema — skip rather than fail the whole breakdown.\n\t\t}\n\t}\n\treturn Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\n/**\n * Compute the estimated context window breakdown.\n * All numbers are chars/4 estimates, matching `estimateTokens`.\n */\nexport function computeContextBreakdown(input: ContextBreakdownInput): ContextBreakdown {\n\tconst sections = splitSystemPromptSections(input.systemPrompt);\n\tconst systemPrompt = estimateChars(sections.base);\n\tconst contextFiles = sections.contextFiles.map((file) => ({\n\t\tlabel: contextFileLabel(file.path),\n\t\tpath: file.path,\n\t\ttokens: estimateChars(file.content),\n\t}));\n\tconst skills = estimateChars(sections.skills);\n\tconst toolDefinitions = estimateToolDefinitionTokens(input.tools);\n\n\tlet user = 0;\n\tlet assistantText = 0;\n\tlet thinking = 0;\n\tlet toolCalls = 0;\n\tlet toolResults = 0;\n\tlet summaries = 0;\n\n\tconst counts = {\n\t\tuser: 0,\n\t\tassistantText: 0,\n\t\tthinking: 0,\n\t\ttoolCalls: 0,\n\t\ttoolResults: 0,\n\t\tsummaries: 0,\n\t};\n\n\tfor (const message of input.messages) {\n\t\tswitch (message.role) {\n\t\t\tcase \"assistant\": {\n\t\t\t\tlet textChars = 0;\n\t\t\t\tlet thinkingChars = 0;\n\t\t\t\tlet toolCallChars = 0;\n\t\t\t\tfor (const block of (message as AssistantMessage).content) {\n\t\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\t\ttextChars += block.text.length;\n\t\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\t\tthinkingChars += block.thinking.length;\n\t\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\t\ttoolCallChars += block.name.length + JSON.stringify(block.arguments).length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tassistantText += Math.ceil(textChars / CHARS_PER_TOKEN);\n\t\t\t\tthinking += Math.ceil(thinkingChars / CHARS_PER_TOKEN);\n\t\t\t\ttoolCalls += Math.ceil(toolCallChars / CHARS_PER_TOKEN);\n\t\t\t\tif (textChars > 0) counts.assistantText++;\n\t\t\t\tif (thinkingChars > 0) counts.thinking++;\n\t\t\t\tif (toolCallChars > 0) counts.toolCalls++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"user\":\n\t\t\tcase \"custom\":\n\t\t\t\tuser += estimateTokens(message);\n\t\t\t\tcounts.user++;\n\t\t\t\tbreak;\n\t\t\tcase \"toolResult\":\n\t\t\tcase \"bashExecution\":\n\t\t\t\ttoolResults += estimateTokens(message);\n\t\t\t\tcounts.toolResults++;\n\t\t\t\tbreak;\n\t\t\tcase \"branchSummary\":\n\t\t\tcase \"compactionSummary\":\n\t\t\t\tsummaries += estimateTokens(message);\n\t\t\t\tcounts.summaries++;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst contextFileTokens = contextFiles.reduce((sum, file) => sum + file.tokens, 0);\n\tconst total =\n\t\tsystemPrompt +\n\t\tcontextFileTokens +\n\t\tskills +\n\t\ttoolDefinitions +\n\t\tuser +\n\t\tassistantText +\n\t\tthinking +\n\t\ttoolCalls +\n\t\ttoolResults +\n\t\tsummaries;\n\treturn {\n\t\tsystemPrompt,\n\t\tcontextFiles,\n\t\tskills,\n\t\ttoolDefinitions,\n\t\tuser,\n\t\tassistantText,\n\t\tthinking,\n\t\ttoolCalls,\n\t\ttoolResults,\n\t\tsummaries,\n\t\ttotal,\n\t\tcontextWindow: input.contextWindow,\n\t\tfree: Math.max(0, input.contextWindow - total),\n\t\tcounts,\n\t};\n}\n"]}
|
|
@@ -12,6 +12,43 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { estimateTokens } from "./compaction/index.js";
|
|
14
14
|
const CHARS_PER_TOKEN = 4;
|
|
15
|
+
const PROJECT_CONTEXT_RE = /<project_context>[\s\S]*?<\/project_context>/;
|
|
16
|
+
const PROJECT_INSTRUCTIONS_RE = /<project_instructions(?:\s+path="([^"]*)")?>([\s\S]*?)<\/project_instructions>/g;
|
|
17
|
+
const SKILLS_BLOCK_RE = /\n\nThe following skills provide specialized instructions for specific tasks\.[\s\S]*<\/available_skills>/;
|
|
18
|
+
function estimateChars(text) {
|
|
19
|
+
return text.length === 0 ? 0 : Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
20
|
+
}
|
|
21
|
+
function contextFileLabel(filePath) {
|
|
22
|
+
if (!filePath)
|
|
23
|
+
return "AGENTS.md";
|
|
24
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
25
|
+
const slash = normalized.lastIndexOf("/");
|
|
26
|
+
return slash === -1 ? normalized : normalized.slice(slash + 1);
|
|
27
|
+
}
|
|
28
|
+
/** Split a live system prompt into base / context-file / skills parts. */
|
|
29
|
+
export function splitSystemPromptSections(systemPrompt) {
|
|
30
|
+
let remaining = systemPrompt;
|
|
31
|
+
const contextFiles = [];
|
|
32
|
+
const contextMatch = remaining.match(PROJECT_CONTEXT_RE);
|
|
33
|
+
if (contextMatch) {
|
|
34
|
+
const block = contextMatch[0];
|
|
35
|
+
PROJECT_INSTRUCTIONS_RE.lastIndex = 0;
|
|
36
|
+
for (const match of block.matchAll(PROJECT_INSTRUCTIONS_RE)) {
|
|
37
|
+
contextFiles.push({
|
|
38
|
+
path: match[1],
|
|
39
|
+
content: match[2] ?? "",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
remaining = remaining.replace(block, "");
|
|
43
|
+
}
|
|
44
|
+
let skills = "";
|
|
45
|
+
const skillsMatch = remaining.match(SKILLS_BLOCK_RE);
|
|
46
|
+
if (skillsMatch) {
|
|
47
|
+
skills = skillsMatch[0];
|
|
48
|
+
remaining = remaining.replace(skills, "");
|
|
49
|
+
}
|
|
50
|
+
return { base: remaining, contextFiles, skills };
|
|
51
|
+
}
|
|
15
52
|
/**
|
|
16
53
|
* Estimate the token cost of the tool definitions sent to the model.
|
|
17
54
|
* The wire format is roughly `{ name, description, parameters(JSON schema) }` per tool.
|
|
@@ -36,7 +73,14 @@ export function estimateToolDefinitionTokens(tools) {
|
|
|
36
73
|
* All numbers are chars/4 estimates, matching `estimateTokens`.
|
|
37
74
|
*/
|
|
38
75
|
export function computeContextBreakdown(input) {
|
|
39
|
-
const
|
|
76
|
+
const sections = splitSystemPromptSections(input.systemPrompt);
|
|
77
|
+
const systemPrompt = estimateChars(sections.base);
|
|
78
|
+
const contextFiles = sections.contextFiles.map((file) => ({
|
|
79
|
+
label: contextFileLabel(file.path),
|
|
80
|
+
path: file.path,
|
|
81
|
+
tokens: estimateChars(file.content),
|
|
82
|
+
}));
|
|
83
|
+
const skills = estimateChars(sections.skills);
|
|
40
84
|
const toolDefinitions = estimateToolDefinitionTokens(input.tools);
|
|
41
85
|
let user = 0;
|
|
42
86
|
let assistantText = 0;
|
|
@@ -97,9 +141,21 @@ export function computeContextBreakdown(input) {
|
|
|
97
141
|
break;
|
|
98
142
|
}
|
|
99
143
|
}
|
|
100
|
-
const
|
|
144
|
+
const contextFileTokens = contextFiles.reduce((sum, file) => sum + file.tokens, 0);
|
|
145
|
+
const total = systemPrompt +
|
|
146
|
+
contextFileTokens +
|
|
147
|
+
skills +
|
|
148
|
+
toolDefinitions +
|
|
149
|
+
user +
|
|
150
|
+
assistantText +
|
|
151
|
+
thinking +
|
|
152
|
+
toolCalls +
|
|
153
|
+
toolResults +
|
|
154
|
+
summaries;
|
|
101
155
|
return {
|
|
102
156
|
systemPrompt,
|
|
157
|
+
contextFiles,
|
|
158
|
+
skills,
|
|
103
159
|
toolDefinitions,
|
|
104
160
|
user,
|
|
105
161
|
assistantText,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-breakdown.js","sourceRoot":"","sources":["../../src/core/context-breakdown.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunr: Context window breakdown estimation for the /context command.\n *\n * Pure functions that estimate (chars/4, same heuristic as `estimateTokens` in\n * compaction.ts) what consumes the model's context window: the system prompt\n * (project context files are baked into it), the registered tool definitions,\n * and the live session messages split into user / assistant text / thinking /\n * tool calls / tool results / summaries.\n *\n * Pass the same message list the context actually contains (post-compaction,\n * that is `AgentSession.messages`) so the breakdown matches reality.\n */\n\nimport type { AgentMessage } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage } from \"@ashx-j/lunr-ai/compat\";\nimport { estimateTokens } from \"./compaction/index.ts\";\n\nexport interface ContextBreakdownTool {\n\tname: string;\n\tdescription: string;\n\tparameters: unknown;\n}\n\nexport interface ContextBreakdownInput {\n\tsystemPrompt: string;\n\ttools: ReadonlyArray<ContextBreakdownTool>;\n\tmessages: ReadonlyArray<AgentMessage>;\n\tcontextWindow: number;\n}\n\nexport interface ContextBreakdown {\n\t/** System prompt, including appended project context files (AGENTS.md etc.). */\n\tsystemPrompt: number;\n\t/** Serialized tool schemas (name + description + JSON parameter schema). */\n\ttoolDefinitions: number;\n\t/** User and extension (\"custom\") messages. */\n\tuser: number;\n\t/** Assistant text blocks. */\n\tassistantText: number;\n\t/** Assistant thinking blocks. */\n\tthinking: number;\n\t/** Assistant tool call blocks (name + serialized arguments). */\n\ttoolCalls: number;\n\t/** Tool result messages and bash execution output. */\n\ttoolResults: number;\n\t/** Compaction and branch summaries. */\n\tsummaries: number;\n\t/** Sum of all categories above. */\n\ttotal: number;\n\tcontextWindow: number;\n\t/** max(0, contextWindow - total) */\n\tfree: number;\n\t/** Per-category message counts (not token counts). */\n\tcounts: {\n\t\tuser: number;\n\t\tassistantText: number;\n\t\tthinking: number;\n\t\ttoolCalls: number;\n\t\ttoolResults: number;\n\t\tsummaries: number;\n\t};\n}\n\nconst CHARS_PER_TOKEN = 4;\n\n/**\n * Estimate the token cost of the tool definitions sent to the model.\n * The wire format is roughly `{ name, description, parameters(JSON schema) }` per tool.\n */\nexport function estimateToolDefinitionTokens(tools: ReadonlyArray<ContextBreakdownTool>): number {\n\tlet chars = 0;\n\tfor (const tool of tools) {\n\t\tchars += tool.name.length + tool.description.length;\n\t\ttry {\n\t\t\tconst schema = JSON.stringify(tool.parameters);\n\t\t\tif (schema) chars += schema.length;\n\t\t} catch {\n\t\t\t// Unserializable schema — skip rather than fail the whole breakdown.\n\t\t}\n\t}\n\treturn Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\n/**\n * Compute the estimated context window breakdown.\n * All numbers are chars/4 estimates, matching `estimateTokens`.\n */\nexport function computeContextBreakdown(input: ContextBreakdownInput): ContextBreakdown {\n\tconst systemPrompt = Math.ceil(input.systemPrompt.length / CHARS_PER_TOKEN);\n\tconst toolDefinitions = estimateToolDefinitionTokens(input.tools);\n\n\tlet user = 0;\n\tlet assistantText = 0;\n\tlet thinking = 0;\n\tlet toolCalls = 0;\n\tlet toolResults = 0;\n\tlet summaries = 0;\n\n\tconst counts = {\n\t\tuser: 0,\n\t\tassistantText: 0,\n\t\tthinking: 0,\n\t\ttoolCalls: 0,\n\t\ttoolResults: 0,\n\t\tsummaries: 0,\n\t};\n\n\tfor (const message of input.messages) {\n\t\tswitch (message.role) {\n\t\t\tcase \"assistant\": {\n\t\t\t\tlet textChars = 0;\n\t\t\t\tlet thinkingChars = 0;\n\t\t\t\tlet toolCallChars = 0;\n\t\t\t\tfor (const block of (message as AssistantMessage).content) {\n\t\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\t\ttextChars += block.text.length;\n\t\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\t\tthinkingChars += block.thinking.length;\n\t\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\t\ttoolCallChars += block.name.length + JSON.stringify(block.arguments).length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tassistantText += Math.ceil(textChars / CHARS_PER_TOKEN);\n\t\t\t\tthinking += Math.ceil(thinkingChars / CHARS_PER_TOKEN);\n\t\t\t\ttoolCalls += Math.ceil(toolCallChars / CHARS_PER_TOKEN);\n\t\t\t\tif (textChars > 0) counts.assistantText++;\n\t\t\t\tif (thinkingChars > 0) counts.thinking++;\n\t\t\t\tif (toolCallChars > 0) counts.toolCalls++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"user\":\n\t\t\tcase \"custom\":\n\t\t\t\tuser += estimateTokens(message);\n\t\t\t\tcounts.user++;\n\t\t\t\tbreak;\n\t\t\tcase \"toolResult\":\n\t\t\tcase \"bashExecution\":\n\t\t\t\ttoolResults += estimateTokens(message);\n\t\t\t\tcounts.toolResults++;\n\t\t\t\tbreak;\n\t\t\tcase \"branchSummary\":\n\t\t\tcase \"compactionSummary\":\n\t\t\t\tsummaries += estimateTokens(message);\n\t\t\t\tcounts.summaries++;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst total = systemPrompt + toolDefinitions + user + assistantText + thinking + toolCalls + toolResults + summaries;\n\treturn {\n\t\tsystemPrompt,\n\t\ttoolDefinitions,\n\t\tuser,\n\t\tassistantText,\n\t\tthinking,\n\t\ttoolCalls,\n\t\ttoolResults,\n\t\tsummaries,\n\t\ttotal,\n\t\tcontextWindow: input.contextWindow,\n\t\tfree: Math.max(0, input.contextWindow - total),\n\t\tcounts,\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context-breakdown.js","sourceRoot":"","sources":["../../src/core/context-breakdown.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunr: Context window breakdown estimation for the /context command.\n *\n * Pure functions that estimate (chars/4, same heuristic as `estimateTokens` in\n * compaction.ts) what consumes the model's context window: the system prompt\n * (project context files are baked into it), the registered tool definitions,\n * and the live session messages split into user / assistant text / thinking /\n * tool calls / tool results / summaries.\n *\n * Pass the same message list the context actually contains (post-compaction,\n * that is `AgentSession.messages`) so the breakdown matches reality.\n */\n\nimport type { AgentMessage } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage } from \"@ashx-j/lunr-ai/compat\";\nimport { estimateTokens } from \"./compaction/index.ts\";\n\nexport interface ContextBreakdownTool {\n\tname: string;\n\tdescription: string;\n\tparameters: unknown;\n}\n\nexport interface ContextBreakdownInput {\n\tsystemPrompt: string;\n\ttools: ReadonlyArray<ContextBreakdownTool>;\n\tmessages: ReadonlyArray<AgentMessage>;\n\tcontextWindow: number;\n}\n\nexport interface ContextFileBreakdown {\n\t/** Basename of the context file (AGENTS.md, CLAUDE.md, …). */\n\tlabel: string;\n\t/** Path from the <project_instructions path=\"…\"> wrapper, when present. */\n\tpath?: string;\n\ttokens: number;\n}\n\nexport interface ContextBreakdown {\n\t/** System prompt minus <project_context> and the skills block. */\n\tsystemPrompt: number;\n\t/** Each AGENTS.md / context file from <project_instructions>. */\n\tcontextFiles: ContextFileBreakdown[];\n\t/** formatSkillsForPrompt section, if present. */\n\tskills: number;\n\t/** Serialized tool schemas (name + description + JSON parameter schema). */\n\ttoolDefinitions: number;\n\t/** User and extension (\"custom\") messages. */\n\tuser: number;\n\t/** Assistant text blocks. */\n\tassistantText: number;\n\t/** Assistant thinking blocks. */\n\tthinking: number;\n\t/** Assistant tool call blocks (name + serialized arguments). */\n\ttoolCalls: number;\n\t/** Tool result messages and bash execution output. */\n\ttoolResults: number;\n\t/** Compaction and branch summaries. */\n\tsummaries: number;\n\t/** Sum of all categories above. */\n\ttotal: number;\n\tcontextWindow: number;\n\t/** max(0, contextWindow - total) */\n\tfree: number;\n\t/** Per-category message counts (not token counts). */\n\tcounts: {\n\t\tuser: number;\n\t\tassistantText: number;\n\t\tthinking: number;\n\t\ttoolCalls: number;\n\t\ttoolResults: number;\n\t\tsummaries: number;\n\t};\n}\n\nconst CHARS_PER_TOKEN = 4;\nconst PROJECT_CONTEXT_RE = /<project_context>[\\s\\S]*?<\\/project_context>/;\nconst PROJECT_INSTRUCTIONS_RE = /<project_instructions(?:\\s+path=\"([^\"]*)\")?>([\\s\\S]*?)<\\/project_instructions>/g;\nconst SKILLS_BLOCK_RE =\n\t/\\n\\nThe following skills provide specialized instructions for specific tasks\\.[\\s\\S]*<\\/available_skills>/;\n\nfunction estimateChars(text: string): number {\n\treturn text.length === 0 ? 0 : Math.ceil(text.length / CHARS_PER_TOKEN);\n}\n\nfunction contextFileLabel(filePath: string | undefined): string {\n\tif (!filePath) return \"AGENTS.md\";\n\tconst normalized = filePath.replace(/\\\\/g, \"/\");\n\tconst slash = normalized.lastIndexOf(\"/\");\n\treturn slash === -1 ? normalized : normalized.slice(slash + 1);\n}\n\n/** Split a live system prompt into base / context-file / skills parts. */\nexport function splitSystemPromptSections(systemPrompt: string): {\n\tbase: string;\n\tcontextFiles: Array<{ path?: string; content: string }>;\n\tskills: string;\n} {\n\tlet remaining = systemPrompt;\n\tconst contextFiles: Array<{ path?: string; content: string }> = [];\n\tconst contextMatch = remaining.match(PROJECT_CONTEXT_RE);\n\tif (contextMatch) {\n\t\tconst block = contextMatch[0];\n\t\tPROJECT_INSTRUCTIONS_RE.lastIndex = 0;\n\t\tfor (const match of block.matchAll(PROJECT_INSTRUCTIONS_RE)) {\n\t\t\tcontextFiles.push({\n\t\t\t\tpath: match[1],\n\t\t\t\tcontent: match[2] ?? \"\",\n\t\t\t});\n\t\t}\n\t\tremaining = remaining.replace(block, \"\");\n\t}\n\tlet skills = \"\";\n\tconst skillsMatch = remaining.match(SKILLS_BLOCK_RE);\n\tif (skillsMatch) {\n\t\tskills = skillsMatch[0];\n\t\tremaining = remaining.replace(skills, \"\");\n\t}\n\treturn { base: remaining, contextFiles, skills };\n}\n\n/**\n * Estimate the token cost of the tool definitions sent to the model.\n * The wire format is roughly `{ name, description, parameters(JSON schema) }` per tool.\n */\nexport function estimateToolDefinitionTokens(tools: ReadonlyArray<ContextBreakdownTool>): number {\n\tlet chars = 0;\n\tfor (const tool of tools) {\n\t\tchars += tool.name.length + tool.description.length;\n\t\ttry {\n\t\t\tconst schema = JSON.stringify(tool.parameters);\n\t\t\tif (schema) chars += schema.length;\n\t\t} catch {\n\t\t\t// Unserializable schema — skip rather than fail the whole breakdown.\n\t\t}\n\t}\n\treturn Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\n/**\n * Compute the estimated context window breakdown.\n * All numbers are chars/4 estimates, matching `estimateTokens`.\n */\nexport function computeContextBreakdown(input: ContextBreakdownInput): ContextBreakdown {\n\tconst sections = splitSystemPromptSections(input.systemPrompt);\n\tconst systemPrompt = estimateChars(sections.base);\n\tconst contextFiles = sections.contextFiles.map((file) => ({\n\t\tlabel: contextFileLabel(file.path),\n\t\tpath: file.path,\n\t\ttokens: estimateChars(file.content),\n\t}));\n\tconst skills = estimateChars(sections.skills);\n\tconst toolDefinitions = estimateToolDefinitionTokens(input.tools);\n\n\tlet user = 0;\n\tlet assistantText = 0;\n\tlet thinking = 0;\n\tlet toolCalls = 0;\n\tlet toolResults = 0;\n\tlet summaries = 0;\n\n\tconst counts = {\n\t\tuser: 0,\n\t\tassistantText: 0,\n\t\tthinking: 0,\n\t\ttoolCalls: 0,\n\t\ttoolResults: 0,\n\t\tsummaries: 0,\n\t};\n\n\tfor (const message of input.messages) {\n\t\tswitch (message.role) {\n\t\t\tcase \"assistant\": {\n\t\t\t\tlet textChars = 0;\n\t\t\t\tlet thinkingChars = 0;\n\t\t\t\tlet toolCallChars = 0;\n\t\t\t\tfor (const block of (message as AssistantMessage).content) {\n\t\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\t\ttextChars += block.text.length;\n\t\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\t\tthinkingChars += block.thinking.length;\n\t\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\t\ttoolCallChars += block.name.length + JSON.stringify(block.arguments).length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tassistantText += Math.ceil(textChars / CHARS_PER_TOKEN);\n\t\t\t\tthinking += Math.ceil(thinkingChars / CHARS_PER_TOKEN);\n\t\t\t\ttoolCalls += Math.ceil(toolCallChars / CHARS_PER_TOKEN);\n\t\t\t\tif (textChars > 0) counts.assistantText++;\n\t\t\t\tif (thinkingChars > 0) counts.thinking++;\n\t\t\t\tif (toolCallChars > 0) counts.toolCalls++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"user\":\n\t\t\tcase \"custom\":\n\t\t\t\tuser += estimateTokens(message);\n\t\t\t\tcounts.user++;\n\t\t\t\tbreak;\n\t\t\tcase \"toolResult\":\n\t\t\tcase \"bashExecution\":\n\t\t\t\ttoolResults += estimateTokens(message);\n\t\t\t\tcounts.toolResults++;\n\t\t\t\tbreak;\n\t\t\tcase \"branchSummary\":\n\t\t\tcase \"compactionSummary\":\n\t\t\t\tsummaries += estimateTokens(message);\n\t\t\t\tcounts.summaries++;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst contextFileTokens = contextFiles.reduce((sum, file) => sum + file.tokens, 0);\n\tconst total =\n\t\tsystemPrompt +\n\t\tcontextFileTokens +\n\t\tskills +\n\t\ttoolDefinitions +\n\t\tuser +\n\t\tassistantText +\n\t\tthinking +\n\t\ttoolCalls +\n\t\ttoolResults +\n\t\tsummaries;\n\treturn {\n\t\tsystemPrompt,\n\t\tcontextFiles,\n\t\tskills,\n\t\ttoolDefinitions,\n\t\tuser,\n\t\tassistantText,\n\t\tthinking,\n\t\ttoolCalls,\n\t\ttoolResults,\n\t\tsummaries,\n\t\ttotal,\n\t\tcontextWindow: input.contextWindow,\n\t\tfree: Math.max(0, input.contextWindow - total),\n\t\tcounts,\n\t};\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PermissionMode } from "./permissions.ts";
|
|
2
|
+
/**
|
|
3
|
+
* lunR: permission-mode control bridge for the goal extension.
|
|
4
|
+
*
|
|
5
|
+
* Exposed on `globalThis` under `Symbol.for("@lunr/permission-mode-control")`
|
|
6
|
+
* so the vendored goal extension can force session auto while a goal is
|
|
7
|
+
* active without importing InteractiveMode.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PERMISSION_MODE_CONTROL_SYMBOL: unique symbol;
|
|
10
|
+
export interface PermissionModeControlBridge {
|
|
11
|
+
enterGoalAuto(): void;
|
|
12
|
+
leaveGoalAuto(): void;
|
|
13
|
+
}
|
|
14
|
+
export type GoalPermissionEvent = "enter" | "leave";
|
|
15
|
+
/**
|
|
16
|
+
* Pure save/restore helper for `/goal` forcing session auto.
|
|
17
|
+
* `saved` is the mode in effect before the goal stretch (undefined = none).
|
|
18
|
+
*/
|
|
19
|
+
export declare function nextModeForGoal(current: PermissionMode, event: GoalPermissionEvent, saved: PermissionMode | undefined): {
|
|
20
|
+
mode: PermissionMode;
|
|
21
|
+
saved: PermissionMode | undefined;
|
|
22
|
+
};
|
|
23
|
+
export declare function registerPermissionModeControlBridge(bridge: PermissionModeControlBridge): void;
|
|
24
|
+
export declare function getPermissionModeControlBridge(): PermissionModeControlBridge | undefined;
|
|
25
|
+
export declare function enterGoalAuto(): void;
|
|
26
|
+
export declare function leaveGoalAuto(): void;
|
|
27
|
+
//# sourceMappingURL=permission-mode-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-mode-control.d.ts","sourceRoot":"","sources":["../../src/core/permission-mode-control.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;;;;GAMG;AAEH,eAAO,MAAM,8BAA8B,eAA8C,CAAC;AAE1F,MAAM,WAAW,2BAA2B;IAC3C,aAAa,IAAI,IAAI,CAAC;IACtB,aAAa,IAAI,IAAI,CAAC;CACtB;AAED,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,OAAO,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,mBAAmB,EAC1B,KAAK,EAAE,cAAc,GAAG,SAAS,GAC/B;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,cAAc,GAAG,SAAS,CAAA;CAAE,CAW7D;AAED,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,2BAA2B,GAAG,IAAI,CAE7F;AAED,wBAAgB,8BAA8B,IAAI,2BAA2B,GAAG,SAAS,CAIxF;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC","sourcesContent":["import type { PermissionMode } from \"./permissions.ts\";\n\n/**\n * lunR: permission-mode control bridge for the goal extension.\n *\n * Exposed on `globalThis` under `Symbol.for(\"@lunr/permission-mode-control\")`\n * so the vendored goal extension can force session auto while a goal is\n * active without importing InteractiveMode.\n */\n\nexport const PERMISSION_MODE_CONTROL_SYMBOL = Symbol.for(\"@lunr/permission-mode-control\");\n\nexport interface PermissionModeControlBridge {\n\tenterGoalAuto(): void;\n\tleaveGoalAuto(): void;\n}\n\nexport type GoalPermissionEvent = \"enter\" | \"leave\";\n\n/**\n * Pure save/restore helper for `/goal` forcing session auto.\n * `saved` is the mode in effect before the goal stretch (undefined = none).\n */\nexport function nextModeForGoal(\n\tcurrent: PermissionMode,\n\tevent: GoalPermissionEvent,\n\tsaved: PermissionMode | undefined,\n): { mode: PermissionMode; saved: PermissionMode | undefined } {\n\tif (event === \"enter\") {\n\t\tif (current === \"auto\") return { mode: \"auto\", saved };\n\t\t// Already inside a goal stretch: keep a hand-changed mode (yolo/plan/manual).\n\t\tif (saved !== undefined) return { mode: current, saved };\n\t\treturn { mode: \"auto\", saved: current };\n\t}\n\tif (current === \"auto\" && saved !== undefined) {\n\t\treturn { mode: saved, saved: undefined };\n\t}\n\treturn { mode: current, saved: undefined };\n}\n\nexport function registerPermissionModeControlBridge(bridge: PermissionModeControlBridge): void {\n\t(globalThis as Record<symbol, unknown>)[PERMISSION_MODE_CONTROL_SYMBOL] = bridge;\n}\n\nexport function getPermissionModeControlBridge(): PermissionModeControlBridge | undefined {\n\treturn (globalThis as Record<symbol, unknown>)[PERMISSION_MODE_CONTROL_SYMBOL] as\n\t\t| PermissionModeControlBridge\n\t\t| undefined;\n}\n\nexport function enterGoalAuto(): void {\n\tgetPermissionModeControlBridge()?.enterGoalAuto();\n}\n\nexport function leaveGoalAuto(): void {\n\tgetPermissionModeControlBridge()?.leaveGoalAuto();\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lunR: permission-mode control bridge for the goal extension.
|
|
3
|
+
*
|
|
4
|
+
* Exposed on `globalThis` under `Symbol.for("@lunr/permission-mode-control")`
|
|
5
|
+
* so the vendored goal extension can force session auto while a goal is
|
|
6
|
+
* active without importing InteractiveMode.
|
|
7
|
+
*/
|
|
8
|
+
export const PERMISSION_MODE_CONTROL_SYMBOL = Symbol.for("@lunr/permission-mode-control");
|
|
9
|
+
/**
|
|
10
|
+
* Pure save/restore helper for `/goal` forcing session auto.
|
|
11
|
+
* `saved` is the mode in effect before the goal stretch (undefined = none).
|
|
12
|
+
*/
|
|
13
|
+
export function nextModeForGoal(current, event, saved) {
|
|
14
|
+
if (event === "enter") {
|
|
15
|
+
if (current === "auto")
|
|
16
|
+
return { mode: "auto", saved };
|
|
17
|
+
// Already inside a goal stretch: keep a hand-changed mode (yolo/plan/manual).
|
|
18
|
+
if (saved !== undefined)
|
|
19
|
+
return { mode: current, saved };
|
|
20
|
+
return { mode: "auto", saved: current };
|
|
21
|
+
}
|
|
22
|
+
if (current === "auto" && saved !== undefined) {
|
|
23
|
+
return { mode: saved, saved: undefined };
|
|
24
|
+
}
|
|
25
|
+
return { mode: current, saved: undefined };
|
|
26
|
+
}
|
|
27
|
+
export function registerPermissionModeControlBridge(bridge) {
|
|
28
|
+
globalThis[PERMISSION_MODE_CONTROL_SYMBOL] = bridge;
|
|
29
|
+
}
|
|
30
|
+
export function getPermissionModeControlBridge() {
|
|
31
|
+
return globalThis[PERMISSION_MODE_CONTROL_SYMBOL];
|
|
32
|
+
}
|
|
33
|
+
export function enterGoalAuto() {
|
|
34
|
+
getPermissionModeControlBridge()?.enterGoalAuto();
|
|
35
|
+
}
|
|
36
|
+
export function leaveGoalAuto() {
|
|
37
|
+
getPermissionModeControlBridge()?.leaveGoalAuto();
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=permission-mode-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-mode-control.js","sourceRoot":"","sources":["../../src/core/permission-mode-control.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,8BAA8B,GAAG,MAAM,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;AAS1F;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC9B,OAAuB,EACvB,KAA0B,EAC1B,KAAiC,EAC6B;IAC9D,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACvB,IAAI,OAAO,KAAK,MAAM;YAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACvD,8EAA8E;QAC9E,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACzD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACzC,CAAC;IACD,IAAI,OAAO,KAAK,MAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAC1C,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAAA,CAC3C;AAED,MAAM,UAAU,mCAAmC,CAAC,MAAmC,EAAQ;IAC7F,UAAsC,CAAC,8BAA8B,CAAC,GAAG,MAAM,CAAC;AAAA,CACjF;AAED,MAAM,UAAU,8BAA8B,GAA4C;IACzF,OAAQ,UAAsC,CAAC,8BAA8B,CAEjE,CAAC;AAAA,CACb;AAED,MAAM,UAAU,aAAa,GAAS;IACrC,8BAA8B,EAAE,EAAE,aAAa,EAAE,CAAC;AAAA,CAClD;AAED,MAAM,UAAU,aAAa,GAAS;IACrC,8BAA8B,EAAE,EAAE,aAAa,EAAE,CAAC;AAAA,CAClD","sourcesContent":["import type { PermissionMode } from \"./permissions.ts\";\n\n/**\n * lunR: permission-mode control bridge for the goal extension.\n *\n * Exposed on `globalThis` under `Symbol.for(\"@lunr/permission-mode-control\")`\n * so the vendored goal extension can force session auto while a goal is\n * active without importing InteractiveMode.\n */\n\nexport const PERMISSION_MODE_CONTROL_SYMBOL = Symbol.for(\"@lunr/permission-mode-control\");\n\nexport interface PermissionModeControlBridge {\n\tenterGoalAuto(): void;\n\tleaveGoalAuto(): void;\n}\n\nexport type GoalPermissionEvent = \"enter\" | \"leave\";\n\n/**\n * Pure save/restore helper for `/goal` forcing session auto.\n * `saved` is the mode in effect before the goal stretch (undefined = none).\n */\nexport function nextModeForGoal(\n\tcurrent: PermissionMode,\n\tevent: GoalPermissionEvent,\n\tsaved: PermissionMode | undefined,\n): { mode: PermissionMode; saved: PermissionMode | undefined } {\n\tif (event === \"enter\") {\n\t\tif (current === \"auto\") return { mode: \"auto\", saved };\n\t\t// Already inside a goal stretch: keep a hand-changed mode (yolo/plan/manual).\n\t\tif (saved !== undefined) return { mode: current, saved };\n\t\treturn { mode: \"auto\", saved: current };\n\t}\n\tif (current === \"auto\" && saved !== undefined) {\n\t\treturn { mode: saved, saved: undefined };\n\t}\n\treturn { mode: current, saved: undefined };\n}\n\nexport function registerPermissionModeControlBridge(bridge: PermissionModeControlBridge): void {\n\t(globalThis as Record<symbol, unknown>)[PERMISSION_MODE_CONTROL_SYMBOL] = bridge;\n}\n\nexport function getPermissionModeControlBridge(): PermissionModeControlBridge | undefined {\n\treturn (globalThis as Record<symbol, unknown>)[PERMISSION_MODE_CONTROL_SYMBOL] as\n\t\t| PermissionModeControlBridge\n\t\t| undefined;\n}\n\nexport function enterGoalAuto(): void {\n\tgetPermissionModeControlBridge()?.enterGoalAuto();\n}\n\nexport function leaveGoalAuto(): void {\n\tgetPermissionModeControlBridge()?.leaveGoalAuto();\n}\n"]}
|