@gajae-code/coding-agent 0.3.1 → 0.4.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/CHANGELOG.md +46 -0
- package/README.md +1 -1
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/commands/launch.d.ts +6 -0
- package/dist/types/config/model-profile-activation.d.ts +30 -0
- package/dist/types/config/model-profiles.d.ts +19 -0
- package/dist/types/config/model-registry.d.ts +25 -10
- package/dist/types/config/model-resolver.d.ts +1 -1
- package/dist/types/config/models-config-schema.d.ts +84 -0
- package/dist/types/config/settings-schema.d.ts +15 -0
- package/dist/types/edit/diff.d.ts +16 -0
- package/dist/types/edit/modes/replace.d.ts +7 -0
- package/dist/types/extensibility/gjc-plugins/activation.d.ts +14 -0
- package/dist/types/extensibility/gjc-plugins/index.d.ts +9 -0
- package/dist/types/extensibility/gjc-plugins/injection.d.ts +31 -0
- package/dist/types/extensibility/gjc-plugins/loader.d.ts +3 -0
- package/dist/types/extensibility/gjc-plugins/paths.d.ts +8 -0
- package/dist/types/extensibility/gjc-plugins/schema.d.ts +3 -0
- package/dist/types/extensibility/gjc-plugins/state.d.ts +9 -0
- package/dist/types/extensibility/gjc-plugins/tools.d.ts +8 -0
- package/dist/types/extensibility/gjc-plugins/types.d.ts +64 -0
- package/dist/types/extensibility/gjc-plugins/validation.d.ts +4 -0
- package/dist/types/extensibility/skills.d.ts +9 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +22 -0
- package/dist/types/harness-control-plane/storage.d.ts +7 -0
- package/dist/types/lsp/client.d.ts +1 -0
- package/dist/types/main.d.ts +10 -1
- package/dist/types/modes/bridge/bridge-mode.d.ts +2 -0
- package/dist/types/modes/components/custom-provider-wizard.d.ts +10 -0
- package/dist/types/modes/components/model-selector.d.ts +6 -1
- package/dist/types/modes/components/provider-onboarding-selector.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +2 -2
- package/dist/types/modes/rpc/rpc-client.d.ts +9 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +179 -2
- package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +57 -0
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +16 -1
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +47 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +7 -0
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +11 -1
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +3 -1
- package/dist/types/modes/shared/agent-wire/responses.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +27 -0
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +68 -0
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +161 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +61 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +114 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +39 -0
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
- package/dist/types/sdk.d.ts +8 -1
- package/dist/types/session/agent-session.d.ts +10 -0
- package/dist/types/session/blob-store.d.ts +17 -0
- package/dist/types/session/messages.d.ts +3 -0
- package/dist/types/session/session-storage.d.ts +6 -0
- package/dist/types/skill-state/active-state.d.ts +13 -0
- package/dist/types/task/executor.d.ts +1 -0
- package/dist/types/thinking.d.ts +3 -2
- package/dist/types/tools/hindsight-recall.d.ts +0 -2
- package/dist/types/tools/hindsight-reflect.d.ts +0 -2
- package/dist/types/tools/hindsight-retain.d.ts +0 -2
- package/dist/types/tools/index.d.ts +7 -4
- package/package.json +9 -7
- package/src/cli/args.ts +10 -0
- package/src/cli.ts +14 -0
- package/src/commands/harness.ts +192 -7
- package/src/commands/launch.ts +8 -0
- package/src/commands/ultragoal.ts +1 -21
- package/src/config/model-equivalence.ts +1 -1
- package/src/config/model-profile-activation.ts +157 -0
- package/src/config/model-profiles.ts +155 -0
- package/src/config/model-registry.ts +51 -5
- package/src/config/model-resolver.ts +3 -2
- package/src/config/models-config-schema.ts +42 -1
- package/src/config/settings-schema.ts +14 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +11 -1
- package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +61 -0
- package/src/defaults/gjc-defaults.ts +7 -0
- package/src/discovery/claude-plugins.ts +25 -5
- package/src/edit/diff.ts +64 -1
- package/src/edit/modes/replace.ts +60 -2
- package/src/extensibility/gjc-plugins/activation.ts +87 -0
- package/src/extensibility/gjc-plugins/index.ts +9 -0
- package/src/extensibility/gjc-plugins/injection.ts +114 -0
- package/src/extensibility/gjc-plugins/loader.ts +131 -0
- package/src/extensibility/gjc-plugins/paths.ts +66 -0
- package/src/extensibility/gjc-plugins/schema.ts +79 -0
- package/src/extensibility/gjc-plugins/state.ts +29 -0
- package/src/extensibility/gjc-plugins/tools.ts +47 -0
- package/src/extensibility/gjc-plugins/types.ts +97 -0
- package/src/extensibility/gjc-plugins/validation.ts +76 -0
- package/src/extensibility/skills.ts +39 -7
- package/src/gjc-runtime/state-runtime.ts +93 -2
- package/src/gjc-runtime/state-writer.ts +17 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +62 -2
- package/src/gjc-runtime/workflow-manifest.generated.json +5 -0
- package/src/gjc-runtime/workflow-manifest.ts +2 -2
- package/src/harness-control-plane/storage.ts +144 -2
- package/src/hashline/hash.ts +23 -0
- package/src/hooks/skill-state.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +8 -11
- package/src/lsp/client.ts +7 -0
- package/src/main.ts +67 -1
- package/src/modes/acp/acp-agent.ts +25 -2
- package/src/modes/bridge/bridge-mode.ts +124 -2
- package/src/modes/components/custom-provider-wizard.ts +318 -0
- package/src/modes/components/model-selector.ts +108 -18
- package/src/modes/components/provider-onboarding-selector.ts +6 -1
- package/src/modes/controllers/input-controller.ts +14 -2
- package/src/modes/controllers/selector-controller.ts +57 -1
- package/src/modes/prompt-action-autocomplete.ts +49 -10
- package/src/modes/rpc/rpc-client.ts +57 -3
- package/src/modes/rpc/rpc-mode.ts +67 -0
- package/src/modes/rpc/rpc-types.ts +224 -2
- package/src/modes/shared/agent-wire/approval-gate.ts +151 -0
- package/src/modes/shared/agent-wire/command-dispatch.ts +97 -4
- package/src/modes/shared/agent-wire/command-validation.ts +25 -1
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +222 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +13 -0
- package/src/modes/shared/agent-wire/handshake.ts +43 -3
- package/src/modes/shared/agent-wire/protocol.ts +7 -0
- package/src/modes/shared/agent-wire/responses.ts +2 -2
- package/src/modes/shared/agent-wire/scopes.ts +2 -0
- package/src/modes/shared/agent-wire/unattended-action-policy.ts +341 -0
- package/src/modes/shared/agent-wire/unattended-audit.ts +175 -0
- package/src/modes/shared/agent-wire/unattended-run-controller.ts +406 -0
- package/src/modes/shared/agent-wire/unattended-session.ts +180 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +324 -0
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +331 -0
- package/src/modes/theme/theme.ts +6 -0
- package/src/modes/types.ts +1 -0
- package/src/prompts/memories/consolidation.md +1 -1
- package/src/prompts/memories/read-path.md +6 -7
- package/src/prompts/memories/unavailable.md +2 -2
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/irc.md +1 -1
- package/src/prompts/tools/read.md +2 -2
- package/src/prompts/tools/recall.md +1 -0
- package/src/prompts/tools/reflect.md +1 -0
- package/src/prompts/tools/retain.md +1 -0
- package/src/runtime-mcp/client.ts +7 -4
- package/src/runtime-mcp/manager.ts +45 -13
- package/src/runtime-mcp/transports/http.ts +40 -14
- package/src/runtime-mcp/transports/stdio.ts +11 -10
- package/src/sdk.ts +48 -1
- package/src/session/agent-session.ts +211 -2
- package/src/session/blob-store.ts +84 -0
- package/src/session/messages.ts +3 -0
- package/src/session/session-manager.ts +390 -33
- package/src/session/session-storage.ts +26 -0
- package/src/setup/provider-onboarding.ts +2 -2
- package/src/skill-state/active-state.ts +89 -1
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/task/discovery.ts +7 -1
- package/src/task/executor.ts +18 -2
- package/src/task/index.ts +2 -0
- package/src/thinking.ts +8 -2
- package/src/tools/ask.ts +39 -9
- package/src/tools/hindsight-recall.ts +0 -2
- package/src/tools/hindsight-reflect.ts +0 -2
- package/src/tools/hindsight-retain.ts +0 -2
- package/src/tools/index.ts +7 -18
- package/src/tools/read.ts +3 -3
- package/src/tools/skill.ts +15 -3
- package/src/utils/edit-mode.ts +1 -1
|
@@ -20,6 +20,85 @@ import * as os from "node:os";
|
|
|
20
20
|
import * as path from "node:path";
|
|
21
21
|
import type { EventEnvelope, ReceiptFamily, SessionState } from "./types";
|
|
22
22
|
|
|
23
|
+
interface HarnessRootRegistryEntry {
|
|
24
|
+
root: string;
|
|
25
|
+
updatedAt: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface HarnessRootRegistry {
|
|
29
|
+
sessionId: string;
|
|
30
|
+
roots: HarnessRootRegistryEntry[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface ResolveHarnessSessionRootOptions {
|
|
34
|
+
expectedWorkspace?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function canonicalWorkspacePath(workspace: string): string {
|
|
38
|
+
return path.resolve(workspace);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function samePath(left: string, right: string): boolean {
|
|
42
|
+
return canonicalWorkspacePath(left) === canonicalWorkspacePath(right);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function ensurePrivateDir(dir: string): Promise<void> {
|
|
46
|
+
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
47
|
+
await fs.chmod(dir, 0o700);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function ensurePrivateDirSync(dir: string): void {
|
|
51
|
+
fsSync.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
52
|
+
fsSync.chmodSync(dir, 0o700);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function sessionMatchesWorkspace(state: SessionState, expectedWorkspace: string): boolean {
|
|
56
|
+
return samePath(state.handle.workspace, expectedWorkspace);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function harnessRootRegistryDir(env: NodeJS.ProcessEnv = process.env): string {
|
|
60
|
+
const override = env.GJC_HARNESS_ROOT_REGISTRY_DIR?.trim();
|
|
61
|
+
if (override) return path.resolve(override);
|
|
62
|
+
return path.join(os.tmpdir(), `gjch${process.getuid?.() ?? "u"}`, "harness-roots");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function harnessRootRegistryPath(sessionId: string, env: NodeJS.ProcessEnv = process.env): string {
|
|
66
|
+
assertSafeSessionId(sessionId);
|
|
67
|
+
return path.join(harnessRootRegistryDir(env), `${sessionId}.json`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function readHarnessRootRegistry(
|
|
71
|
+
sessionId: string,
|
|
72
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
73
|
+
): Promise<HarnessRootRegistry> {
|
|
74
|
+
const file = harnessRootRegistryPath(sessionId, env);
|
|
75
|
+
try {
|
|
76
|
+
const raw = await fs.readFile(file, "utf8");
|
|
77
|
+
const parsed = JSON.parse(raw) as HarnessRootRegistry;
|
|
78
|
+
if (parsed.sessionId === sessionId && Array.isArray(parsed.roots)) return parsed;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
81
|
+
}
|
|
82
|
+
return { sessionId, roots: [] };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function writeJsonAtomicPrivate(file: string, value: unknown): Promise<void> {
|
|
86
|
+
await ensurePrivateDir(path.dirname(file));
|
|
87
|
+
const tmp = `${file}.tmp-${randomBytes(4).toString("hex")}`;
|
|
88
|
+
await fs.writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
89
|
+
await fs.rename(tmp, file);
|
|
90
|
+
await fs.chmod(file, 0o600);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function writeHarnessRootRegistry(
|
|
94
|
+
registry: HarnessRootRegistry,
|
|
95
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
96
|
+
): Promise<void> {
|
|
97
|
+
const dir = harnessRootRegistryDir(env);
|
|
98
|
+
await ensurePrivateDir(dir);
|
|
99
|
+
const file = harnessRootRegistryPath(registry.sessionId, env);
|
|
100
|
+
await writeJsonAtomicPrivate(file, registry);
|
|
101
|
+
}
|
|
23
102
|
const SESSION_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
24
103
|
export const MAX_UNIX_SOCKET_PATH_BYTES = 100;
|
|
25
104
|
|
|
@@ -36,7 +115,7 @@ function socketBase(env: NodeJS.ProcessEnv, allowOverride: boolean): { base: str
|
|
|
36
115
|
|
|
37
116
|
function socketPathForBase(root: string, sessionId: string, base: string): string {
|
|
38
117
|
const digest = createHash("sha256").update(`${root}\0${sessionId}`).digest("hex");
|
|
39
|
-
|
|
118
|
+
ensurePrivateDirSync(base);
|
|
40
119
|
for (const len of [16, 24, 32, 48, 64]) {
|
|
41
120
|
const stem = `c-${digest.slice(0, len)}`;
|
|
42
121
|
const metadataPath = path.join(base, `${stem}.json`);
|
|
@@ -46,7 +125,10 @@ function socketPathForBase(root: string, sessionId: string, base: string): strin
|
|
|
46
125
|
if (existing.root === root && existing.sessionId === sessionId) return path.join(base, `${stem}.sock`);
|
|
47
126
|
} catch (error) {
|
|
48
127
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
49
|
-
fsSync.writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`,
|
|
128
|
+
fsSync.writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`, {
|
|
129
|
+
encoding: "utf8",
|
|
130
|
+
mode: 0o600,
|
|
131
|
+
});
|
|
50
132
|
return path.join(base, `${stem}.sock`);
|
|
51
133
|
}
|
|
52
134
|
}
|
|
@@ -149,6 +231,66 @@ async function readJson<T>(file: string): Promise<T | null> {
|
|
|
149
231
|
export async function readSessionState(root: string, sessionId: string): Promise<SessionState | null> {
|
|
150
232
|
return readJson<SessionState>(sessionPaths(root, sessionId).state);
|
|
151
233
|
}
|
|
234
|
+
export async function rememberHarnessSessionRoot(
|
|
235
|
+
root: string,
|
|
236
|
+
sessionId: string,
|
|
237
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
238
|
+
): Promise<void> {
|
|
239
|
+
assertSafeSessionId(sessionId);
|
|
240
|
+
const resolvedRoot = path.resolve(root);
|
|
241
|
+
const registry = await readHarnessRootRegistry(sessionId, env);
|
|
242
|
+
const now = new Date().toISOString();
|
|
243
|
+
registry.roots = [
|
|
244
|
+
{ root: resolvedRoot, updatedAt: now },
|
|
245
|
+
...registry.roots.filter(entry => path.resolve(entry.root) !== resolvedRoot),
|
|
246
|
+
].slice(0, 8);
|
|
247
|
+
await writeHarnessRootRegistry(registry, env);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export async function resolveHarnessSessionRoot(
|
|
251
|
+
root: string,
|
|
252
|
+
sessionId: string,
|
|
253
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
254
|
+
options: ResolveHarnessSessionRootOptions = {},
|
|
255
|
+
): Promise<string> {
|
|
256
|
+
assertSafeSessionId(sessionId);
|
|
257
|
+
const resolvedRoot = path.resolve(root);
|
|
258
|
+
const candidates: { root: string; state: SessionState }[] = [];
|
|
259
|
+
const seenRoots = new Set<string>();
|
|
260
|
+
const addCandidate = async (candidateRoot: string): Promise<void> => {
|
|
261
|
+
const candidate = path.resolve(candidateRoot);
|
|
262
|
+
if (seenRoots.has(candidate)) return;
|
|
263
|
+
seenRoots.add(candidate);
|
|
264
|
+
const state = await readSessionState(candidate, sessionId);
|
|
265
|
+
if (state !== null) candidates.push({ root: candidate, state });
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
await addCandidate(resolvedRoot);
|
|
269
|
+
const registry = await readHarnessRootRegistry(sessionId, env);
|
|
270
|
+
for (const entry of registry.roots) await addCandidate(entry.root);
|
|
271
|
+
|
|
272
|
+
if (!options.expectedWorkspace) {
|
|
273
|
+
if (candidates.some(candidate => candidate.root === resolvedRoot)) return resolvedRoot;
|
|
274
|
+
if (candidates.length === 1) return candidates[0].root;
|
|
275
|
+
if (candidates.length > 1) {
|
|
276
|
+
throw new StorageError(`ambiguous_harness_session_root:${sessionId}`, "ambiguous_harness_session_root");
|
|
277
|
+
}
|
|
278
|
+
return resolvedRoot;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const expectedWorkspace = canonicalWorkspacePath(options.expectedWorkspace);
|
|
282
|
+
const matchingCandidates = candidates.filter(candidate =>
|
|
283
|
+
sessionMatchesWorkspace(candidate.state, expectedWorkspace),
|
|
284
|
+
);
|
|
285
|
+
if (matchingCandidates.length === 1) return matchingCandidates[0].root;
|
|
286
|
+
if (matchingCandidates.length > 1) {
|
|
287
|
+
throw new StorageError(`ambiguous_harness_session_root:${sessionId}`, "ambiguous_harness_session_root");
|
|
288
|
+
}
|
|
289
|
+
if (candidates.length > 0) {
|
|
290
|
+
throw new StorageError(`session_workspace_mismatch:${sessionId}`, "session_workspace_mismatch");
|
|
291
|
+
}
|
|
292
|
+
return resolvedRoot;
|
|
293
|
+
}
|
|
152
294
|
|
|
153
295
|
export async function writeSessionState(root: string, state: SessionState): Promise<void> {
|
|
154
296
|
const paths = sessionPaths(root, state.sessionId);
|
package/src/hashline/hash.ts
CHANGED
|
@@ -5,6 +5,20 @@
|
|
|
5
5
|
|
|
6
6
|
import bigrams from "./bigrams.json" with { type: "json" };
|
|
7
7
|
|
|
8
|
+
// Optional native acceleration for formatHashLines. Loaded WITHOUT throwing at
|
|
9
|
+
// module evaluation so this core module (and its re-exported helpers) stays
|
|
10
|
+
// usable and falls back to the TS loop if the native addon is unavailable.
|
|
11
|
+
let formatHashLinesNative: ((text: string, startLine?: number) => string) | undefined;
|
|
12
|
+
void import("../../../natives/native/index.js")
|
|
13
|
+
.then(mod => {
|
|
14
|
+
if (typeof mod.h06FormatHashLines === "function") {
|
|
15
|
+
formatHashLinesNative = mod.h06FormatHashLines;
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
.catch(() => {
|
|
19
|
+
// Native unavailable; formatHashLines uses the TS loop.
|
|
20
|
+
});
|
|
21
|
+
|
|
8
22
|
/**
|
|
9
23
|
* 647 single-token BPE bigrams for hashline anchors. Every entry tokenizes as
|
|
10
24
|
* exactly one token in modern BPE vocabularies (cl100k / o200k / Anthropic model family),
|
|
@@ -168,6 +182,15 @@ export function formatHashLine(lineNumber: number, line: string): string {
|
|
|
168
182
|
* ```
|
|
169
183
|
*/
|
|
170
184
|
export function formatHashLines(text: string, startLine = 1): string {
|
|
185
|
+
// Native path only for the supported startLine domain (non-negative integer);
|
|
186
|
+
// other values fall through to JS numeric semantics in the TS loop.
|
|
187
|
+
if (formatHashLinesNative && Number.isInteger(startLine) && startLine >= 0) {
|
|
188
|
+
try {
|
|
189
|
+
return formatHashLinesNative(text, startLine);
|
|
190
|
+
} catch {
|
|
191
|
+
// Native hashline formatting is an optimization only; preserve the TS contract.
|
|
192
|
+
}
|
|
193
|
+
}
|
|
171
194
|
const lines = text.split("\n");
|
|
172
195
|
return lines.map((line, i) => formatHashLine(startLine + i, line)).join("\n");
|
|
173
196
|
}
|
package/src/hooks/skill-state.ts
CHANGED
|
@@ -382,6 +382,8 @@ async function seedSkillActivationState(
|
|
|
382
382
|
return state;
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
+
// Fallback for native-hook prompts when SkillPromptDetails.subskillActivation is absent;
|
|
386
|
+
// real /skill dispatch paths resolve sub-skill activation before prompt construction.
|
|
385
387
|
export async function recordSkillActivation(input: RecordSkillActivationInput): Promise<SkillActiveState | null> {
|
|
386
388
|
const match = detectPrimarySkillKeyword(input.text);
|
|
387
389
|
if (!match) return null;
|