@contractspec/module.ai-chat 4.3.6 → 4.3.10
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 +62 -370
- package/dist/adapters/ai-sdk-bundle-adapter.d.ts +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/browser/context/index.js +93 -93
- package/dist/browser/core/index.js +308 -307
- package/dist/browser/index.js +3803 -3801
- package/dist/browser/presentation/components/index.js +2617 -2618
- package/dist/browser/presentation/hooks/index.js +476 -476
- package/dist/browser/presentation/index.js +2474 -2475
- package/dist/browser/providers/index.js +7 -7
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.js +93 -93
- package/dist/core/agent-tools-adapter.d.ts +1 -1
- package/dist/core/chat-service.d.ts +4 -4
- package/dist/core/create-chat-route.d.ts +1 -1
- package/dist/core/create-completion-route.d.ts +15 -0
- package/dist/core/export-formatters.d.ts +1 -1
- package/dist/core/index.d.ts +6 -6
- package/dist/core/index.js +308 -307
- package/dist/core/local-storage-conversation-store.d.ts +1 -1
- package/dist/core/surface-planner-tools.d.ts +2 -2
- package/dist/core/workflow-tools.d.ts +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +3803 -3801
- package/dist/node/context/index.js +93 -93
- package/dist/node/core/index.js +308 -307
- package/dist/node/index.js +3803 -3801
- package/dist/node/presentation/components/index.js +2617 -2618
- package/dist/node/presentation/hooks/index.js +476 -476
- package/dist/node/presentation/index.js +2474 -2475
- package/dist/node/providers/index.js +7 -7
- package/dist/presentation/components/ChainOfThought.d.ts +1 -1
- package/dist/presentation/components/ChatExportToolbar.d.ts +1 -1
- package/dist/presentation/components/ChatSidebar.d.ts +1 -1
- package/dist/presentation/components/ChatWithExport.d.ts +1 -1
- package/dist/presentation/components/index.d.ts +11 -11
- package/dist/presentation/components/index.js +2617 -2618
- package/dist/presentation/hooks/index.d.ts +4 -4
- package/dist/presentation/hooks/index.js +476 -476
- package/dist/presentation/hooks/useChat.d.ts +6 -6
- package/dist/presentation/hooks/useConversations.d.ts +1 -1
- package/dist/presentation/hooks/useProviders.d.ts +1 -1
- package/dist/presentation/index.js +2474 -2475
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js +7 -7
- package/package.json +15 -15
|
@@ -6,13 +6,109 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
6
6
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
+
// src/presentation/hooks/index.ts
|
|
10
|
+
import { useCompletion } from "@ai-sdk/react";
|
|
11
|
+
|
|
9
12
|
// src/presentation/hooks/useChat.tsx
|
|
10
|
-
import
|
|
13
|
+
import {
|
|
14
|
+
createProvider
|
|
15
|
+
} from "@contractspec/lib.ai-providers";
|
|
11
16
|
import { tool as tool4 } from "ai";
|
|
17
|
+
import * as React from "react";
|
|
12
18
|
import { z as z4 } from "zod";
|
|
13
19
|
|
|
14
20
|
// src/core/chat-service.ts
|
|
15
21
|
import { generateText, streamText } from "ai";
|
|
22
|
+
import { compilePlannerPrompt } from "@contractspec/lib.surface-runtime/runtime/planner-prompt";
|
|
23
|
+
|
|
24
|
+
// src/core/agent-tools-adapter.ts
|
|
25
|
+
import { tool } from "ai";
|
|
26
|
+
import { z } from "zod";
|
|
27
|
+
function getInputSchema(_schema) {
|
|
28
|
+
return z.object({}).passthrough();
|
|
29
|
+
}
|
|
30
|
+
function agentToolConfigsToToolSet(configs, handlers) {
|
|
31
|
+
const result = {};
|
|
32
|
+
for (const config of configs) {
|
|
33
|
+
const handler = handlers?.[config.name];
|
|
34
|
+
const inputSchema = getInputSchema(config.schema);
|
|
35
|
+
result[config.name] = tool({
|
|
36
|
+
description: config.description ?? config.name,
|
|
37
|
+
inputSchema,
|
|
38
|
+
execute: async (input) => {
|
|
39
|
+
if (!handler) {
|
|
40
|
+
return {
|
|
41
|
+
status: "unimplemented",
|
|
42
|
+
message: "Wire handler in host",
|
|
43
|
+
toolName: config.name
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const output = await Promise.resolve(handler(input));
|
|
48
|
+
return typeof output === "string" ? output : output;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
return {
|
|
51
|
+
status: "error",
|
|
52
|
+
error: err instanceof Error ? err.message : String(err),
|
|
53
|
+
toolName: config.name
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/core/contracts-context.ts
|
|
63
|
+
function buildContractsContextPrompt(config) {
|
|
64
|
+
const parts = [];
|
|
65
|
+
if (!config.agentSpecs?.length && !config.dataViewSpecs?.length && !config.formSpecs?.length && !config.presentationSpecs?.length && !config.operationRefs?.length) {
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
parts.push(`
|
|
69
|
+
|
|
70
|
+
## Available resources`);
|
|
71
|
+
if (config.agentSpecs?.length) {
|
|
72
|
+
parts.push(`
|
|
73
|
+
### Agent tools`);
|
|
74
|
+
for (const agent of config.agentSpecs) {
|
|
75
|
+
const toolNames = agent.tools?.map((t) => t.name).join(", ") ?? "none";
|
|
76
|
+
parts.push(`- **${agent.key}**: tools: ${toolNames}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (config.dataViewSpecs?.length) {
|
|
80
|
+
parts.push(`
|
|
81
|
+
### Data views`);
|
|
82
|
+
for (const dv of config.dataViewSpecs) {
|
|
83
|
+
parts.push(`- **${dv.key}**: ${dv.meta.title ?? dv.key}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (config.formSpecs?.length) {
|
|
87
|
+
parts.push(`
|
|
88
|
+
### Forms`);
|
|
89
|
+
for (const form of config.formSpecs) {
|
|
90
|
+
parts.push(`- **${form.key}**: ${form.meta.title ?? form.key}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (config.presentationSpecs?.length) {
|
|
94
|
+
parts.push(`
|
|
95
|
+
### Presentations`);
|
|
96
|
+
for (const pres of config.presentationSpecs) {
|
|
97
|
+
parts.push(`- **${pres.key}**: ${pres.meta.title ?? pres.key} (targets: ${pres.targets?.join(", ") ?? "react"})`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (config.operationRefs?.length) {
|
|
101
|
+
parts.push(`
|
|
102
|
+
### Operations`);
|
|
103
|
+
for (const op of config.operationRefs) {
|
|
104
|
+
parts.push(`- **${op.key}@${op.version}**`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
parts.push(`
|
|
108
|
+
Use the available tools to invoke operations, query data views, or propose surface changes when appropriate.`);
|
|
109
|
+
return parts.join(`
|
|
110
|
+
`);
|
|
111
|
+
}
|
|
16
112
|
|
|
17
113
|
// src/core/conversation-store.ts
|
|
18
114
|
function generateId(prefix) {
|
|
@@ -175,6 +271,163 @@ function createInMemoryConversationStore() {
|
|
|
175
271
|
return new InMemoryConversationStore;
|
|
176
272
|
}
|
|
177
273
|
|
|
274
|
+
// src/core/surface-planner-tools.ts
|
|
275
|
+
import { buildSurfacePatchProposal } from "@contractspec/lib.surface-runtime/runtime/planner-tools";
|
|
276
|
+
import {
|
|
277
|
+
validatePatchProposal
|
|
278
|
+
} from "@contractspec/lib.surface-runtime/spec/validate-surface-patch";
|
|
279
|
+
import { tool as tool2 } from "ai";
|
|
280
|
+
import { z as z2 } from "zod";
|
|
281
|
+
var VALID_OPS = [
|
|
282
|
+
"insert-node",
|
|
283
|
+
"replace-node",
|
|
284
|
+
"remove-node",
|
|
285
|
+
"move-node",
|
|
286
|
+
"resize-panel",
|
|
287
|
+
"set-layout",
|
|
288
|
+
"reveal-field",
|
|
289
|
+
"hide-field",
|
|
290
|
+
"promote-action",
|
|
291
|
+
"set-focus"
|
|
292
|
+
];
|
|
293
|
+
var DEFAULT_NODE_KINDS = [
|
|
294
|
+
"entity-section",
|
|
295
|
+
"entity-card",
|
|
296
|
+
"data-view",
|
|
297
|
+
"assistant-panel",
|
|
298
|
+
"chat-thread",
|
|
299
|
+
"action-bar",
|
|
300
|
+
"timeline",
|
|
301
|
+
"table",
|
|
302
|
+
"rich-doc",
|
|
303
|
+
"form",
|
|
304
|
+
"chart",
|
|
305
|
+
"custom-widget"
|
|
306
|
+
];
|
|
307
|
+
function collectSlotIdsFromRegion(node) {
|
|
308
|
+
const ids = [];
|
|
309
|
+
if (node.type === "slot") {
|
|
310
|
+
ids.push(node.slotId);
|
|
311
|
+
}
|
|
312
|
+
if (node.type === "panel-group" || node.type === "stack") {
|
|
313
|
+
for (const child of node.children) {
|
|
314
|
+
ids.push(...collectSlotIdsFromRegion(child));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (node.type === "tabs") {
|
|
318
|
+
for (const tab of node.tabs) {
|
|
319
|
+
ids.push(...collectSlotIdsFromRegion(tab.child));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (node.type === "floating") {
|
|
323
|
+
ids.push(node.anchorSlotId);
|
|
324
|
+
ids.push(...collectSlotIdsFromRegion(node.child));
|
|
325
|
+
}
|
|
326
|
+
return ids;
|
|
327
|
+
}
|
|
328
|
+
function deriveConstraints(plan) {
|
|
329
|
+
const slotIds = collectSlotIdsFromRegion(plan.layoutRoot);
|
|
330
|
+
const uniqueSlots = [...new Set(slotIds)];
|
|
331
|
+
return {
|
|
332
|
+
allowedOps: VALID_OPS,
|
|
333
|
+
allowedSlots: uniqueSlots.length > 0 ? uniqueSlots : ["assistant", "primary"],
|
|
334
|
+
allowedNodeKinds: DEFAULT_NODE_KINDS
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
var ProposePatchInputSchema = z2.object({
|
|
338
|
+
proposalId: z2.string().describe("Unique proposal identifier"),
|
|
339
|
+
ops: z2.array(z2.object({
|
|
340
|
+
op: z2.enum([
|
|
341
|
+
"insert-node",
|
|
342
|
+
"replace-node",
|
|
343
|
+
"remove-node",
|
|
344
|
+
"move-node",
|
|
345
|
+
"resize-panel",
|
|
346
|
+
"set-layout",
|
|
347
|
+
"reveal-field",
|
|
348
|
+
"hide-field",
|
|
349
|
+
"promote-action",
|
|
350
|
+
"set-focus"
|
|
351
|
+
]),
|
|
352
|
+
slotId: z2.string().optional(),
|
|
353
|
+
nodeId: z2.string().optional(),
|
|
354
|
+
toSlotId: z2.string().optional(),
|
|
355
|
+
index: z2.number().optional(),
|
|
356
|
+
node: z2.object({
|
|
357
|
+
nodeId: z2.string(),
|
|
358
|
+
kind: z2.string(),
|
|
359
|
+
title: z2.string().optional(),
|
|
360
|
+
props: z2.record(z2.string(), z2.unknown()).optional(),
|
|
361
|
+
children: z2.array(z2.unknown()).optional()
|
|
362
|
+
}).optional(),
|
|
363
|
+
persistKey: z2.string().optional(),
|
|
364
|
+
sizes: z2.array(z2.number()).optional(),
|
|
365
|
+
layoutId: z2.string().optional(),
|
|
366
|
+
fieldId: z2.string().optional(),
|
|
367
|
+
actionId: z2.string().optional(),
|
|
368
|
+
placement: z2.enum(["header", "inline", "context", "assistant"]).optional(),
|
|
369
|
+
targetId: z2.string().optional()
|
|
370
|
+
}))
|
|
371
|
+
});
|
|
372
|
+
function createSurfacePlannerTools(config) {
|
|
373
|
+
const { plan, constraints, onPatchProposal } = config;
|
|
374
|
+
const resolvedConstraints = constraints ?? deriveConstraints(plan);
|
|
375
|
+
const proposePatchTool = tool2({
|
|
376
|
+
description: "Propose surface patches (layout changes, node insertions, etc.) for user approval. " + "Only use allowed ops, slots, and node kinds from the planner context.",
|
|
377
|
+
inputSchema: ProposePatchInputSchema,
|
|
378
|
+
execute: async (input) => {
|
|
379
|
+
const ops = input.ops;
|
|
380
|
+
try {
|
|
381
|
+
validatePatchProposal(ops, resolvedConstraints);
|
|
382
|
+
const proposal = buildSurfacePatchProposal(input.proposalId, ops);
|
|
383
|
+
onPatchProposal?.(proposal);
|
|
384
|
+
return {
|
|
385
|
+
success: true,
|
|
386
|
+
proposalId: proposal.proposalId,
|
|
387
|
+
opsCount: proposal.ops.length,
|
|
388
|
+
message: "Patch proposal validated; awaiting user approval"
|
|
389
|
+
};
|
|
390
|
+
} catch (err) {
|
|
391
|
+
return {
|
|
392
|
+
success: false,
|
|
393
|
+
error: err instanceof Error ? err.message : String(err),
|
|
394
|
+
proposalId: input.proposalId
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
return {
|
|
400
|
+
"propose-patch": proposePatchTool
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
function buildPlannerPromptInput(plan) {
|
|
404
|
+
const constraints = deriveConstraints(plan);
|
|
405
|
+
return {
|
|
406
|
+
bundleMeta: {
|
|
407
|
+
key: plan.bundleKey,
|
|
408
|
+
version: "0.0.0",
|
|
409
|
+
title: plan.bundleKey
|
|
410
|
+
},
|
|
411
|
+
surfaceId: plan.surfaceId,
|
|
412
|
+
allowedPatchOps: constraints.allowedOps,
|
|
413
|
+
allowedSlots: [...constraints.allowedSlots],
|
|
414
|
+
allowedNodeKinds: [...constraints.allowedNodeKinds],
|
|
415
|
+
actions: plan.actions.map((a) => ({
|
|
416
|
+
actionId: a.actionId,
|
|
417
|
+
title: a.title
|
|
418
|
+
})),
|
|
419
|
+
preferences: {
|
|
420
|
+
guidance: "hints",
|
|
421
|
+
density: "standard",
|
|
422
|
+
dataDepth: "detailed",
|
|
423
|
+
control: "standard",
|
|
424
|
+
media: "text",
|
|
425
|
+
pace: "balanced",
|
|
426
|
+
narrative: "top-down"
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
178
431
|
// src/core/thinking-levels.ts
|
|
179
432
|
var THINKING_LEVEL_LABELS = {
|
|
180
433
|
instant: "Instant",
|
|
@@ -227,457 +480,210 @@ function getProviderOptions(level, providerName) {
|
|
|
227
480
|
}
|
|
228
481
|
|
|
229
482
|
// src/core/workflow-tools.ts
|
|
230
|
-
import { tool } from "ai";
|
|
231
|
-
import { z } from "zod";
|
|
232
483
|
import {
|
|
233
|
-
|
|
234
|
-
|
|
484
|
+
validateExtension,
|
|
485
|
+
WorkflowComposer
|
|
235
486
|
} from "@contractspec/lib.workflow-composer";
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
487
|
+
import { tool as tool3 } from "ai";
|
|
488
|
+
import { z as z3 } from "zod";
|
|
489
|
+
var StepTypeSchema = z3.enum(["human", "automation", "decision"]);
|
|
490
|
+
var StepActionSchema = z3.object({
|
|
491
|
+
operation: z3.object({
|
|
492
|
+
name: z3.string(),
|
|
493
|
+
version: z3.number()
|
|
241
494
|
}).optional(),
|
|
242
|
-
form:
|
|
243
|
-
key:
|
|
244
|
-
version:
|
|
495
|
+
form: z3.object({
|
|
496
|
+
key: z3.string(),
|
|
497
|
+
version: z3.number()
|
|
245
498
|
}).optional()
|
|
246
499
|
}).optional();
|
|
247
|
-
var StepSchema =
|
|
248
|
-
id:
|
|
500
|
+
var StepSchema = z3.object({
|
|
501
|
+
id: z3.string(),
|
|
249
502
|
type: StepTypeSchema,
|
|
250
|
-
label:
|
|
251
|
-
description:
|
|
503
|
+
label: z3.string(),
|
|
504
|
+
description: z3.string().optional(),
|
|
252
505
|
action: StepActionSchema
|
|
253
506
|
});
|
|
254
|
-
var StepInjectionSchema =
|
|
255
|
-
after:
|
|
256
|
-
before:
|
|
507
|
+
var StepInjectionSchema = z3.object({
|
|
508
|
+
after: z3.string().optional(),
|
|
509
|
+
before: z3.string().optional(),
|
|
257
510
|
inject: StepSchema,
|
|
258
|
-
transitionTo:
|
|
259
|
-
transitionFrom:
|
|
260
|
-
when:
|
|
511
|
+
transitionTo: z3.string().optional(),
|
|
512
|
+
transitionFrom: z3.string().optional(),
|
|
513
|
+
when: z3.string().optional()
|
|
261
514
|
});
|
|
262
|
-
var WorkflowExtensionInputSchema =
|
|
263
|
-
workflow:
|
|
264
|
-
tenantId:
|
|
265
|
-
role:
|
|
266
|
-
priority:
|
|
267
|
-
customSteps:
|
|
268
|
-
hiddenSteps:
|
|
515
|
+
var WorkflowExtensionInputSchema = z3.object({
|
|
516
|
+
workflow: z3.string(),
|
|
517
|
+
tenantId: z3.string().optional(),
|
|
518
|
+
role: z3.string().optional(),
|
|
519
|
+
priority: z3.number().optional(),
|
|
520
|
+
customSteps: z3.array(StepInjectionSchema).optional(),
|
|
521
|
+
hiddenSteps: z3.array(z3.string()).optional()
|
|
269
522
|
});
|
|
270
523
|
function createWorkflowTools(config) {
|
|
271
524
|
const { baseWorkflows, composer } = config;
|
|
272
525
|
const baseByKey = new Map(baseWorkflows.map((b) => [b.meta.key, b]));
|
|
273
|
-
const createWorkflowExtensionTool =
|
|
526
|
+
const createWorkflowExtensionTool = tool3({
|
|
274
527
|
description: "Create or validate a workflow extension. Use when the user asks to add steps, modify a workflow, or create a tenant-specific extension. The extension targets an existing base workflow.",
|
|
275
|
-
inputSchema: WorkflowExtensionInputSchema,
|
|
276
|
-
execute: async (input) => {
|
|
277
|
-
const extension = {
|
|
278
|
-
workflow: input.workflow,
|
|
279
|
-
tenantId: input.tenantId,
|
|
280
|
-
role: input.role,
|
|
281
|
-
priority: input.priority,
|
|
282
|
-
customSteps: input.customSteps,
|
|
283
|
-
hiddenSteps: input.hiddenSteps
|
|
284
|
-
};
|
|
285
|
-
const base = baseByKey.get(input.workflow);
|
|
286
|
-
if (!base) {
|
|
287
|
-
return {
|
|
288
|
-
success: false,
|
|
289
|
-
error: `Base workflow "${input.workflow}" not found. Available: ${Array.from(baseByKey.keys()).join(", ")}`,
|
|
290
|
-
extension
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
try {
|
|
294
|
-
validateExtension(extension, base);
|
|
295
|
-
return {
|
|
296
|
-
success: true,
|
|
297
|
-
message: "Extension validated successfully",
|
|
298
|
-
extension
|
|
299
|
-
};
|
|
300
|
-
} catch (err) {
|
|
301
|
-
return {
|
|
302
|
-
success: false,
|
|
303
|
-
error: err instanceof Error ? err.message : String(err),
|
|
304
|
-
extension
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
const composeWorkflowInputSchema = z.object({
|
|
310
|
-
workflowKey: z.string().describe("Base workflow meta.key"),
|
|
311
|
-
tenantId: z.string().optional(),
|
|
312
|
-
role: z.string().optional(),
|
|
313
|
-
extensions: z.array(WorkflowExtensionInputSchema).optional().describe("Extensions to register before composing")
|
|
314
|
-
});
|
|
315
|
-
const composeWorkflowTool = tool({
|
|
316
|
-
description: "Compose a workflow by applying registered extensions to a base workflow. Returns the composed WorkflowSpec.",
|
|
317
|
-
inputSchema: composeWorkflowInputSchema,
|
|
318
|
-
execute: async (input) => {
|
|
319
|
-
const base = baseByKey.get(input.workflowKey);
|
|
320
|
-
if (!base) {
|
|
321
|
-
return {
|
|
322
|
-
success: false,
|
|
323
|
-
error: `Base workflow "${input.workflowKey}" not found. Available: ${Array.from(baseByKey.keys()).join(", ")}`
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
const comp = composer ?? new WorkflowComposer;
|
|
327
|
-
if (input.extensions?.length) {
|
|
328
|
-
for (const ext of input.extensions) {
|
|
329
|
-
comp.register({
|
|
330
|
-
workflow: ext.workflow,
|
|
331
|
-
tenantId: ext.tenantId,
|
|
332
|
-
role: ext.role,
|
|
333
|
-
priority: ext.priority,
|
|
334
|
-
customSteps: ext.customSteps,
|
|
335
|
-
hiddenSteps: ext.hiddenSteps
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
try {
|
|
340
|
-
const composed = comp.compose({
|
|
341
|
-
base,
|
|
342
|
-
tenantId: input.tenantId,
|
|
343
|
-
role: input.role
|
|
344
|
-
});
|
|
345
|
-
return {
|
|
346
|
-
success: true,
|
|
347
|
-
workflow: composed,
|
|
348
|
-
meta: composed.meta,
|
|
349
|
-
stepIds: composed.definition.steps.map((s) => s.id)
|
|
350
|
-
};
|
|
351
|
-
} catch (err) {
|
|
352
|
-
return {
|
|
353
|
-
success: false,
|
|
354
|
-
error: err instanceof Error ? err.message : String(err)
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
const generateWorkflowSpecCodeInputSchema = z.object({
|
|
360
|
-
workflowKey: z.string().describe("Workflow meta.key"),
|
|
361
|
-
composedSteps: z.array(z.object({
|
|
362
|
-
id: z.string(),
|
|
363
|
-
type: z.enum(["human", "automation", "decision"]),
|
|
364
|
-
label: z.string(),
|
|
365
|
-
description: z.string().optional()
|
|
366
|
-
})).optional().describe("Steps to include; if omitted, uses the base workflow")
|
|
367
|
-
});
|
|
368
|
-
const generateWorkflowSpecCodeTool = tool({
|
|
369
|
-
description: "Generate TypeScript code for a workflow spec. Use after composing a workflow to output the spec as code the user can save.",
|
|
370
|
-
inputSchema: generateWorkflowSpecCodeInputSchema,
|
|
371
|
-
execute: async (input) => {
|
|
372
|
-
const base = baseByKey.get(input.workflowKey);
|
|
373
|
-
if (!base) {
|
|
374
|
-
return {
|
|
375
|
-
success: false,
|
|
376
|
-
error: `Base workflow "${input.workflowKey}" not found. Available: ${Array.from(baseByKey.keys()).join(", ")}`,
|
|
377
|
-
code: null
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
const steps = input.composedSteps ?? base.definition.steps;
|
|
381
|
-
const specVarName = toPascalCase((base.meta.key.split(".").pop() ?? "Workflow") + "") + "Workflow";
|
|
382
|
-
const stepsCode = steps.map((s) => ` {
|
|
383
|
-
id: '${s.id}',
|
|
384
|
-
type: '${s.type}',
|
|
385
|
-
label: '${escapeString(s.label)}',${s.description ? `
|
|
386
|
-
description: '${escapeString(s.description)}',` : ""}
|
|
387
|
-
}`).join(`,
|
|
388
|
-
`);
|
|
389
|
-
const meta = base.meta;
|
|
390
|
-
const transitionsJson = JSON.stringify(base.definition.transitions, null, 6);
|
|
391
|
-
const code = `import type { WorkflowSpec } from '@contractspec/lib.contracts-spec/workflow';
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Workflow: ${base.meta.key}
|
|
395
|
-
* Generated via AI chat workflow tools.
|
|
396
|
-
*/
|
|
397
|
-
export const ${specVarName}: WorkflowSpec = {
|
|
398
|
-
meta: {
|
|
399
|
-
key: '${base.meta.key}',
|
|
400
|
-
version: '${String(base.meta.version)}',
|
|
401
|
-
title: '${escapeString(meta.title ?? base.meta.key)}',
|
|
402
|
-
description: '${escapeString(meta.description ?? "")}',
|
|
403
|
-
},
|
|
404
|
-
definition: {
|
|
405
|
-
entryStepId: '${base.definition.entryStepId ?? base.definition.steps[0]?.id ?? ""}',
|
|
406
|
-
steps: [
|
|
407
|
-
${stepsCode}
|
|
408
|
-
],
|
|
409
|
-
transitions: ${transitionsJson},
|
|
410
|
-
},
|
|
411
|
-
};
|
|
412
|
-
`;
|
|
413
|
-
return {
|
|
414
|
-
success: true,
|
|
415
|
-
code,
|
|
416
|
-
workflowKey: input.workflowKey
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
return {
|
|
421
|
-
create_workflow_extension: createWorkflowExtensionTool,
|
|
422
|
-
compose_workflow: composeWorkflowTool,
|
|
423
|
-
generate_workflow_spec_code: generateWorkflowSpecCodeTool
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
function toPascalCase(value) {
|
|
427
|
-
return value.split(/[-_.]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
428
|
-
}
|
|
429
|
-
function escapeString(value) {
|
|
430
|
-
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// src/core/contracts-context.ts
|
|
434
|
-
function buildContractsContextPrompt(config) {
|
|
435
|
-
const parts = [];
|
|
436
|
-
if (!config.agentSpecs?.length && !config.dataViewSpecs?.length && !config.formSpecs?.length && !config.presentationSpecs?.length && !config.operationRefs?.length) {
|
|
437
|
-
return "";
|
|
438
|
-
}
|
|
439
|
-
parts.push(`
|
|
440
|
-
|
|
441
|
-
## Available resources`);
|
|
442
|
-
if (config.agentSpecs?.length) {
|
|
443
|
-
parts.push(`
|
|
444
|
-
### Agent tools`);
|
|
445
|
-
for (const agent of config.agentSpecs) {
|
|
446
|
-
const toolNames = agent.tools?.map((t) => t.name).join(", ") ?? "none";
|
|
447
|
-
parts.push(`- **${agent.key}**: tools: ${toolNames}`);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
if (config.dataViewSpecs?.length) {
|
|
451
|
-
parts.push(`
|
|
452
|
-
### Data views`);
|
|
453
|
-
for (const dv of config.dataViewSpecs) {
|
|
454
|
-
parts.push(`- **${dv.key}**: ${dv.meta.title ?? dv.key}`);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
if (config.formSpecs?.length) {
|
|
458
|
-
parts.push(`
|
|
459
|
-
### Forms`);
|
|
460
|
-
for (const form of config.formSpecs) {
|
|
461
|
-
parts.push(`- **${form.key}**: ${form.meta.title ?? form.key}`);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
if (config.presentationSpecs?.length) {
|
|
465
|
-
parts.push(`
|
|
466
|
-
### Presentations`);
|
|
467
|
-
for (const pres of config.presentationSpecs) {
|
|
468
|
-
parts.push(`- **${pres.key}**: ${pres.meta.title ?? pres.key} (targets: ${pres.targets?.join(", ") ?? "react"})`);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
if (config.operationRefs?.length) {
|
|
472
|
-
parts.push(`
|
|
473
|
-
### Operations`);
|
|
474
|
-
for (const op of config.operationRefs) {
|
|
475
|
-
parts.push(`- **${op.key}@${op.version}**`);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
parts.push(`
|
|
479
|
-
Use the available tools to invoke operations, query data views, or propose surface changes when appropriate.`);
|
|
480
|
-
return parts.join(`
|
|
481
|
-
`);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// src/core/agent-tools-adapter.ts
|
|
485
|
-
import { tool as tool2 } from "ai";
|
|
486
|
-
import { z as z2 } from "zod";
|
|
487
|
-
function getInputSchema(_schema) {
|
|
488
|
-
return z2.object({}).passthrough();
|
|
489
|
-
}
|
|
490
|
-
function agentToolConfigsToToolSet(configs, handlers) {
|
|
491
|
-
const result = {};
|
|
492
|
-
for (const config of configs) {
|
|
493
|
-
const handler = handlers?.[config.name];
|
|
494
|
-
const inputSchema = getInputSchema(config.schema);
|
|
495
|
-
result[config.name] = tool2({
|
|
496
|
-
description: config.description ?? config.name,
|
|
497
|
-
inputSchema,
|
|
498
|
-
execute: async (input) => {
|
|
499
|
-
if (!handler) {
|
|
500
|
-
return {
|
|
501
|
-
status: "unimplemented",
|
|
502
|
-
message: "Wire handler in host",
|
|
503
|
-
toolName: config.name
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
try {
|
|
507
|
-
const output = await Promise.resolve(handler(input));
|
|
508
|
-
return typeof output === "string" ? output : output;
|
|
509
|
-
} catch (err) {
|
|
510
|
-
return {
|
|
511
|
-
status: "error",
|
|
512
|
-
error: err instanceof Error ? err.message : String(err),
|
|
513
|
-
toolName: config.name
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
return result;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// src/core/surface-planner-tools.ts
|
|
523
|
-
import { tool as tool3 } from "ai";
|
|
524
|
-
import { z as z3 } from "zod";
|
|
525
|
-
import {
|
|
526
|
-
validatePatchProposal
|
|
527
|
-
} from "@contractspec/lib.surface-runtime/spec/validate-surface-patch";
|
|
528
|
-
import { buildSurfacePatchProposal } from "@contractspec/lib.surface-runtime/runtime/planner-tools";
|
|
529
|
-
var VALID_OPS = [
|
|
530
|
-
"insert-node",
|
|
531
|
-
"replace-node",
|
|
532
|
-
"remove-node",
|
|
533
|
-
"move-node",
|
|
534
|
-
"resize-panel",
|
|
535
|
-
"set-layout",
|
|
536
|
-
"reveal-field",
|
|
537
|
-
"hide-field",
|
|
538
|
-
"promote-action",
|
|
539
|
-
"set-focus"
|
|
540
|
-
];
|
|
541
|
-
var DEFAULT_NODE_KINDS = [
|
|
542
|
-
"entity-section",
|
|
543
|
-
"entity-card",
|
|
544
|
-
"data-view",
|
|
545
|
-
"assistant-panel",
|
|
546
|
-
"chat-thread",
|
|
547
|
-
"action-bar",
|
|
548
|
-
"timeline",
|
|
549
|
-
"table",
|
|
550
|
-
"rich-doc",
|
|
551
|
-
"form",
|
|
552
|
-
"chart",
|
|
553
|
-
"custom-widget"
|
|
554
|
-
];
|
|
555
|
-
function collectSlotIdsFromRegion(node) {
|
|
556
|
-
const ids = [];
|
|
557
|
-
if (node.type === "slot") {
|
|
558
|
-
ids.push(node.slotId);
|
|
559
|
-
}
|
|
560
|
-
if (node.type === "panel-group" || node.type === "stack") {
|
|
561
|
-
for (const child of node.children) {
|
|
562
|
-
ids.push(...collectSlotIdsFromRegion(child));
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
if (node.type === "tabs") {
|
|
566
|
-
for (const tab of node.tabs) {
|
|
567
|
-
ids.push(...collectSlotIdsFromRegion(tab.child));
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
if (node.type === "floating") {
|
|
571
|
-
ids.push(node.anchorSlotId);
|
|
572
|
-
ids.push(...collectSlotIdsFromRegion(node.child));
|
|
573
|
-
}
|
|
574
|
-
return ids;
|
|
575
|
-
}
|
|
576
|
-
function deriveConstraints(plan) {
|
|
577
|
-
const slotIds = collectSlotIdsFromRegion(plan.layoutRoot);
|
|
578
|
-
const uniqueSlots = [...new Set(slotIds)];
|
|
579
|
-
return {
|
|
580
|
-
allowedOps: VALID_OPS,
|
|
581
|
-
allowedSlots: uniqueSlots.length > 0 ? uniqueSlots : ["assistant", "primary"],
|
|
582
|
-
allowedNodeKinds: DEFAULT_NODE_KINDS
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
var ProposePatchInputSchema = z3.object({
|
|
586
|
-
proposalId: z3.string().describe("Unique proposal identifier"),
|
|
587
|
-
ops: z3.array(z3.object({
|
|
588
|
-
op: z3.enum([
|
|
589
|
-
"insert-node",
|
|
590
|
-
"replace-node",
|
|
591
|
-
"remove-node",
|
|
592
|
-
"move-node",
|
|
593
|
-
"resize-panel",
|
|
594
|
-
"set-layout",
|
|
595
|
-
"reveal-field",
|
|
596
|
-
"hide-field",
|
|
597
|
-
"promote-action",
|
|
598
|
-
"set-focus"
|
|
599
|
-
]),
|
|
600
|
-
slotId: z3.string().optional(),
|
|
601
|
-
nodeId: z3.string().optional(),
|
|
602
|
-
toSlotId: z3.string().optional(),
|
|
603
|
-
index: z3.number().optional(),
|
|
604
|
-
node: z3.object({
|
|
605
|
-
nodeId: z3.string(),
|
|
606
|
-
kind: z3.string(),
|
|
607
|
-
title: z3.string().optional(),
|
|
608
|
-
props: z3.record(z3.string(), z3.unknown()).optional(),
|
|
609
|
-
children: z3.array(z3.unknown()).optional()
|
|
610
|
-
}).optional(),
|
|
611
|
-
persistKey: z3.string().optional(),
|
|
612
|
-
sizes: z3.array(z3.number()).optional(),
|
|
613
|
-
layoutId: z3.string().optional(),
|
|
614
|
-
fieldId: z3.string().optional(),
|
|
615
|
-
actionId: z3.string().optional(),
|
|
616
|
-
placement: z3.enum(["header", "inline", "context", "assistant"]).optional(),
|
|
617
|
-
targetId: z3.string().optional()
|
|
618
|
-
}))
|
|
619
|
-
});
|
|
620
|
-
function createSurfacePlannerTools(config) {
|
|
621
|
-
const { plan, constraints, onPatchProposal } = config;
|
|
622
|
-
const resolvedConstraints = constraints ?? deriveConstraints(plan);
|
|
623
|
-
const proposePatchTool = tool3({
|
|
624
|
-
description: "Propose surface patches (layout changes, node insertions, etc.) for user approval. " + "Only use allowed ops, slots, and node kinds from the planner context.",
|
|
625
|
-
inputSchema: ProposePatchInputSchema,
|
|
528
|
+
inputSchema: WorkflowExtensionInputSchema,
|
|
626
529
|
execute: async (input) => {
|
|
627
|
-
const
|
|
530
|
+
const extension = {
|
|
531
|
+
workflow: input.workflow,
|
|
532
|
+
tenantId: input.tenantId,
|
|
533
|
+
role: input.role,
|
|
534
|
+
priority: input.priority,
|
|
535
|
+
customSteps: input.customSteps,
|
|
536
|
+
hiddenSteps: input.hiddenSteps
|
|
537
|
+
};
|
|
538
|
+
const base = baseByKey.get(input.workflow);
|
|
539
|
+
if (!base) {
|
|
540
|
+
return {
|
|
541
|
+
success: false,
|
|
542
|
+
error: `Base workflow "${input.workflow}" not found. Available: ${Array.from(baseByKey.keys()).join(", ")}`,
|
|
543
|
+
extension
|
|
544
|
+
};
|
|
545
|
+
}
|
|
628
546
|
try {
|
|
629
|
-
|
|
630
|
-
const proposal = buildSurfacePatchProposal(input.proposalId, ops);
|
|
631
|
-
onPatchProposal?.(proposal);
|
|
547
|
+
validateExtension(extension, base);
|
|
632
548
|
return {
|
|
633
549
|
success: true,
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
message: "Patch proposal validated; awaiting user approval"
|
|
550
|
+
message: "Extension validated successfully",
|
|
551
|
+
extension
|
|
637
552
|
};
|
|
638
553
|
} catch (err) {
|
|
639
554
|
return {
|
|
640
555
|
success: false,
|
|
641
556
|
error: err instanceof Error ? err.message : String(err),
|
|
642
|
-
|
|
557
|
+
extension
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
const composeWorkflowInputSchema = z3.object({
|
|
563
|
+
workflowKey: z3.string().describe("Base workflow meta.key"),
|
|
564
|
+
tenantId: z3.string().optional(),
|
|
565
|
+
role: z3.string().optional(),
|
|
566
|
+
extensions: z3.array(WorkflowExtensionInputSchema).optional().describe("Extensions to register before composing")
|
|
567
|
+
});
|
|
568
|
+
const composeWorkflowTool = tool3({
|
|
569
|
+
description: "Compose a workflow by applying registered extensions to a base workflow. Returns the composed WorkflowSpec.",
|
|
570
|
+
inputSchema: composeWorkflowInputSchema,
|
|
571
|
+
execute: async (input) => {
|
|
572
|
+
const base = baseByKey.get(input.workflowKey);
|
|
573
|
+
if (!base) {
|
|
574
|
+
return {
|
|
575
|
+
success: false,
|
|
576
|
+
error: `Base workflow "${input.workflowKey}" not found. Available: ${Array.from(baseByKey.keys()).join(", ")}`
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
const comp = composer ?? new WorkflowComposer;
|
|
580
|
+
if (input.extensions?.length) {
|
|
581
|
+
for (const ext of input.extensions) {
|
|
582
|
+
comp.register({
|
|
583
|
+
workflow: ext.workflow,
|
|
584
|
+
tenantId: ext.tenantId,
|
|
585
|
+
role: ext.role,
|
|
586
|
+
priority: ext.priority,
|
|
587
|
+
customSteps: ext.customSteps,
|
|
588
|
+
hiddenSteps: ext.hiddenSteps
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
const composed = comp.compose({
|
|
594
|
+
base,
|
|
595
|
+
tenantId: input.tenantId,
|
|
596
|
+
role: input.role
|
|
597
|
+
});
|
|
598
|
+
return {
|
|
599
|
+
success: true,
|
|
600
|
+
workflow: composed,
|
|
601
|
+
meta: composed.meta,
|
|
602
|
+
stepIds: composed.definition.steps.map((s) => s.id)
|
|
603
|
+
};
|
|
604
|
+
} catch (err) {
|
|
605
|
+
return {
|
|
606
|
+
success: false,
|
|
607
|
+
error: err instanceof Error ? err.message : String(err)
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
const generateWorkflowSpecCodeInputSchema = z3.object({
|
|
613
|
+
workflowKey: z3.string().describe("Workflow meta.key"),
|
|
614
|
+
composedSteps: z3.array(z3.object({
|
|
615
|
+
id: z3.string(),
|
|
616
|
+
type: z3.enum(["human", "automation", "decision"]),
|
|
617
|
+
label: z3.string(),
|
|
618
|
+
description: z3.string().optional()
|
|
619
|
+
})).optional().describe("Steps to include; if omitted, uses the base workflow")
|
|
620
|
+
});
|
|
621
|
+
const generateWorkflowSpecCodeTool = tool3({
|
|
622
|
+
description: "Generate TypeScript code for a workflow spec. Use after composing a workflow to output the spec as code the user can save.",
|
|
623
|
+
inputSchema: generateWorkflowSpecCodeInputSchema,
|
|
624
|
+
execute: async (input) => {
|
|
625
|
+
const base = baseByKey.get(input.workflowKey);
|
|
626
|
+
if (!base) {
|
|
627
|
+
return {
|
|
628
|
+
success: false,
|
|
629
|
+
error: `Base workflow "${input.workflowKey}" not found. Available: ${Array.from(baseByKey.keys()).join(", ")}`,
|
|
630
|
+
code: null
|
|
643
631
|
};
|
|
644
632
|
}
|
|
633
|
+
const steps = input.composedSteps ?? base.definition.steps;
|
|
634
|
+
const specVarName = toPascalCase((base.meta.key.split(".").pop() ?? "Workflow") + "") + "Workflow";
|
|
635
|
+
const stepsCode = steps.map((s) => ` {
|
|
636
|
+
id: '${s.id}',
|
|
637
|
+
type: '${s.type}',
|
|
638
|
+
label: '${escapeString(s.label)}',${s.description ? `
|
|
639
|
+
description: '${escapeString(s.description)}',` : ""}
|
|
640
|
+
}`).join(`,
|
|
641
|
+
`);
|
|
642
|
+
const meta = base.meta;
|
|
643
|
+
const transitionsJson = JSON.stringify(base.definition.transitions, null, 6);
|
|
644
|
+
const code = `import type { WorkflowSpec } from '@contractspec/lib.contracts-spec/workflow';
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Workflow: ${base.meta.key}
|
|
648
|
+
* Generated via AI chat workflow tools.
|
|
649
|
+
*/
|
|
650
|
+
export const ${specVarName}: WorkflowSpec = {
|
|
651
|
+
meta: {
|
|
652
|
+
key: '${base.meta.key}',
|
|
653
|
+
version: '${String(base.meta.version)}',
|
|
654
|
+
title: '${escapeString(meta.title ?? base.meta.key)}',
|
|
655
|
+
description: '${escapeString(meta.description ?? "")}',
|
|
656
|
+
},
|
|
657
|
+
definition: {
|
|
658
|
+
entryStepId: '${base.definition.entryStepId ?? base.definition.steps[0]?.id ?? ""}',
|
|
659
|
+
steps: [
|
|
660
|
+
${stepsCode}
|
|
661
|
+
],
|
|
662
|
+
transitions: ${transitionsJson},
|
|
663
|
+
},
|
|
664
|
+
};
|
|
665
|
+
`;
|
|
666
|
+
return {
|
|
667
|
+
success: true,
|
|
668
|
+
code,
|
|
669
|
+
workflowKey: input.workflowKey
|
|
670
|
+
};
|
|
645
671
|
}
|
|
646
672
|
});
|
|
647
673
|
return {
|
|
648
|
-
|
|
674
|
+
create_workflow_extension: createWorkflowExtensionTool,
|
|
675
|
+
compose_workflow: composeWorkflowTool,
|
|
676
|
+
generate_workflow_spec_code: generateWorkflowSpecCodeTool
|
|
649
677
|
};
|
|
650
678
|
}
|
|
651
|
-
function
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
version: "0.0.0",
|
|
657
|
-
title: plan.bundleKey
|
|
658
|
-
},
|
|
659
|
-
surfaceId: plan.surfaceId,
|
|
660
|
-
allowedPatchOps: constraints.allowedOps,
|
|
661
|
-
allowedSlots: [...constraints.allowedSlots],
|
|
662
|
-
allowedNodeKinds: [...constraints.allowedNodeKinds],
|
|
663
|
-
actions: plan.actions.map((a) => ({
|
|
664
|
-
actionId: a.actionId,
|
|
665
|
-
title: a.title
|
|
666
|
-
})),
|
|
667
|
-
preferences: {
|
|
668
|
-
guidance: "hints",
|
|
669
|
-
density: "standard",
|
|
670
|
-
dataDepth: "detailed",
|
|
671
|
-
control: "standard",
|
|
672
|
-
media: "text",
|
|
673
|
-
pace: "balanced",
|
|
674
|
-
narrative: "top-down"
|
|
675
|
-
}
|
|
676
|
-
};
|
|
679
|
+
function toPascalCase(value) {
|
|
680
|
+
return value.split(/[-_.]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
681
|
+
}
|
|
682
|
+
function escapeString(value) {
|
|
683
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
677
684
|
}
|
|
678
685
|
|
|
679
686
|
// src/core/chat-service.ts
|
|
680
|
-
import { compilePlannerPrompt } from "@contractspec/lib.surface-runtime/runtime/planner-prompt";
|
|
681
687
|
var DEFAULT_SYSTEM_PROMPT = `You are ContractSpec AI, an expert coding assistant specialized in ContractSpec development.
|
|
682
688
|
|
|
683
689
|
Your capabilities:
|
|
@@ -1093,9 +1099,6 @@ function createChatService(config) {
|
|
|
1093
1099
|
}
|
|
1094
1100
|
|
|
1095
1101
|
// src/presentation/hooks/useChat.tsx
|
|
1096
|
-
import {
|
|
1097
|
-
createProvider
|
|
1098
|
-
} from "@contractspec/lib.ai-providers";
|
|
1099
1102
|
"use client";
|
|
1100
1103
|
function toolsToToolSet(defs) {
|
|
1101
1104
|
const result = {};
|
|
@@ -1534,44 +1537,42 @@ function useChat(options = {}) {
|
|
|
1534
1537
|
...hasApprovalTools && { addToolApprovalResponse }
|
|
1535
1538
|
};
|
|
1536
1539
|
}
|
|
1537
|
-
// src/presentation/hooks/
|
|
1540
|
+
// src/presentation/hooks/useConversations.ts
|
|
1538
1541
|
import * as React2 from "react";
|
|
1539
|
-
import {
|
|
1540
|
-
getAvailableProviders,
|
|
1541
|
-
getModelsForProvider
|
|
1542
|
-
} from "@contractspec/lib.ai-providers";
|
|
1543
1542
|
"use client";
|
|
1544
|
-
function
|
|
1545
|
-
const
|
|
1543
|
+
function useConversations(options) {
|
|
1544
|
+
const { store, projectId, tags, limit = 50 } = options;
|
|
1545
|
+
const [conversations, setConversations] = React2.useState([]);
|
|
1546
1546
|
const [isLoading, setIsLoading] = React2.useState(true);
|
|
1547
|
-
const
|
|
1547
|
+
const refresh = React2.useCallback(async () => {
|
|
1548
1548
|
setIsLoading(true);
|
|
1549
1549
|
try {
|
|
1550
|
-
const
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
console.error("Failed to load providers:", error);
|
|
1550
|
+
const list = await store.list({
|
|
1551
|
+
status: "active",
|
|
1552
|
+
projectId,
|
|
1553
|
+
tags,
|
|
1554
|
+
limit
|
|
1555
|
+
});
|
|
1556
|
+
setConversations(list);
|
|
1558
1557
|
} finally {
|
|
1559
1558
|
setIsLoading(false);
|
|
1560
1559
|
}
|
|
1561
|
-
}, []);
|
|
1560
|
+
}, [store, projectId, tags, limit]);
|
|
1562
1561
|
React2.useEffect(() => {
|
|
1563
|
-
|
|
1564
|
-
}, [
|
|
1565
|
-
const
|
|
1566
|
-
|
|
1567
|
-
|
|
1562
|
+
refresh();
|
|
1563
|
+
}, [refresh]);
|
|
1564
|
+
const deleteConversation = React2.useCallback(async (id) => {
|
|
1565
|
+
const ok = await store.delete(id);
|
|
1566
|
+
if (ok) {
|
|
1567
|
+
setConversations((prev) => prev.filter((c) => c.id !== id));
|
|
1568
|
+
}
|
|
1569
|
+
return ok;
|
|
1570
|
+
}, [store]);
|
|
1568
1571
|
return {
|
|
1569
|
-
|
|
1570
|
-
availableProviders,
|
|
1571
|
-
isAvailable,
|
|
1572
|
-
getModels: getModelsCallback,
|
|
1572
|
+
conversations,
|
|
1573
1573
|
isLoading,
|
|
1574
|
-
refresh
|
|
1574
|
+
refresh,
|
|
1575
|
+
deleteConversation
|
|
1575
1576
|
};
|
|
1576
1577
|
}
|
|
1577
1578
|
// src/presentation/hooks/useMessageSelection.ts
|
|
@@ -1617,47 +1618,46 @@ function useMessageSelection(messageIds) {
|
|
|
1617
1618
|
selectedCount
|
|
1618
1619
|
};
|
|
1619
1620
|
}
|
|
1620
|
-
// src/presentation/hooks/
|
|
1621
|
+
// src/presentation/hooks/useProviders.tsx
|
|
1622
|
+
import {
|
|
1623
|
+
getAvailableProviders,
|
|
1624
|
+
getModelsForProvider
|
|
1625
|
+
} from "@contractspec/lib.ai-providers";
|
|
1621
1626
|
import * as React4 from "react";
|
|
1622
1627
|
"use client";
|
|
1623
|
-
function
|
|
1624
|
-
const
|
|
1625
|
-
const [conversations, setConversations] = React4.useState([]);
|
|
1628
|
+
function useProviders() {
|
|
1629
|
+
const [providers, setProviders] = React4.useState([]);
|
|
1626
1630
|
const [isLoading, setIsLoading] = React4.useState(true);
|
|
1627
|
-
const
|
|
1631
|
+
const loadProviders = React4.useCallback(async () => {
|
|
1628
1632
|
setIsLoading(true);
|
|
1629
1633
|
try {
|
|
1630
|
-
const
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1634
|
+
const available = getAvailableProviders();
|
|
1635
|
+
const providersWithModels = available.map((p) => ({
|
|
1636
|
+
...p,
|
|
1637
|
+
models: getModelsForProvider(p.provider)
|
|
1638
|
+
}));
|
|
1639
|
+
setProviders(providersWithModels);
|
|
1640
|
+
} catch (error) {
|
|
1641
|
+
console.error("Failed to load providers:", error);
|
|
1637
1642
|
} finally {
|
|
1638
1643
|
setIsLoading(false);
|
|
1639
1644
|
}
|
|
1640
|
-
}, [
|
|
1645
|
+
}, []);
|
|
1641
1646
|
React4.useEffect(() => {
|
|
1642
|
-
|
|
1643
|
-
}, [
|
|
1644
|
-
const
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
setConversations((prev) => prev.filter((c) => c.id !== id));
|
|
1648
|
-
}
|
|
1649
|
-
return ok;
|
|
1650
|
-
}, [store]);
|
|
1647
|
+
loadProviders();
|
|
1648
|
+
}, [loadProviders]);
|
|
1649
|
+
const availableProviders = React4.useMemo(() => providers.filter((p) => p.available), [providers]);
|
|
1650
|
+
const isAvailable = React4.useCallback((provider) => providers.some((p) => p.provider === provider && p.available), [providers]);
|
|
1651
|
+
const getModelsCallback = React4.useCallback((provider) => providers.find((p) => p.provider === provider)?.models ?? [], [providers]);
|
|
1651
1652
|
return {
|
|
1652
|
-
|
|
1653
|
+
providers,
|
|
1654
|
+
availableProviders,
|
|
1655
|
+
isAvailable,
|
|
1656
|
+
getModels: getModelsCallback,
|
|
1653
1657
|
isLoading,
|
|
1654
|
-
refresh
|
|
1655
|
-
deleteConversation
|
|
1658
|
+
refresh: loadProviders
|
|
1656
1659
|
};
|
|
1657
1660
|
}
|
|
1658
|
-
|
|
1659
|
-
// src/presentation/hooks/index.ts
|
|
1660
|
-
import { useCompletion } from "@ai-sdk/react";
|
|
1661
1661
|
export {
|
|
1662
1662
|
useProviders,
|
|
1663
1663
|
useMessageSelection,
|