@lakitu/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -0
- package/convex/_generated/api.d.ts +45 -0
- package/convex/_generated/api.js +23 -0
- package/convex/_generated/dataModel.d.ts +58 -0
- package/convex/_generated/server.d.ts +143 -0
- package/convex/_generated/server.js +93 -0
- package/convex/cloud/CLAUDE.md +238 -0
- package/convex/cloud/_generated/api.ts +84 -0
- package/convex/cloud/_generated/component.ts +861 -0
- package/convex/cloud/_generated/dataModel.ts +60 -0
- package/convex/cloud/_generated/server.ts +156 -0
- package/convex/cloud/convex.config.ts +16 -0
- package/convex/cloud/index.ts +29 -0
- package/convex/cloud/intentSchema/generate.ts +447 -0
- package/convex/cloud/intentSchema/index.ts +16 -0
- package/convex/cloud/intentSchema/types.ts +418 -0
- package/convex/cloud/ksaPolicy.ts +554 -0
- package/convex/cloud/mail.ts +92 -0
- package/convex/cloud/schema.ts +322 -0
- package/convex/cloud/utils/kanbanContext.ts +229 -0
- package/convex/cloud/workflows/agentBoard.ts +451 -0
- package/convex/cloud/workflows/agentPrompt.ts +272 -0
- package/convex/cloud/workflows/agentThread.ts +374 -0
- package/convex/cloud/workflows/compileSandbox.ts +146 -0
- package/convex/cloud/workflows/crudBoard.ts +217 -0
- package/convex/cloud/workflows/crudKSAs.ts +262 -0
- package/convex/cloud/workflows/crudLorobeads.ts +371 -0
- package/convex/cloud/workflows/crudSkills.ts +205 -0
- package/convex/cloud/workflows/crudThreads.ts +708 -0
- package/convex/cloud/workflows/lifecycleSandbox.ts +1396 -0
- package/convex/cloud/workflows/sandboxConvex.ts +1046 -0
- package/convex/sandbox/README.md +90 -0
- package/convex/sandbox/_generated/api.d.ts +2934 -0
- package/convex/sandbox/_generated/api.js +23 -0
- package/convex/sandbox/_generated/dataModel.d.ts +60 -0
- package/convex/sandbox/_generated/server.d.ts +143 -0
- package/convex/sandbox/_generated/server.js +93 -0
- package/convex/sandbox/actions/bash.ts +130 -0
- package/convex/sandbox/actions/browser.ts +282 -0
- package/convex/sandbox/actions/file.ts +336 -0
- package/convex/sandbox/actions/lsp.ts +325 -0
- package/convex/sandbox/actions/pdf.ts +119 -0
- package/convex/sandbox/agent/codeExecLoop.ts +535 -0
- package/convex/sandbox/agent/decisions.ts +284 -0
- package/convex/sandbox/agent/index.ts +515 -0
- package/convex/sandbox/agent/subagents.ts +651 -0
- package/convex/sandbox/brandResearch/index.ts +417 -0
- package/convex/sandbox/context/index.ts +7 -0
- package/convex/sandbox/context/session.ts +402 -0
- package/convex/sandbox/convex.config.ts +17 -0
- package/convex/sandbox/index.ts +51 -0
- package/convex/sandbox/nodeActions/codeExec.ts +130 -0
- package/convex/sandbox/planning/beads.ts +187 -0
- package/convex/sandbox/planning/index.ts +8 -0
- package/convex/sandbox/planning/sync.ts +194 -0
- package/convex/sandbox/prompts/codeExec.ts +852 -0
- package/convex/sandbox/prompts/modes.ts +231 -0
- package/convex/sandbox/prompts/system.ts +142 -0
- package/convex/sandbox/schema.ts +510 -0
- package/convex/sandbox/state/artifacts.ts +99 -0
- package/convex/sandbox/state/checkpoints.ts +341 -0
- package/convex/sandbox/state/files.ts +383 -0
- package/convex/sandbox/state/index.ts +10 -0
- package/convex/sandbox/state/verification.actions.ts +268 -0
- package/convex/sandbox/state/verification.ts +101 -0
- package/convex/sandbox/tsconfig.json +25 -0
- package/convex/sandbox/utils/codeExecHelpers.ts +52 -0
- package/dist/cli/commands/build.d.ts +19 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +223 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +148 -0
- package/dist/cli/commands/publish.d.ts +12 -0
- package/dist/cli/commands/publish.d.ts.map +1 -0
- package/dist/cli/commands/publish.js +33 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +40 -0
- package/dist/sdk/builders.d.ts +104 -0
- package/dist/sdk/builders.d.ts.map +1 -0
- package/dist/sdk/builders.js +214 -0
- package/dist/sdk/index.d.ts +29 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +38 -0
- package/dist/sdk/types.d.ts +107 -0
- package/dist/sdk/types.d.ts.map +1 -0
- package/dist/sdk/types.js +6 -0
- package/ksa/README.md +263 -0
- package/ksa/_generated/REFERENCE.md +2954 -0
- package/ksa/_generated/registry.ts +257 -0
- package/ksa/_shared/configReader.ts +302 -0
- package/ksa/_shared/configSchemas.ts +649 -0
- package/ksa/_shared/gateway.ts +175 -0
- package/ksa/_shared/ksaBehaviors.ts +411 -0
- package/ksa/_shared/ksaProxy.ts +248 -0
- package/ksa/_shared/localDb.ts +302 -0
- package/ksa/index.ts +134 -0
- package/package.json +93 -0
- package/runtime/browser/agent-browser.ts +330 -0
- package/runtime/entrypoint.ts +194 -0
- package/runtime/lsp/manager.ts +366 -0
- package/runtime/pdf/pdf-generator.ts +50 -0
- package/runtime/pdf/renderer.ts +357 -0
- package/runtime/pdf/schema.ts +97 -0
- package/runtime/services/file-watcher.ts +191 -0
- package/template/build.ts +307 -0
- package/template/e2b/Dockerfile +69 -0
- package/template/e2b/e2b.toml +13 -0
- package/template/e2b/prebuild.sh +68 -0
- package/template/e2b/start.sh +14 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Gateway Module
|
|
3
|
+
*
|
|
4
|
+
* Common utilities for calling the cloud gateway from KSAs.
|
|
5
|
+
* All KSAs should use this module instead of implementing their own fetch logic.
|
|
6
|
+
*
|
|
7
|
+
* Optimizations:
|
|
8
|
+
* - callGateway: Standard blocking call (await response)
|
|
9
|
+
* - callGatewayBatch: Multiple calls in single HTTP request (parallel execution)
|
|
10
|
+
* - fireAndForget: Non-blocking call (don't wait for response)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync, existsSync } from "fs";
|
|
14
|
+
|
|
15
|
+
// Load env vars from /home/user/.env if present (set by pool claim)
|
|
16
|
+
function loadEnvFile(): Record<string, string> {
|
|
17
|
+
const envPath = "/home/user/.env";
|
|
18
|
+
if (!existsSync(envPath)) return {};
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const content = readFileSync(envPath, "utf-8");
|
|
22
|
+
const envVars: Record<string, string> = {};
|
|
23
|
+
for (const line of content.split("\n")) {
|
|
24
|
+
const match = line.match(/^export\s+(\w+)="([^"]*)"/);
|
|
25
|
+
if (match) {
|
|
26
|
+
envVars[match[1]] = match[2];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return envVars;
|
|
30
|
+
} catch {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const envFile = loadEnvFile();
|
|
36
|
+
|
|
37
|
+
// Gateway config from environment (set by sandbox runtime)
|
|
38
|
+
// Check both process.env and .env file (for pooled sandboxes)
|
|
39
|
+
const GATEWAY_URL = process.env.GATEWAY_URL || envFile.GATEWAY_URL || "http://localhost:3210";
|
|
40
|
+
const JWT = process.env.SANDBOX_JWT || envFile.SANDBOX_JWT || "";
|
|
41
|
+
|
|
42
|
+
// Export THREAD_ID, CARD_ID, and WORKSPACE_ID for other KSAs to use
|
|
43
|
+
export const THREAD_ID = process.env.THREAD_ID || envFile.THREAD_ID;
|
|
44
|
+
export const CARD_ID = process.env.CARD_ID || envFile.CARD_ID;
|
|
45
|
+
export const WORKSPACE_ID = process.env.WORKSPACE_ID || envFile.WORKSPACE_ID;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Call the cloud gateway to invoke a Convex service.
|
|
49
|
+
*
|
|
50
|
+
* @param path - Service path (e.g., 'services.Valyu.internal.search')
|
|
51
|
+
* @param args - Arguments to pass to the service
|
|
52
|
+
* @returns Service response data
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const data = await callGateway('services.SendGrid.internal.send', {
|
|
56
|
+
* to: 'user@example.com',
|
|
57
|
+
* subject: 'Hello',
|
|
58
|
+
* text: 'World'
|
|
59
|
+
* });
|
|
60
|
+
*/
|
|
61
|
+
export async function callGateway<T = unknown>(
|
|
62
|
+
path: string,
|
|
63
|
+
args: Record<string, unknown>,
|
|
64
|
+
type?: "query" | "mutation" | "action"
|
|
65
|
+
): Promise<T> {
|
|
66
|
+
const response = await fetch(`${GATEWAY_URL}/agent/call`, {
|
|
67
|
+
method: "POST",
|
|
68
|
+
headers: {
|
|
69
|
+
"Content-Type": "application/json",
|
|
70
|
+
Authorization: `Bearer ${JWT}`,
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify({ path, args, type }),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
const text = await response.text();
|
|
77
|
+
throw new Error(`Gateway error (${response.status}): ${text}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const result = await response.json();
|
|
81
|
+
if (!result.ok) {
|
|
82
|
+
throw new Error(`Service error: ${result.error || JSON.stringify(result)}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return result.data as T;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface BatchCall {
|
|
89
|
+
path: string;
|
|
90
|
+
args?: Record<string, unknown>;
|
|
91
|
+
type?: "query" | "mutation" | "action";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface BatchResult<T = unknown> {
|
|
95
|
+
ok: boolean;
|
|
96
|
+
data?: T;
|
|
97
|
+
error?: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Execute multiple gateway calls in a single HTTP request.
|
|
102
|
+
* All calls execute in parallel on the server.
|
|
103
|
+
*
|
|
104
|
+
* @param calls - Array of calls to execute
|
|
105
|
+
* @returns Array of results (same order as calls)
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* const [users, posts] = await callGatewayBatch([
|
|
109
|
+
* { path: 'services.Users.internal.list', args: { limit: 10 } },
|
|
110
|
+
* { path: 'services.Posts.internal.recent', args: {} }
|
|
111
|
+
* ]);
|
|
112
|
+
*/
|
|
113
|
+
export async function callGatewayBatch<T extends unknown[] = unknown[]>(
|
|
114
|
+
calls: BatchCall[]
|
|
115
|
+
): Promise<BatchResult<T[number]>[]> {
|
|
116
|
+
const response = await fetch(`${GATEWAY_URL}/agent/batch`, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: {
|
|
119
|
+
"Content-Type": "application/json",
|
|
120
|
+
Authorization: `Bearer ${JWT}`,
|
|
121
|
+
},
|
|
122
|
+
body: JSON.stringify({ calls }),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (!response.ok) {
|
|
126
|
+
const text = await response.text();
|
|
127
|
+
throw new Error(`Gateway batch error (${response.status}): ${text}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const result = await response.json();
|
|
131
|
+
if (!result.ok) {
|
|
132
|
+
throw new Error(`Batch error: ${result.error || JSON.stringify(result)}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return result.results;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Fire-and-forget gateway call - doesn't wait for response.
|
|
140
|
+
* Use for non-critical operations like logging, analytics, beads updates.
|
|
141
|
+
*
|
|
142
|
+
* @param path - Service path
|
|
143
|
+
* @param args - Arguments to pass
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* // Log something without blocking
|
|
147
|
+
* fireAndForget('agent.workflows.sandboxConvex.appendLogs', { sessionId, logs });
|
|
148
|
+
*/
|
|
149
|
+
export function fireAndForget(
|
|
150
|
+
path: string,
|
|
151
|
+
args: Record<string, unknown>,
|
|
152
|
+
type?: "query" | "mutation" | "action"
|
|
153
|
+
): void {
|
|
154
|
+
fetch(`${GATEWAY_URL}/agent/call`, {
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers: {
|
|
157
|
+
"Content-Type": "application/json",
|
|
158
|
+
Authorization: `Bearer ${JWT}`,
|
|
159
|
+
},
|
|
160
|
+
body: JSON.stringify({ path, args, type }),
|
|
161
|
+
}).catch(() => {
|
|
162
|
+
// Intentionally ignore errors - fire and forget
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get the gateway configuration.
|
|
168
|
+
* Useful for debugging or checking connectivity.
|
|
169
|
+
*/
|
|
170
|
+
export function getGatewayConfig() {
|
|
171
|
+
return {
|
|
172
|
+
url: GATEWAY_URL,
|
|
173
|
+
hasJwt: !!JWT,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KSA Behaviors - Per-KSA Hooks for Local DB Integration
|
|
3
|
+
*
|
|
4
|
+
* Defines before/after hooks that the proxy layer calls automatically.
|
|
5
|
+
* These hooks enable file tracking, caching, session persistence, etc.
|
|
6
|
+
* without requiring changes to individual KSA implementations.
|
|
7
|
+
*
|
|
8
|
+
* Hook Types:
|
|
9
|
+
* - before: Called before function execution, can return cached result
|
|
10
|
+
* - after: Called after function execution, for tracking/caching
|
|
11
|
+
* - transform: Transform result before returning (optional)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { localDb, getSessionId, getThreadId, cacheKey, simpleHash } from "./localDb";
|
|
15
|
+
import type { FrameworkConfig } from "./configSchemas";
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Types
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
export interface BeforeHookResult {
|
|
22
|
+
/** If true, skip function execution and return cachedResult */
|
|
23
|
+
skipExecution?: boolean;
|
|
24
|
+
/** Cached result to return if skipExecution is true */
|
|
25
|
+
cachedResult?: unknown;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface HookContext {
|
|
29
|
+
ksaName: string;
|
|
30
|
+
funcName: string;
|
|
31
|
+
args: unknown[];
|
|
32
|
+
config: FrameworkConfig;
|
|
33
|
+
startTime: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type BeforeHook = (
|
|
37
|
+
ctx: HookContext
|
|
38
|
+
) => Promise<BeforeHookResult | void> | BeforeHookResult | void;
|
|
39
|
+
|
|
40
|
+
export type AfterHook = (
|
|
41
|
+
ctx: HookContext,
|
|
42
|
+
result: unknown,
|
|
43
|
+
error?: Error
|
|
44
|
+
) => Promise<void> | void;
|
|
45
|
+
|
|
46
|
+
export interface KSABehavior {
|
|
47
|
+
/** Hooks for specific functions */
|
|
48
|
+
functions?: Record<string, {
|
|
49
|
+
before?: BeforeHook;
|
|
50
|
+
after?: AfterHook;
|
|
51
|
+
}>;
|
|
52
|
+
/** Default hooks for all functions in this KSA */
|
|
53
|
+
default?: {
|
|
54
|
+
before?: BeforeHook;
|
|
55
|
+
after?: AfterHook;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// Cache Helpers
|
|
61
|
+
// ============================================================================
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check cache for a function result.
|
|
65
|
+
*/
|
|
66
|
+
async function checkCache(
|
|
67
|
+
ctx: HookContext
|
|
68
|
+
): Promise<BeforeHookResult | void> {
|
|
69
|
+
if (!ctx.config.cacheResults) return;
|
|
70
|
+
|
|
71
|
+
const key = cacheKey(ctx.ksaName, ctx.funcName, ctx.args);
|
|
72
|
+
const taskHash = simpleHash(key);
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const cached = await localDb.query<{
|
|
76
|
+
context: { result: unknown; timestamp: number };
|
|
77
|
+
} | null>("context/session.getCachedContext", {
|
|
78
|
+
sessionId: getSessionId(),
|
|
79
|
+
taskHash,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (cached?.context) {
|
|
83
|
+
const age = Date.now() - cached.context.timestamp;
|
|
84
|
+
if (age < ctx.config.cacheTTLMs) {
|
|
85
|
+
return {
|
|
86
|
+
skipExecution: true,
|
|
87
|
+
cachedResult: cached.context.result,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
// Cache miss or error - continue with execution
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Store result in cache.
|
|
98
|
+
*/
|
|
99
|
+
function storeInCache(ctx: HookContext, result: unknown): void {
|
|
100
|
+
if (!ctx.config.cacheResults) return;
|
|
101
|
+
|
|
102
|
+
const key = cacheKey(ctx.ksaName, ctx.funcName, ctx.args);
|
|
103
|
+
const taskHash = simpleHash(key);
|
|
104
|
+
|
|
105
|
+
localDb.fire("context/session.cacheContext", {
|
|
106
|
+
sessionId: getSessionId(),
|
|
107
|
+
taskHash,
|
|
108
|
+
context: {
|
|
109
|
+
relevantFiles: [],
|
|
110
|
+
toolsNeeded: [ctx.ksaName],
|
|
111
|
+
tokenBudget: 0,
|
|
112
|
+
// Store our custom fields
|
|
113
|
+
result,
|
|
114
|
+
timestamp: Date.now(),
|
|
115
|
+
ksaName: ctx.ksaName,
|
|
116
|
+
funcName: ctx.funcName,
|
|
117
|
+
},
|
|
118
|
+
ttlMs: ctx.config.cacheTTLMs,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Log a KSA function call for tracking.
|
|
124
|
+
*/
|
|
125
|
+
function logCall(
|
|
126
|
+
ctx: HookContext,
|
|
127
|
+
result: unknown,
|
|
128
|
+
error?: Error
|
|
129
|
+
): void {
|
|
130
|
+
if (!ctx.config.trackCalls) return;
|
|
131
|
+
|
|
132
|
+
const threadId = getThreadId();
|
|
133
|
+
if (!threadId) return;
|
|
134
|
+
|
|
135
|
+
localDb.fire("agentDecisions.create", {
|
|
136
|
+
threadId,
|
|
137
|
+
task: `${ctx.ksaName}.${ctx.funcName}`,
|
|
138
|
+
decisionType: "tool_selection",
|
|
139
|
+
selectedTools: [ctx.ksaName],
|
|
140
|
+
reasoning: `Called ${ctx.funcName} with ${ctx.args.length} args`,
|
|
141
|
+
expectedOutcome: error ? "failure" : "success",
|
|
142
|
+
metadata: {
|
|
143
|
+
ksaName: ctx.ksaName,
|
|
144
|
+
funcName: ctx.funcName,
|
|
145
|
+
durationMs: Date.now() - ctx.startTime,
|
|
146
|
+
success: !error,
|
|
147
|
+
error: error?.message,
|
|
148
|
+
},
|
|
149
|
+
timestamp: Date.now(),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ============================================================================
|
|
154
|
+
// File KSA Behaviors
|
|
155
|
+
// ============================================================================
|
|
156
|
+
|
|
157
|
+
const fileBehaviors: KSABehavior = {
|
|
158
|
+
functions: {
|
|
159
|
+
read: {
|
|
160
|
+
after: (ctx, result) => {
|
|
161
|
+
if (!ctx.config.trackFileState) return;
|
|
162
|
+
|
|
163
|
+
const [path] = ctx.args as [string];
|
|
164
|
+
const content = result as string;
|
|
165
|
+
|
|
166
|
+
localDb.fire("state/files.trackFileAccess", {
|
|
167
|
+
path,
|
|
168
|
+
operation: "read",
|
|
169
|
+
size: content?.length,
|
|
170
|
+
contentHash: content ? simpleHash(content) : undefined,
|
|
171
|
+
threadId: getThreadId(),
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
write: {
|
|
176
|
+
after: (ctx) => {
|
|
177
|
+
if (!ctx.config.trackFileState) return;
|
|
178
|
+
|
|
179
|
+
const [path, content] = ctx.args as [string, string];
|
|
180
|
+
|
|
181
|
+
localDb.fire("state/files.trackFileAccess", {
|
|
182
|
+
path,
|
|
183
|
+
operation: "write",
|
|
184
|
+
size: content?.length,
|
|
185
|
+
contentHash: content ? simpleHash(content) : undefined,
|
|
186
|
+
threadId: getThreadId(),
|
|
187
|
+
});
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
edit: {
|
|
191
|
+
after: (ctx) => {
|
|
192
|
+
if (!ctx.config.trackFileState) return;
|
|
193
|
+
|
|
194
|
+
const [path, oldText, newText] = ctx.args as [string, string, string];
|
|
195
|
+
|
|
196
|
+
// Generate a simple diff representation
|
|
197
|
+
const diff = `- ${oldText.slice(0, 100)}...\n+ ${newText.slice(0, 100)}...`;
|
|
198
|
+
|
|
199
|
+
localDb.fire("state/files.recordEdit", {
|
|
200
|
+
path,
|
|
201
|
+
oldContent: oldText,
|
|
202
|
+
newContent: newText,
|
|
203
|
+
diff,
|
|
204
|
+
verified: false,
|
|
205
|
+
threadId: getThreadId(),
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
glob: {
|
|
210
|
+
// Cache glob results for 1 minute
|
|
211
|
+
before: checkCache,
|
|
212
|
+
after: (ctx, result) => {
|
|
213
|
+
storeInCache(ctx, result);
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
grep: {
|
|
217
|
+
// Cache grep results for 1 minute
|
|
218
|
+
before: checkCache,
|
|
219
|
+
after: (ctx, result) => {
|
|
220
|
+
storeInCache(ctx, result);
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// ============================================================================
|
|
227
|
+
// Web KSA Behaviors
|
|
228
|
+
// ============================================================================
|
|
229
|
+
|
|
230
|
+
const webBehaviors: KSABehavior = {
|
|
231
|
+
functions: {
|
|
232
|
+
search: {
|
|
233
|
+
before: checkCache,
|
|
234
|
+
after: (ctx, result) => {
|
|
235
|
+
storeInCache(ctx, result);
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
scrape: {
|
|
239
|
+
before: checkCache,
|
|
240
|
+
after: (ctx, result) => {
|
|
241
|
+
storeInCache(ctx, result);
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
news: {
|
|
245
|
+
before: checkCache,
|
|
246
|
+
after: (ctx, result) => {
|
|
247
|
+
storeInCache(ctx, result);
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// ============================================================================
|
|
254
|
+
// Social KSA Behaviors
|
|
255
|
+
// ============================================================================
|
|
256
|
+
|
|
257
|
+
const socialBehaviors: KSABehavior = {
|
|
258
|
+
default: {
|
|
259
|
+
// Cache all social API results
|
|
260
|
+
before: checkCache,
|
|
261
|
+
after: (ctx, result) => {
|
|
262
|
+
storeInCache(ctx, result);
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// ============================================================================
|
|
268
|
+
// Companies KSA Behaviors
|
|
269
|
+
// ============================================================================
|
|
270
|
+
|
|
271
|
+
const companiesBehaviors: KSABehavior = {
|
|
272
|
+
default: {
|
|
273
|
+
// Cache all company enrichment results
|
|
274
|
+
before: checkCache,
|
|
275
|
+
after: (ctx, result) => {
|
|
276
|
+
storeInCache(ctx, result);
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// ============================================================================
|
|
282
|
+
// News KSA Behaviors
|
|
283
|
+
// ============================================================================
|
|
284
|
+
|
|
285
|
+
const newsBehaviors: KSABehavior = {
|
|
286
|
+
default: {
|
|
287
|
+
before: checkCache,
|
|
288
|
+
after: (ctx, result) => {
|
|
289
|
+
storeInCache(ctx, result);
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// ============================================================================
|
|
295
|
+
// Beads KSA Behaviors
|
|
296
|
+
// ============================================================================
|
|
297
|
+
|
|
298
|
+
const beadsBehaviors: KSABehavior = {
|
|
299
|
+
// Beads already uses local DB directly, no additional behaviors needed
|
|
300
|
+
// But we track calls for observability
|
|
301
|
+
default: {
|
|
302
|
+
after: (ctx, result, error) => {
|
|
303
|
+
logCall(ctx, result, error);
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// ============================================================================
|
|
309
|
+
// Artifacts KSA Behaviors
|
|
310
|
+
// ============================================================================
|
|
311
|
+
|
|
312
|
+
const artifactsBehaviors: KSABehavior = {
|
|
313
|
+
functions: {
|
|
314
|
+
listArtifacts: {
|
|
315
|
+
before: checkCache,
|
|
316
|
+
after: (ctx, result) => {
|
|
317
|
+
storeInCache(ctx, result);
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// ============================================================================
|
|
324
|
+
// Browser KSA Behaviors
|
|
325
|
+
// ============================================================================
|
|
326
|
+
|
|
327
|
+
const browserBehaviors: KSABehavior = {
|
|
328
|
+
functions: {
|
|
329
|
+
screenshot: {
|
|
330
|
+
// Don't cache screenshots
|
|
331
|
+
after: (ctx, _result, error) => {
|
|
332
|
+
logCall(ctx, "[screenshot]", error);
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
default: {
|
|
337
|
+
// Log all browser actions
|
|
338
|
+
after: (ctx, result, error) => {
|
|
339
|
+
logCall(ctx, result, error);
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// ============================================================================
|
|
345
|
+
// Default Behaviors (for any KSA without specific behaviors)
|
|
346
|
+
// ============================================================================
|
|
347
|
+
|
|
348
|
+
const defaultBehaviors: KSABehavior = {
|
|
349
|
+
default: {
|
|
350
|
+
before: checkCache,
|
|
351
|
+
after: (ctx, result, error) => {
|
|
352
|
+
if (!error) {
|
|
353
|
+
storeInCache(ctx, result);
|
|
354
|
+
}
|
|
355
|
+
logCall(ctx, result, error);
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// ============================================================================
|
|
361
|
+
// Registry
|
|
362
|
+
// ============================================================================
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* KSA behavior registry.
|
|
366
|
+
* Maps KSA names to their behavior definitions.
|
|
367
|
+
*/
|
|
368
|
+
export const KSA_BEHAVIORS: Record<string, KSABehavior> = {
|
|
369
|
+
file: fileBehaviors,
|
|
370
|
+
web: webBehaviors,
|
|
371
|
+
social: socialBehaviors,
|
|
372
|
+
companies: companiesBehaviors,
|
|
373
|
+
news: newsBehaviors,
|
|
374
|
+
beads: beadsBehaviors,
|
|
375
|
+
artifacts: artifactsBehaviors,
|
|
376
|
+
browser: browserBehaviors,
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Get behavior for a specific KSA function.
|
|
381
|
+
* Falls back to default behaviors if no specific behavior is defined.
|
|
382
|
+
*/
|
|
383
|
+
export function getBehavior(
|
|
384
|
+
ksaName: string,
|
|
385
|
+
funcName: string
|
|
386
|
+
): { before?: BeforeHook; after?: AfterHook } {
|
|
387
|
+
const ksaBehavior = KSA_BEHAVIORS[ksaName];
|
|
388
|
+
|
|
389
|
+
if (ksaBehavior) {
|
|
390
|
+
// Check for function-specific behavior
|
|
391
|
+
const funcBehavior = ksaBehavior.functions?.[funcName];
|
|
392
|
+
if (funcBehavior) {
|
|
393
|
+
return funcBehavior;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Fall back to KSA default behavior
|
|
397
|
+
if (ksaBehavior.default) {
|
|
398
|
+
return ksaBehavior.default;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Fall back to global default behavior
|
|
403
|
+
return defaultBehaviors.default || {};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Check if a KSA has any custom behaviors defined.
|
|
408
|
+
*/
|
|
409
|
+
export function hasCustomBehaviors(ksaName: string): boolean {
|
|
410
|
+
return ksaName in KSA_BEHAVIORS;
|
|
411
|
+
}
|