@lota-sdk/shared 0.1.5

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/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@lota-sdk/shared",
3
+ "version": "0.1.5",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "files": [
8
+ "src"
9
+ ],
10
+ "exports": {
11
+ ".": {
12
+ "bun": "./src/index.ts",
13
+ "import": "./src/index.ts",
14
+ "types": "./src/index.ts"
15
+ },
16
+ "./constants/*": {
17
+ "bun": "./src/constants/*.ts",
18
+ "import": "./src/constants/*.ts",
19
+ "types": "./src/constants/*.ts"
20
+ },
21
+ "./runtime/*": {
22
+ "bun": "./src/runtime/*.ts",
23
+ "import": "./src/runtime/*.ts",
24
+ "types": "./src/runtime/*.ts"
25
+ },
26
+ "./schemas/*": {
27
+ "bun": "./src/schemas/*.ts",
28
+ "import": "./src/schemas/*.ts",
29
+ "types": "./src/schemas/*.ts"
30
+ },
31
+ "./utils/*": {
32
+ "bun": "./src/utils/*.ts",
33
+ "import": "./src/utils/*.ts",
34
+ "types": "./src/utils/*.ts"
35
+ }
36
+ },
37
+ "scripts": {
38
+ "lint": "node ../node_modules/oxlint/bin/oxlint --fix -c ../oxlint.config.ts src",
39
+ "format": "bunx oxfmt src",
40
+ "typecheck": "bunx tsgo --noEmit"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "registry": "https://registry.npmjs.org/"
45
+ },
46
+ "dependencies": {
47
+ "ai": "^6.0.116",
48
+ "zod": "^4.3.6"
49
+ }
50
+ }
@@ -0,0 +1,50 @@
1
+ export const ATTACHMENT_MAX_FILE_SIZE_MB = 100
2
+ export const ATTACHMENT_MAX_PER_MESSAGE = 5
3
+ export const ATTACHMENT_MAX_TOTAL_SIZE_MB = 1024
4
+
5
+ export const ATTACHMENT_MAX_FILE_SIZE_BYTES = ATTACHMENT_MAX_FILE_SIZE_MB * 1024 * 1024
6
+ export const ATTACHMENT_MAX_TOTAL_SIZE_BYTES = ATTACHMENT_MAX_TOTAL_SIZE_MB * 1024 * 1024
7
+
8
+ export const SUPPORTED_ATTACHMENT_MIME_TYPES = new Set([
9
+ 'application/pdf',
10
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
11
+ 'text/plain',
12
+ 'text/markdown',
13
+ 'text/x-markdown',
14
+ ])
15
+
16
+ export const SUPPORTED_ATTACHMENT_EXTENSIONS = new Set(['pdf', 'docx', 'txt', 'md', 'markdown'])
17
+
18
+ export const ATTACHMENT_ACCEPT =
19
+ 'application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,text/plain,text/markdown,text/x-markdown,.pdf,.docx,.txt,.md,.markdown'
20
+
21
+ export interface AttachmentDescriptor {
22
+ name: string
23
+ type?: string | null
24
+ }
25
+
26
+ export function getAttachmentExtension(name: string): string | null {
27
+ const trimmed = name.trim()
28
+ const lastDot = trimmed.lastIndexOf('.')
29
+ if (lastDot <= 0 || lastDot === trimmed.length - 1) return null
30
+ return trimmed.slice(lastDot + 1).toLowerCase()
31
+ }
32
+
33
+ export function isSupportedAttachment(file: AttachmentDescriptor): boolean {
34
+ const mimeType = file.type?.toLowerCase() ?? ''
35
+ if (SUPPORTED_ATTACHMENT_MIME_TYPES.has(mimeType)) return true
36
+ const extension = getAttachmentExtension(file.name)
37
+ return extension ? SUPPORTED_ATTACHMENT_EXTENSIONS.has(extension) : false
38
+ }
39
+
40
+ export function inferContentType(file: AttachmentDescriptor | string): string {
41
+ if (typeof file !== 'string' && file.type?.trim()) return file.type
42
+ const extension = getAttachmentExtension(typeof file === 'string' ? file : file.name)
43
+ if (extension === 'pdf') return 'application/pdf'
44
+ if (extension === 'docx') {
45
+ return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
46
+ }
47
+ if (extension === 'md' || extension === 'markdown') return 'text/markdown'
48
+ if (extension === 'txt') return 'text/plain'
49
+ return 'application/octet-stream'
50
+ }
@@ -0,0 +1,20 @@
1
+ export const WORKSTREAM = {
2
+ DEFAULT_TITLE: 'New Workstream',
3
+ DEFAULT_PAGE_LIMIT: 15,
4
+ DEFAULT_MESSAGE_PAGE_LIMIT: 6,
5
+ MAX_MESSAGES_PER_PAGE: 100,
6
+ STREAMING_THROTTLE_MS: 50,
7
+ STREAM_RENDER_THROTTLE_MS: 16,
8
+ STREAM_REVEAL_PROFILE: 'natural-fast',
9
+ STREAM_REVEAL_BASE_MS: 18,
10
+ STREAM_REVEAL_CHAR_DELAY_FACTOR: 0.6,
11
+ STREAM_REVEAL_MAX_CHAR_DELAY_MS: 22,
12
+ STREAM_REVEAL_PUNCTUATION_PAUSE_MS: 18,
13
+ STREAM_REVEAL_MIN_CHARS: 8,
14
+ STREAM_REVEAL_MAX_CHARS: 24,
15
+ STREAM_REVEAL_MAX_TAIL_LATENCY_MS: 900,
16
+ STREAM_EMIT_MIN_CHARS: 36,
17
+ STREAM_EMIT_MAX_CHARS: 72,
18
+ MESSAGE_SEND_THROTTLE_MS: 250,
19
+ LLM_TIMEOUT_MS: 30_000,
20
+ } as const
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ export * from './constants/attachments'
2
+ export * from './constants/workstream'
3
+ export * from './runtime/chat-message-metadata'
4
+ export * from './schemas/chat-api'
5
+ export * from './schemas/chat-message'
6
+ export * from './schemas/common'
7
+ export * from './schemas/document'
8
+ export * from './schemas/error'
9
+ export * from './schemas/execution-plan'
10
+ export * from './schemas/recent-activity'
11
+ export * from './schemas/repository-structure'
12
+ export * from './schemas/tools'
13
+ export * from './utils/assistant-text'
14
+ export * from './utils/markdown-normalization'
15
+ export * from './utils/string'
@@ -0,0 +1,47 @@
1
+ export interface MessageMetadataLike {
2
+ createdAt?: unknown
3
+ [key: string]: unknown
4
+ }
5
+
6
+ export function toTimestamp(value: unknown): number {
7
+ if (typeof value === 'number' && Number.isFinite(value)) return value
8
+ if (typeof value === 'string') {
9
+ const parsed = Date.parse(value)
10
+ if (Number.isFinite(parsed)) return parsed
11
+ }
12
+ if (value instanceof Date) return value.getTime()
13
+ return Date.now()
14
+ }
15
+
16
+ export function getMessageCreatedAt(
17
+ message: { metadata?: MessageMetadataLike },
18
+ options?: { fallback?: number },
19
+ ): number {
20
+ const createdAt = toTimestamp(message.metadata?.createdAt)
21
+ if (Number.isFinite(createdAt)) return createdAt
22
+ return options?.fallback ?? Date.now()
23
+ }
24
+
25
+ export function withCreatedAtMetadata<TMetadata extends MessageMetadataLike | undefined>(
26
+ metadata: TMetadata,
27
+ fallback = Date.now(),
28
+ ): NonNullable<TMetadata> & { createdAt: number } {
29
+ const next =
30
+ metadata && typeof metadata === 'object'
31
+ ? ({ ...(metadata as Record<string, unknown>) } as NonNullable<TMetadata>)
32
+ : ({} as NonNullable<TMetadata>)
33
+
34
+ if (typeof next.createdAt !== 'number' || !Number.isFinite(next.createdAt)) {
35
+ next.createdAt = fallback
36
+ }
37
+
38
+ return next as NonNullable<TMetadata> & { createdAt: number }
39
+ }
40
+
41
+ export function withMessageCreatedAt<TMessage extends { metadata?: MessageMetadataLike }>(
42
+ message: TMessage,
43
+ fallback = Date.now(),
44
+ ): TMessage {
45
+ const metadata = withCreatedAtMetadata(message.metadata, fallback)
46
+ return { ...message, metadata }
47
+ }
@@ -0,0 +1,26 @@
1
+ import { z } from 'zod'
2
+
3
+ import { messageMetadataSchema } from './chat-message'
4
+
5
+ const textChatMessagePartSchema = z.object({ type: z.literal('text'), text: z.string() }).passthrough()
6
+ const reasoningChatMessagePartSchema = z
7
+ .object({ type: z.literal('reasoning'), text: z.string(), state: z.enum(['streaming', 'done']).optional() })
8
+ .passthrough()
9
+ const genericChatMessagePartSchema = z
10
+ .object({ type: z.string().refine((value) => value !== 'text' && value !== 'reasoning', 'Unsupported message part') })
11
+ .passthrough()
12
+
13
+ export const baseChatMessagePartSchema = z.union([
14
+ textChatMessagePartSchema,
15
+ reasoningChatMessagePartSchema,
16
+ genericChatMessagePartSchema,
17
+ ])
18
+
19
+ export const baseChatMessageSchema = z
20
+ .object({
21
+ id: z.string(),
22
+ role: z.enum(['system', 'user', 'assistant']),
23
+ parts: z.array(baseChatMessagePartSchema),
24
+ metadata: messageMetadataSchema,
25
+ })
26
+ .passthrough()
@@ -0,0 +1,55 @@
1
+ import type { UIDataTypes, UIMessage, UITools } from 'ai'
2
+ import { z } from 'zod'
3
+
4
+ import type { CoreChatTools } from './tools'
5
+
6
+ const publicWorkstreamStateSchema = z.record(z.string(), z.unknown())
7
+
8
+ export const messageMetadataSchema = z
9
+ .object({
10
+ agentId: z.string().optional(),
11
+ agentName: z.string().optional(),
12
+ reasoningProfile: z.enum(['fast', 'standard', 'deep']).optional(),
13
+ highImpactClasses: z.array(z.string()).optional(),
14
+ policyClasses: z.array(z.string()).optional(),
15
+ semanticTerminationReason: z.enum(['none', 'conflict', 'cycle-detected']).optional(),
16
+ inputTokens: z.number().optional(),
17
+ outputTokens: z.number().optional(),
18
+ totalTokens: z.number().optional(),
19
+ cost: z.number().optional(),
20
+ upstreamInferenceCost: z.number().optional(),
21
+ finishReason: z.string().optional(),
22
+ isCompacted: z.boolean().optional(),
23
+ createdAt: z.number().optional(),
24
+ })
25
+ .optional()
26
+
27
+ export type MessageMetadata = z.infer<typeof messageMetadataSchema>
28
+
29
+ export function parseRowMetadata(raw: unknown): MessageMetadata {
30
+ if (!raw || typeof raw !== 'object') return undefined
31
+ const result = messageMetadataSchema.safeParse(raw)
32
+ return result.success ? result.data : undefined
33
+ }
34
+
35
+ export const dataPartsSchema = { workstreamTracker: publicWorkstreamStateSchema }
36
+
37
+ export type CustomDataParts = { workstreamTracker: z.infer<typeof publicWorkstreamStateSchema> }
38
+
39
+ export type AnyChatTools = Record<string, { input: unknown; output: unknown }>
40
+ export type HostExtensionChatTools = AnyChatTools
41
+ export type ChatUITools = CoreChatTools & HostExtensionChatTools
42
+
43
+ export type BaseChatMessage<
44
+ TDataParts extends UIDataTypes = CustomDataParts,
45
+ TTools extends UITools = AnyChatTools,
46
+ > = UIMessage<MessageMetadata, TDataParts, TTools>
47
+
48
+ export type BaseChatMessagePart<
49
+ TDataParts extends UIDataTypes = CustomDataParts,
50
+ TTools extends UITools = AnyChatTools,
51
+ > = BaseChatMessage<TDataParts, TTools>['parts'][number]
52
+
53
+ export type AnyChatMessage = BaseChatMessage
54
+ export type ChatMessage = BaseChatMessage<CustomDataParts, ChatUITools>
55
+ export type ChatMessagePart = BaseChatMessagePart<CustomDataParts, ChatUITools>
@@ -0,0 +1,71 @@
1
+ import { z } from 'zod'
2
+
3
+ import { WORKSTREAM } from '../constants/workstream'
4
+
5
+ export const unixTimestampSchema = z.coerce.date()
6
+
7
+ const SURREALDB_RECORD_ID_CLASSES = new Set(['RecordId', 'StringRecordId'])
8
+
9
+ function coerceRecordIdToString(val: unknown): unknown {
10
+ if (val === null || val === undefined) return val
11
+ if (typeof val === 'string') return val
12
+ if (typeof val === 'object') {
13
+ const constructorName = val.constructor.name
14
+ if (constructorName && SURREALDB_RECORD_ID_CLASSES.has(constructorName)) {
15
+ return (val as { toString(): string }).toString()
16
+ }
17
+ }
18
+ return val
19
+ }
20
+
21
+ /**
22
+ * Record ID schema that accepts:
23
+ * - String format: "table:id" or just "id"
24
+ * - Object format: { tb: string, id: string | number }
25
+ * - SurrealDB RecordId/StringRecordId objects (coerced via toString)
26
+ */
27
+ export const recordIdSchema = z.preprocess(
28
+ (val) => {
29
+ if (val === null || val === undefined) return val
30
+ if (typeof val === 'string') return val
31
+ if (typeof val === 'object') {
32
+ const constructorName = val.constructor.name
33
+ // Only coerce known SurrealDB RecordId classes
34
+ if (constructorName && SURREALDB_RECORD_ID_CLASSES.has(constructorName)) {
35
+ return (val as { toString(): string }).toString()
36
+ }
37
+ // Handle plain { tb, id } objects
38
+ const record = val as { tb?: unknown; id?: unknown }
39
+ if (typeof record.tb === 'string' && (typeof record.id === 'string' || typeof record.id === 'number')) {
40
+ return val
41
+ }
42
+ }
43
+ return val
44
+ },
45
+ z.union([z.string(), z.object({ tb: z.string(), id: z.union([z.string(), z.number()]) })]),
46
+ )
47
+
48
+ export const recordIdStringSchema = z.preprocess(coerceRecordIdToString, z.string())
49
+
50
+ export const paginationTakeSchema = z.coerce.number().int().min(1).max(WORKSTREAM.MAX_MESSAGES_PER_PAGE)
51
+ export const paginationPageSchema = z.coerce.number().int().min(1)
52
+ export const paginationCursorSchema = z.preprocess((value) => value, recordIdSchema)
53
+
54
+ export const paginationSchemaBase = z.object({
55
+ page: paginationPageSchema.optional(),
56
+ cursor: paginationCursorSchema.optional(),
57
+ take: paginationTakeSchema.default(WORKSTREAM.DEFAULT_PAGE_LIMIT),
58
+ })
59
+
60
+ export const paginationSchema = paginationSchemaBase.refine(
61
+ (value) => !(value.page !== undefined && value.cursor !== undefined),
62
+ { message: 'Provide either page or cursor, not both', path: ['cursor'] },
63
+ )
64
+
65
+ export const installUrlResponseSchema = z.object({ url: z.string().url() }).meta({ id: 'InstallUrlResponseSchema' })
66
+
67
+ export type InstallUrlResponse = z.infer<typeof installUrlResponseSchema>
68
+
69
+ export const successResponseSchema = z.object({ success: z.boolean() }).meta({ id: 'SuccessResponseSchema' })
70
+
71
+ export type SuccessResponse = z.infer<typeof successResponseSchema>
@@ -0,0 +1,51 @@
1
+ import { z } from 'zod'
2
+
3
+ export const DocumentRenderModeSchema = z.enum(['markdown', 'text', 'pdf']).meta({ id: 'DocumentRenderModeSchema' })
4
+
5
+ export const DocumentKnowledgeStatusSchema = z
6
+ .enum(['processing', 'ready', 'error'])
7
+ .meta({ id: 'DocumentKnowledgeStatusSchema' })
8
+
9
+ export const DocumentRecordSchema = z
10
+ .object({
11
+ id: z.string().optional(),
12
+ title: z.string(),
13
+ relativePath: z.string(),
14
+ mediaType: z.string(),
15
+ renderMode: DocumentRenderModeSchema,
16
+ knowledgeStatus: DocumentKnowledgeStatusSchema,
17
+ storageKey: z.string(),
18
+ sizeBytes: z.number().int().nonnegative().optional(),
19
+ createdAt: z.iso.datetime(),
20
+ updatedAt: z.iso.datetime(),
21
+ })
22
+ .meta({ id: 'DocumentRecordSchema' })
23
+
24
+ export const DocumentChunkRecordSchema = z
25
+ .object({
26
+ id: z.string().optional(),
27
+ documentId: z.string(),
28
+ sourceCanonicalKey: z.string(),
29
+ sourceVersionKey: z.string(),
30
+ chunkKey: z.string(),
31
+ chunkIndex: z.number().int(),
32
+ sectionPath: z.string().optional(),
33
+ pageStart: z.number().int().positive().optional(),
34
+ pageEnd: z.number().int().positive().optional(),
35
+ content: z.string(),
36
+ embedding: z.array(z.number()).optional(),
37
+ contentHash: z.string(),
38
+ tokenEstimate: z.number().int(),
39
+ summary: z.string().optional(),
40
+ keywords: z.array(z.string()).optional(),
41
+ metadata: z.record(z.string(), z.unknown()).optional(),
42
+ archivedAt: z.iso.datetime().optional(),
43
+ createdAt: z.iso.datetime(),
44
+ updatedAt: z.iso.datetime(),
45
+ })
46
+ .meta({ id: 'DocumentChunkRecordSchema' })
47
+
48
+ export type DocumentRenderMode = z.infer<typeof DocumentRenderModeSchema>
49
+ export type DocumentKnowledgeStatus = z.infer<typeof DocumentKnowledgeStatusSchema>
50
+ export type DocumentRecord = z.infer<typeof DocumentRecordSchema>
51
+ export type DocumentChunkRecord = z.infer<typeof DocumentChunkRecordSchema>
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod'
2
+
3
+ export const ErrorResponseSchema = z
4
+ .object({
5
+ error: z.object({
6
+ code: z.string(),
7
+ message: z.string(),
8
+ issues: z.array(z.object({ path: z.string(), message: z.string() })).optional(),
9
+ }),
10
+ })
11
+ .meta({ id: 'ErrorResponseSchema' })
@@ -0,0 +1,183 @@
1
+ import { z } from 'zod'
2
+
3
+ import { recordIdSchema, unixTimestampSchema } from './common'
4
+
5
+ export const ExecutionPlanStatusSchema = z
6
+ .enum(['draft', 'executing', 'blocked', 'completed', 'aborted'])
7
+ .meta({ id: 'ExecutionPlanStatusSchema' })
8
+ export type ExecutionPlanStatus = z.infer<typeof ExecutionPlanStatusSchema>
9
+
10
+ export const ExecutionPlanTaskStatusSchema = z
11
+ .enum(['pending', 'in-progress', 'completed', 'blocked', 'skipped', 'failed'])
12
+ .meta({ id: 'ExecutionPlanTaskStatusSchema' })
13
+ export type ExecutionPlanTaskStatus = z.infer<typeof ExecutionPlanTaskStatusSchema>
14
+
15
+ export const ExecutionPlanTaskResultStatusSchema = z
16
+ .enum(['not-started', 'success', 'partial', 'failed'])
17
+ .meta({ id: 'ExecutionPlanTaskResultStatusSchema' })
18
+ export type ExecutionPlanTaskResultStatus = z.infer<typeof ExecutionPlanTaskResultStatusSchema>
19
+
20
+ export const ExecutionPlanTaskKindSchema = z
21
+ .enum(['agent', 'user-input', 'plugin'])
22
+ .meta({ id: 'ExecutionPlanTaskKindSchema' })
23
+ export type ExecutionPlanTaskKind = z.infer<typeof ExecutionPlanTaskKindSchema>
24
+
25
+ export const ExecutionPlanTaskOwnerTypeSchema = z
26
+ .enum(['agent', 'plugin', 'user'])
27
+ .meta({ id: 'ExecutionPlanTaskOwnerTypeSchema' })
28
+ export type ExecutionPlanTaskOwnerType = z.infer<typeof ExecutionPlanTaskOwnerTypeSchema>
29
+
30
+ export const ExecutionPlanEventTypeSchema = z
31
+ .enum([
32
+ 'plan-created',
33
+ 'plan-replaced',
34
+ 'plan-status-changed',
35
+ 'task-started',
36
+ 'task-progress',
37
+ 'task-completed',
38
+ 'task-failed',
39
+ 'task-blocked',
40
+ 'task-skipped',
41
+ 'task-restarted',
42
+ 'replan',
43
+ ])
44
+ .meta({ id: 'ExecutionPlanEventTypeSchema' })
45
+ export type ExecutionPlanEventType = z.infer<typeof ExecutionPlanEventTypeSchema>
46
+
47
+ export const ExecutionPlanTaskInputSchema = z.union([z.string(), z.record(z.string(), z.unknown())])
48
+ export type ExecutionPlanTaskInput = z.infer<typeof ExecutionPlanTaskInputSchema>
49
+
50
+ export const ExecutionPlanSchema = z.object({
51
+ id: recordIdSchema,
52
+ organizationId: recordIdSchema,
53
+ workstreamId: recordIdSchema,
54
+ title: z.string(),
55
+ objective: z.string(),
56
+ status: ExecutionPlanStatusSchema,
57
+ leadAgentId: z.string(),
58
+ currentTaskId: recordIdSchema.optional(),
59
+ restartFromTaskId: recordIdSchema.optional(),
60
+ replacedPlanId: recordIdSchema.optional(),
61
+ failureCount: z.number().int().nonnegative(),
62
+ createdAt: unixTimestampSchema,
63
+ updatedAt: unixTimestampSchema.optional(),
64
+ startedAt: unixTimestampSchema.optional(),
65
+ completedAt: unixTimestampSchema.optional(),
66
+ })
67
+ export type ExecutionPlanRecord = z.infer<typeof ExecutionPlanSchema>
68
+
69
+ export const ExecutionPlanTaskSchema = z.object({
70
+ id: recordIdSchema,
71
+ planId: recordIdSchema,
72
+ organizationId: recordIdSchema,
73
+ workstreamId: recordIdSchema,
74
+ position: z.number().int().nonnegative(),
75
+ title: z.string(),
76
+ rationale: z.string(),
77
+ kind: ExecutionPlanTaskKindSchema,
78
+ ownerType: ExecutionPlanTaskOwnerTypeSchema,
79
+ ownerRef: z.string(),
80
+ status: ExecutionPlanTaskStatusSchema,
81
+ resultStatus: ExecutionPlanTaskResultStatusSchema,
82
+ input: ExecutionPlanTaskInputSchema.optional(),
83
+ outputSummary: z.string().optional(),
84
+ blockedReason: z.string().optional(),
85
+ externalRef: z.string().optional(),
86
+ retryCount: z.number().int().nonnegative(),
87
+ idempotencyKey: z.string(),
88
+ createdAt: unixTimestampSchema,
89
+ updatedAt: unixTimestampSchema.optional(),
90
+ startedAt: unixTimestampSchema.optional(),
91
+ completedAt: unixTimestampSchema.optional(),
92
+ })
93
+ export type ExecutionPlanTaskRecord = z.infer<typeof ExecutionPlanTaskSchema>
94
+
95
+ export const ExecutionPlanEventSchema = z.object({
96
+ id: recordIdSchema,
97
+ planId: recordIdSchema,
98
+ taskId: recordIdSchema.optional(),
99
+ eventType: ExecutionPlanEventTypeSchema,
100
+ fromStatus: z.string().optional(),
101
+ toStatus: z.string().optional(),
102
+ message: z.string(),
103
+ detail: z.record(z.string(), z.unknown()).optional(),
104
+ emittedBy: z.string(),
105
+ createdAt: unixTimestampSchema,
106
+ })
107
+ export type ExecutionPlanEventRecord = z.infer<typeof ExecutionPlanEventSchema>
108
+
109
+ export const SerializableExecutionPlanTaskSchema = z.object({
110
+ id: z.string(),
111
+ position: z.number().int().nonnegative(),
112
+ title: z.string(),
113
+ rationale: z.string(),
114
+ kind: ExecutionPlanTaskKindSchema,
115
+ ownerType: ExecutionPlanTaskOwnerTypeSchema,
116
+ ownerRef: z.string(),
117
+ status: ExecutionPlanTaskStatusSchema,
118
+ resultStatus: ExecutionPlanTaskResultStatusSchema,
119
+ input: ExecutionPlanTaskInputSchema.optional(),
120
+ outputSummary: z.string().nullish(),
121
+ blockedReason: z.string().nullish(),
122
+ externalRef: z.string().nullish(),
123
+ retryCount: z.number().int().nonnegative(),
124
+ idempotencyKey: z.string(),
125
+ startedAt: z.iso.datetime().nullish(),
126
+ completedAt: z.iso.datetime().nullish(),
127
+ })
128
+ export type SerializableExecutionPlanTask = z.infer<typeof SerializableExecutionPlanTaskSchema>
129
+
130
+ export const SerializableExecutionPlanEventSchema = z.object({
131
+ id: z.string(),
132
+ taskId: z.string().nullish(),
133
+ eventType: ExecutionPlanEventTypeSchema,
134
+ fromStatus: z.string().nullish(),
135
+ toStatus: z.string().nullish(),
136
+ message: z.string(),
137
+ detail: z.record(z.string(), z.unknown()).nullish(),
138
+ emittedBy: z.string(),
139
+ createdAt: z.iso.datetime(),
140
+ })
141
+ export type SerializableExecutionPlanEvent = z.infer<typeof SerializableExecutionPlanEventSchema>
142
+
143
+ export const SerializableExecutionPlanCarriedTaskSchema = z.object({
144
+ planId: z.string(),
145
+ taskId: z.string(),
146
+ title: z.string(),
147
+ resultStatus: ExecutionPlanTaskResultStatusSchema,
148
+ outputSummary: z.string(),
149
+ })
150
+ export type SerializableExecutionPlanCarriedTask = z.infer<typeof SerializableExecutionPlanCarriedTaskSchema>
151
+
152
+ export const SerializableExecutionPlanProgressSchema = z.object({
153
+ total: z.number().int().nonnegative(),
154
+ pending: z.number().int().nonnegative(),
155
+ inProgress: z.number().int().nonnegative(),
156
+ completed: z.number().int().nonnegative(),
157
+ blocked: z.number().int().nonnegative(),
158
+ failed: z.number().int().nonnegative(),
159
+ skipped: z.number().int().nonnegative(),
160
+ completionRatio: z.number().min(0).max(1).nullish(),
161
+ })
162
+ export type SerializableExecutionPlanProgress = z.infer<typeof SerializableExecutionPlanProgressSchema>
163
+
164
+ export const SerializableExecutionPlanSchema = z.object({
165
+ planId: z.string(),
166
+ workstreamId: z.string(),
167
+ organizationId: z.string(),
168
+ title: z.string(),
169
+ objective: z.string(),
170
+ status: ExecutionPlanStatusSchema,
171
+ leadAgentId: z.string(),
172
+ currentTaskId: z.string().nullish(),
173
+ restartFromTaskId: z.string().nullish(),
174
+ replacedPlanId: z.string().nullish(),
175
+ failureCount: z.number().int().nonnegative(),
176
+ startedAt: z.iso.datetime().nullish(),
177
+ completedAt: z.iso.datetime().nullish(),
178
+ progress: SerializableExecutionPlanProgressSchema,
179
+ tasks: z.array(SerializableExecutionPlanTaskSchema),
180
+ recentEvents: z.array(SerializableExecutionPlanEventSchema),
181
+ carriedTasks: z.array(SerializableExecutionPlanCarriedTaskSchema),
182
+ })
183
+ export type SerializableExecutionPlan = z.infer<typeof SerializableExecutionPlanSchema>
@@ -0,0 +1,102 @@
1
+ import { z } from 'zod'
2
+
3
+ export const RecentActivityEventKindSchema = z.enum([
4
+ 'workstream.opened',
5
+ 'document.opened',
6
+ 'website-intelligence.viewed',
7
+ 'chat.turn.completed',
8
+ ])
9
+
10
+ export const RecentActivityTargetKindSchema = z.enum(['workstream', 'document', 'website-intelligence', 'chat-turn'])
11
+
12
+ export const RecentActivityTitleSourceSchema = z.enum(['system', 'chief'])
13
+
14
+ export const RecentActivitySourceSchema = z.enum(['client', 'system'])
15
+
16
+ export const RecentActivityDeepLinkSearchSchema = z.object({
17
+ chat: z.string().optional(),
18
+ agent: z.string().optional(),
19
+ tab: z.string().optional(),
20
+ docId: z.string().optional(),
21
+ })
22
+
23
+ export const RecentActivityDeepLinkSchema = z.object({
24
+ route: z.literal('/chat'),
25
+ search: RecentActivityDeepLinkSearchSchema.default({}),
26
+ })
27
+
28
+ export const RecentActivityMetadataSchema = z
29
+ .object({
30
+ agentId: z.string().optional(),
31
+ agentName: z.string().optional(),
32
+ workstreamId: z.string().optional(),
33
+ workstreamTitle: z.string().optional(),
34
+ workstreamMode: z.string().optional(),
35
+ coreType: z.string().optional(),
36
+ documentTitle: z.string().optional(),
37
+ documentFolder: z.string().optional(),
38
+ documentSource: z.string().optional(),
39
+ normalizedWebsiteUrl: z.string().optional(),
40
+ userMessageText: z.string().optional(),
41
+ assistantSummary: z.string().optional(),
42
+ messageId: z.string().optional(),
43
+ })
44
+ .partial()
45
+
46
+ export const RecentActivityEventInputSchema = z.object({
47
+ sourceEventId: z.string().trim().min(1).max(200),
48
+ kind: RecentActivityEventKindSchema,
49
+ targetKind: RecentActivityTargetKindSchema,
50
+ targetId: z.string().trim().min(1).max(200).optional(),
51
+ mergeKey: z.string().trim().min(1).max(200),
52
+ title: z.string().trim().min(1).max(140),
53
+ sourceLabel: z.string().trim().min(1).max(40),
54
+ deepLink: RecentActivityDeepLinkSchema,
55
+ metadata: RecentActivityMetadataSchema.optional(),
56
+ occurredAt: z.string().optional(),
57
+ })
58
+
59
+ export const RecentActivitySchema = z.object({
60
+ id: z.string(),
61
+ kind: RecentActivityEventKindSchema,
62
+ targetKind: RecentActivityTargetKindSchema,
63
+ targetId: z.string().optional(),
64
+ mergeKey: z.string(),
65
+ title: z.string(),
66
+ systemTitle: z.string(),
67
+ titleSource: RecentActivityTitleSourceSchema,
68
+ sourceLabel: z.string(),
69
+ deepLink: RecentActivityDeepLinkSchema,
70
+ metadata: RecentActivityMetadataSchema.optional(),
71
+ latestEventAt: z.string(),
72
+ titleRefinedAt: z.string().optional(),
73
+ createdAt: z.string(),
74
+ updatedAt: z.string(),
75
+ })
76
+
77
+ export const RecentActivityEventSchema = z.object({
78
+ id: z.string(),
79
+ sourceEventId: z.string(),
80
+ source: RecentActivitySourceSchema,
81
+ kind: RecentActivityEventKindSchema,
82
+ targetKind: RecentActivityTargetKindSchema,
83
+ targetId: z.string().optional(),
84
+ mergeKey: z.string(),
85
+ title: z.string(),
86
+ sourceLabel: z.string(),
87
+ deepLink: RecentActivityDeepLinkSchema,
88
+ metadata: RecentActivityMetadataSchema.optional(),
89
+ occurredAt: z.string(),
90
+ createdAt: z.string(),
91
+ })
92
+
93
+ export type RecentActivityEventKind = z.infer<typeof RecentActivityEventKindSchema>
94
+ export type RecentActivityTargetKind = z.infer<typeof RecentActivityTargetKindSchema>
95
+ export type RecentActivityTitleSource = z.infer<typeof RecentActivityTitleSourceSchema>
96
+ export type RecentActivitySource = z.infer<typeof RecentActivitySourceSchema>
97
+ export type RecentActivityDeepLinkSearch = z.infer<typeof RecentActivityDeepLinkSearchSchema>
98
+ export type RecentActivityDeepLink = z.infer<typeof RecentActivityDeepLinkSchema>
99
+ export type RecentActivityMetadata = z.infer<typeof RecentActivityMetadataSchema>
100
+ export type RecentActivityEventInput = z.infer<typeof RecentActivityEventInputSchema>
101
+ export type RecentActivity = z.infer<typeof RecentActivitySchema>
102
+ export type RecentActivityEvent = z.infer<typeof RecentActivityEventSchema>
@@ -0,0 +1,122 @@
1
+ import { z } from 'zod'
2
+
3
+ export const RepositoryStructureShapeSchema = z
4
+ .enum(['single-project', 'monorepo', 'library', 'docs-only', 'uncertain'])
5
+ .meta({ id: 'RepositoryStructureShapeSchema' })
6
+
7
+ export const RepositoryStructureSignalSchema = z
8
+ .object({
9
+ key: z.string().trim().min(1).max(120),
10
+ label: z.string().trim().min(1).max(200),
11
+ reason: z.string().trim().min(1).max(1_000),
12
+ evidencePaths: z.array(z.string().trim().min(1).max(500)).max(20).default([]),
13
+ })
14
+ .meta({ id: 'RepositoryStructureSignalSchema' })
15
+
16
+ export const RepositoryStructureComponentKindSchema = z
17
+ .enum(['app', 'service', 'package', 'library', 'docs', 'unknown'])
18
+ .meta({ id: 'RepositoryStructureComponentKindSchema' })
19
+
20
+ export const RepositoryStructureComponentSchema = z
21
+ .object({
22
+ kind: RepositoryStructureComponentKindSchema,
23
+ name: z.string().trim().min(1).max(200),
24
+ path: z.string().trim().min(1).max(500),
25
+ packageManager: z.string().trim().min(1).max(80).optional(),
26
+ languages: z.array(z.string().trim().min(1).max(80)).max(20).default([]),
27
+ frameworks: z.array(z.string().trim().min(1).max(80)).max(20).default([]),
28
+ reason: z.string().trim().min(1).max(1_000),
29
+ evidencePaths: z.array(z.string().trim().min(1).max(500)).max(20).default([]),
30
+ })
31
+ .meta({ id: 'RepositoryStructureComponentSchema' })
32
+
33
+ export const RepositoryStructureRepositorySchema = z
34
+ .object({
35
+ repositoryKey: z.string().trim().min(1).max(200),
36
+ repositoryFullName: z.string().trim().min(1).max(200),
37
+ defaultBranch: z.string().trim().min(1).max(120),
38
+ indexBranch: z.string().trim().min(1).max(120),
39
+ commitSha: z.string().trim().min(1).max(120).optional(),
40
+ })
41
+ .meta({ id: 'RepositoryStructureRepositorySchema' })
42
+
43
+ export const RepositoryStructureArtifactSchema = z
44
+ .object({
45
+ schemaVersion: z.literal('repository-structure.v1'),
46
+ extractorVersion: z.string().trim().min(1).max(120),
47
+ generatedAt: z.string().datetime().optional(),
48
+ repository: RepositoryStructureRepositorySchema,
49
+ topLevelEntries: z.array(z.string().trim().min(1).max(200)).max(200).default([]),
50
+ repoShape: z.object({
51
+ kind: RepositoryStructureShapeSchema,
52
+ reason: z.string().trim().min(1).max(1_000),
53
+ evidencePaths: z.array(z.string().trim().min(1).max(500)).max(20).default([]),
54
+ }),
55
+ packageManagers: z.array(RepositoryStructureSignalSchema).max(40).default([]),
56
+ languages: z.array(RepositoryStructureSignalSchema).max(40).default([]),
57
+ frameworks: z.array(RepositoryStructureSignalSchema).max(60).default([]),
58
+ components: z.array(RepositoryStructureComponentSchema).max(200).default([]),
59
+ entryPoints: z.array(RepositoryStructureSignalSchema).max(80).default([]),
60
+ routeSignals: z.array(RepositoryStructureSignalSchema).max(80).default([]),
61
+ apiSignals: z.array(RepositoryStructureSignalSchema).max(80).default([]),
62
+ databaseSignals: z.array(RepositoryStructureSignalSchema).max(80).default([]),
63
+ authSignals: z.array(RepositoryStructureSignalSchema).max(80).default([]),
64
+ jobSignals: z.array(RepositoryStructureSignalSchema).max(80).default([]),
65
+ envFiles: z.array(RepositoryStructureSignalSchema).max(80).default([]),
66
+ dockerFiles: z.array(RepositoryStructureSignalSchema).max(80).default([]),
67
+ ciCdFiles: z.array(RepositoryStructureSignalSchema).max(80).default([]),
68
+ deploymentFiles: z.array(RepositoryStructureSignalSchema).max(80).default([]),
69
+ testSignals: z.array(RepositoryStructureSignalSchema).max(80).default([]),
70
+ lintSignals: z.array(RepositoryStructureSignalSchema).max(80).default([]),
71
+ formatSignals: z.array(RepositoryStructureSignalSchema).max(80).default([]),
72
+ typecheckSignals: z.array(RepositoryStructureSignalSchema).max(80).default([]),
73
+ unknowns: z.array(z.string().trim().min(1).max(500)).max(40).default([]),
74
+ })
75
+ .meta({ id: 'RepositoryStructureArtifactSchema' })
76
+
77
+ export const RepositoryStructureSummarySchema = z
78
+ .object({
79
+ schemaVersion: z.literal('repository-structure-summary.v1'),
80
+ extractorVersion: z.string().trim().min(1).max(120),
81
+ generatedAt: z.string().datetime().optional(),
82
+ repository: RepositoryStructureRepositorySchema,
83
+ repoShape: RepositoryStructureShapeSchema,
84
+ topLevelEntries: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
85
+ packageManagers: z.array(z.string().trim().min(1).max(80)).max(20).default([]),
86
+ languages: z.array(z.string().trim().min(1).max(80)).max(20).default([]),
87
+ frameworks: z.array(z.string().trim().min(1).max(80)).max(20).default([]),
88
+ components: z
89
+ .array(
90
+ z.object({
91
+ kind: RepositoryStructureComponentKindSchema,
92
+ name: z.string().trim().min(1).max(200),
93
+ path: z.string().trim().min(1).max(500),
94
+ }),
95
+ )
96
+ .max(40)
97
+ .default([]),
98
+ entryPoints: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
99
+ routeSignals: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
100
+ apiSignals: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
101
+ databaseSignals: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
102
+ authSignals: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
103
+ jobSignals: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
104
+ envFiles: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
105
+ dockerFiles: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
106
+ ciCdFiles: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
107
+ testSignals: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
108
+ lintSignals: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
109
+ formatSignals: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
110
+ typecheckSignals: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
111
+ deploymentSignals: z.array(z.string().trim().min(1).max(200)).max(20).default([]),
112
+ unknowns: z.array(z.string().trim().min(1).max(500)).max(20).default([]),
113
+ })
114
+ .meta({ id: 'RepositoryStructureSummarySchema' })
115
+
116
+ export type RepositoryStructureShape = z.infer<typeof RepositoryStructureShapeSchema>
117
+ export type RepositoryStructureSignal = z.infer<typeof RepositoryStructureSignalSchema>
118
+ export type RepositoryStructureComponentKind = z.infer<typeof RepositoryStructureComponentKindSchema>
119
+ export type RepositoryStructureComponent = z.infer<typeof RepositoryStructureComponentSchema>
120
+ export type RepositoryStructureRepository = z.infer<typeof RepositoryStructureRepositorySchema>
121
+ export type RepositoryStructureArtifact = z.infer<typeof RepositoryStructureArtifactSchema>
122
+ export type RepositoryStructureSummary = z.infer<typeof RepositoryStructureSummarySchema>
@@ -0,0 +1,163 @@
1
+ import { z } from 'zod'
2
+
3
+ import { baseChatMessageSchema } from './chat-api'
4
+ import type { AnyChatMessage } from './chat-message'
5
+ import {
6
+ ExecutionPlanStatusSchema,
7
+ ExecutionPlanTaskInputSchema,
8
+ ExecutionPlanTaskResultStatusSchema,
9
+ ExecutionPlanTaskStatusSchema,
10
+ SerializableExecutionPlanSchema,
11
+ } from './execution-plan'
12
+
13
+ export const USER_QUESTIONS_TOOL_NAME = 'userQuestions' as const
14
+ export const CONSULT_SPECIALIST_TOOL_NAME = 'consultSpecialist' as const
15
+ export const CONSULT_TEAM_TOOL_NAME = 'consultTeam' as const
16
+ export const CREATE_EXECUTION_PLAN_TOOL_NAME = 'createExecutionPlan' as const
17
+ export const REPLACE_EXECUTION_PLAN_TOOL_NAME = 'replaceExecutionPlan' as const
18
+ export const SET_EXECUTION_TASK_STATUS_TOOL_NAME = 'setExecutionTaskStatus' as const
19
+ export const GET_ACTIVE_EXECUTION_PLAN_TOOL_NAME = 'getActiveExecutionPlan' as const
20
+ export const RESTART_EXECUTION_TASK_TOOL_NAME = 'restartExecutionTask' as const
21
+
22
+ const UserQuestionItemSchema = z
23
+ .object({
24
+ question: z.string(),
25
+ type: z.enum(['single-select', 'multi-select', 'input']),
26
+ options: z.array(z.string()).optional(),
27
+ allowCustomAnswer: z.boolean().optional(),
28
+ })
29
+ .strict()
30
+
31
+ export const UserQuestionsArgsSchema = z.object({ questions: z.array(UserQuestionItemSchema).min(1) }).strict()
32
+
33
+ export const ConsultSpecialistArgsSchema = z
34
+ .object({ agentId: z.string(), task: z.string().trim().min(1).max(4000) })
35
+ .strict()
36
+
37
+ export const ConsultSpecialistResultDataSchema = baseChatMessageSchema
38
+
39
+ export const ConsultTeamResponseSchema = z
40
+ .object({
41
+ agentId: z.string(),
42
+ agentName: z.string().trim().min(1),
43
+ status: z.enum(['running', 'complete', 'error']),
44
+ summary: z.string().trim().optional(),
45
+ error: z.string().trim().optional(),
46
+ message: baseChatMessageSchema.optional(),
47
+ })
48
+ .strict()
49
+
50
+ export const ConsultTeamArgsSchema = z.object({ task: z.string().trim().min(1).max(6000) }).strict()
51
+
52
+ const ExecutionPlanCreateTaskKindSchema = z.enum(['agent', 'user-input'])
53
+ const ExecutionPlanCreateTaskOwnerTypeSchema = z.enum(['agent', 'user'])
54
+
55
+ const ExecutionPlanTaskDefinitionSchema = z
56
+ .object({
57
+ title: z.string().trim().min(1).max(300),
58
+ rationale: z.string().trim().min(1).max(500),
59
+ kind: ExecutionPlanCreateTaskKindSchema,
60
+ ownerType: ExecutionPlanCreateTaskOwnerTypeSchema,
61
+ ownerRef: z.string().trim().min(1).max(200),
62
+ input: ExecutionPlanTaskInputSchema.optional(),
63
+ })
64
+ .strict()
65
+ .refine(
66
+ (task) =>
67
+ (task.kind === 'agent' && task.ownerType === 'agent') ||
68
+ (task.kind === 'user-input' && task.ownerType === 'user'),
69
+ {
70
+ message: 'agent tasks must be owned by an agent and user-input tasks must be owned by a user.',
71
+ path: ['ownerType'],
72
+ },
73
+ )
74
+
75
+ const ExecutionPlanToolActionSchema = z.enum([
76
+ 'created',
77
+ 'replaced',
78
+ 'loaded',
79
+ 'task-status-updated',
80
+ 'task-restarted',
81
+ 'none',
82
+ ])
83
+
84
+ export const CreateExecutionPlanArgsSchema = z
85
+ .object({
86
+ title: z.string().trim().min(1).max(200),
87
+ objective: z.string().trim().min(1).max(1500),
88
+ tasks: z.array(ExecutionPlanTaskDefinitionSchema).min(1).max(10),
89
+ })
90
+ .strict()
91
+
92
+ export const ReplaceExecutionPlanArgsSchema = z
93
+ .object({
94
+ planId: z.string().trim().min(1),
95
+ reason: z.string().trim().min(1).max(1000),
96
+ title: z.string().trim().min(1).max(200),
97
+ objective: z.string().trim().min(1).max(1500),
98
+ tasks: CreateExecutionPlanArgsSchema.shape.tasks,
99
+ })
100
+ .strict()
101
+
102
+ export const SetExecutionTaskStatusArgsSchema = z
103
+ .object({
104
+ planId: z.string().trim().min(1),
105
+ taskId: z.string().trim().min(1),
106
+ status: ExecutionPlanTaskStatusSchema,
107
+ resultStatus: ExecutionPlanTaskResultStatusSchema.optional(),
108
+ outputSummary: z.string().trim().max(1500).optional(),
109
+ blockedReason: z.string().trim().max(1000).optional(),
110
+ externalRef: z.string().trim().max(300).optional(),
111
+ })
112
+ .strict()
113
+
114
+ export const GetActiveExecutionPlanArgsSchema = z
115
+ .object({ includeEvents: z.boolean().optional().default(false) })
116
+ .strict()
117
+
118
+ export const RestartExecutionTaskArgsSchema = z
119
+ .object({
120
+ planId: z.string().trim().min(1),
121
+ taskId: z.string().trim().min(1),
122
+ resetDownstream: z.boolean().optional().default(true),
123
+ })
124
+ .strict()
125
+
126
+ export const ExecutionPlanToolResultDataSchema = z
127
+ .object({
128
+ action: ExecutionPlanToolActionSchema,
129
+ message: z.string().trim().min(1).optional(),
130
+ changedTaskId: z.string().nullish(),
131
+ plan: SerializableExecutionPlanSchema.nullish(),
132
+ hasPlan: z.boolean(),
133
+ status: ExecutionPlanStatusSchema.nullish(),
134
+ })
135
+ .strict()
136
+
137
+ export const ConsultTeamResultDataSchema = z.object({ responses: z.array(ConsultTeamResponseSchema) }).strict()
138
+
139
+ export type UserQuestionItem = z.infer<typeof UserQuestionItemSchema>
140
+ export type UserQuestionsArgs = z.infer<typeof UserQuestionsArgsSchema>
141
+ export type ConsultSpecialistArgs = z.infer<typeof ConsultSpecialistArgsSchema>
142
+ export type ConsultSpecialistResultData = AnyChatMessage
143
+ export type ConsultTeamArgs = z.infer<typeof ConsultTeamArgsSchema>
144
+ export type ConsultTeamResponseData = Omit<z.infer<typeof ConsultTeamResponseSchema>, 'message'> & {
145
+ message?: AnyChatMessage
146
+ }
147
+ export type ConsultTeamResultData = { responses: ConsultTeamResponseData[] }
148
+ export type CreateExecutionPlanArgs = z.infer<typeof CreateExecutionPlanArgsSchema>
149
+ export type ReplaceExecutionPlanArgs = z.infer<typeof ReplaceExecutionPlanArgsSchema>
150
+ export type SetExecutionTaskStatusArgs = z.infer<typeof SetExecutionTaskStatusArgsSchema>
151
+ export type GetActiveExecutionPlanArgs = z.infer<typeof GetActiveExecutionPlanArgsSchema>
152
+ export type RestartExecutionTaskArgs = z.infer<typeof RestartExecutionTaskArgsSchema>
153
+ export type ExecutionPlanToolResultData = z.infer<typeof ExecutionPlanToolResultDataSchema>
154
+ export type CoreChatTools = {
155
+ userQuestions: { input: UserQuestionsArgs; output: unknown }
156
+ consultSpecialist: { input: ConsultSpecialistArgs; output: ConsultSpecialistResultData }
157
+ consultTeam: { input: ConsultTeamArgs; output: ConsultTeamResultData }
158
+ createExecutionPlan: { input: CreateExecutionPlanArgs; output: ExecutionPlanToolResultData }
159
+ replaceExecutionPlan: { input: ReplaceExecutionPlanArgs; output: ExecutionPlanToolResultData }
160
+ setExecutionTaskStatus: { input: SetExecutionTaskStatusArgs; output: ExecutionPlanToolResultData }
161
+ getActiveExecutionPlan: { input: GetActiveExecutionPlanArgs; output: ExecutionPlanToolResultData }
162
+ restartExecutionTask: { input: RestartExecutionTaskArgs; output: ExecutionPlanToolResultData }
163
+ }
@@ -0,0 +1,129 @@
1
+ const INTERNAL_BLOCK_PATTERNS = [
2
+ /<compacted-context[\s\S]*?<\/compacted-context>/gi,
3
+ /<assistant-message[\s\S]*?<\/assistant-message>/gi,
4
+ /<system-message[\s\S]*?<\/system-message>/gi,
5
+ /<run-context[\s\S]*?<\/run-context>/gi,
6
+ /<response-instructions[\s\S]*?<\/response-instructions>/gi,
7
+ /<tool-outcome[\s\S]*?<\/tool-outcome>/gi,
8
+ /<part\b[^>]*type\s*=\s*["']tool[_-](?:call|result|outcome)["'][^>]*>[\s\S]*?<\/part>/gi,
9
+ ]
10
+
11
+ const DANGLING_INTERNAL_BLOCK_PATTERNS = [
12
+ /<compacted-context[\s\S]*$/gi,
13
+ /<assistant-message[\s\S]*$/gi,
14
+ /<system-message[\s\S]*$/gi,
15
+ /<run-context[\s\S]*$/gi,
16
+ /<response-instructions[\s\S]*$/gi,
17
+ /<tool-outcome[\s\S]*$/gi,
18
+ /<part\b[^>]*type\s*=\s*["']tool[_-](?:call|result|outcome)["'][^>]*[\s\S]*$/gi,
19
+ ]
20
+
21
+ const RAW_TOOL_OBJECT_START_PATTERN = /\{\s*"type"\s*:\s*"tool(?:_|-)(?:call|result|outcome)"/gi
22
+
23
+ function findJsonObjectEnd(input: string, startIndex: number): number | null {
24
+ if (startIndex < 0 || startIndex >= input.length) return null
25
+ if (input[startIndex] !== '{') return null
26
+
27
+ let depth = 0
28
+ let inString = false
29
+ let isEscaped = false
30
+
31
+ for (let index = startIndex; index < input.length; index += 1) {
32
+ const character = input[index]
33
+ if (inString) {
34
+ if (isEscaped) {
35
+ isEscaped = false
36
+ continue
37
+ }
38
+
39
+ if (character === '\\') {
40
+ isEscaped = true
41
+ continue
42
+ }
43
+
44
+ if (character === '"') {
45
+ inString = false
46
+ }
47
+ continue
48
+ }
49
+
50
+ if (character === '"') {
51
+ inString = true
52
+ continue
53
+ }
54
+
55
+ if (character === '{') {
56
+ depth += 1
57
+ continue
58
+ }
59
+
60
+ if (character === '}') {
61
+ depth -= 1
62
+ if (depth === 0) return index
63
+ continue
64
+ }
65
+ }
66
+
67
+ return null
68
+ }
69
+
70
+ function stripRawToolPayloadObjects(value: string): string {
71
+ if (!value) return value
72
+
73
+ let cursor = 0
74
+ let result = ''
75
+
76
+ while (cursor < value.length) {
77
+ RAW_TOOL_OBJECT_START_PATTERN.lastIndex = cursor
78
+ const match = RAW_TOOL_OBJECT_START_PATTERN.exec(value)
79
+ if (!match) {
80
+ result += value.slice(cursor)
81
+ break
82
+ }
83
+
84
+ const start = match.index
85
+ result += value.slice(cursor, start)
86
+
87
+ const end = findJsonObjectEnd(value, start)
88
+ if (end === null) {
89
+ cursor = value.length
90
+ break
91
+ }
92
+
93
+ const candidate = value.slice(start, end + 1)
94
+ const looksInternalPayload =
95
+ (/"toolCallId"\s*:/.test(candidate) && /"toolName"\s*:/.test(candidate)) || /"toolResults"\s*:/.test(candidate)
96
+ if (!looksInternalPayload) {
97
+ result += candidate
98
+ }
99
+
100
+ cursor = end + 1
101
+ }
102
+
103
+ return result
104
+ }
105
+
106
+ export function sanitizeAssistantVisibleText(value: string): string {
107
+ let next = value
108
+
109
+ next = next.replace(/\[REDACTED\]/gi, '')
110
+
111
+ for (const pattern of INTERNAL_BLOCK_PATTERNS) {
112
+ next = next.replace(pattern, '')
113
+ }
114
+
115
+ for (const pattern of DANGLING_INTERNAL_BLOCK_PATTERNS) {
116
+ next = next.replace(pattern, '')
117
+ }
118
+
119
+ next = stripRawToolPayloadObjects(next)
120
+
121
+ next = next
122
+ .split('\n')
123
+ .filter((line) => line.trim().toLowerCase() !== '[redacted]')
124
+ .join('\n')
125
+ .replace(/\n{3,}/g, '\n\n')
126
+ .trim()
127
+
128
+ return next
129
+ }
@@ -0,0 +1,39 @@
1
+ type EvidenceGapsPayload = Partial<Record<'evidence' | 'gaps', string>>
2
+
3
+ const COMBINED_EVIDENCE_GAPS_LANGUAGE = 'evidence-gaps'
4
+ const ADJACENT_EVIDENCE_GAPS_PATTERN = /```(evidence|gaps)\s*\n([\s\S]*?)```\s*```(evidence|gaps)\s*\n([\s\S]*?)```/gi
5
+ const EVIDENCE_GAPS_FENCE_PATTERN = /```(?:evidence-gaps|evidence|gaps)\s*\n[\s\S]*?(?:```|$)/gi
6
+
7
+ function buildCombinedEvidenceGapsFence(
8
+ firstLanguage: string,
9
+ firstCode: string,
10
+ secondLanguage: string,
11
+ secondCode: string,
12
+ ): string {
13
+ const payload: EvidenceGapsPayload = {
14
+ evidence: firstLanguage === 'evidence' ? firstCode.trim() : secondLanguage === 'evidence' ? secondCode.trim() : '',
15
+ gaps: firstLanguage === 'gaps' ? firstCode.trim() : secondLanguage === 'gaps' ? secondCode.trim() : '',
16
+ }
17
+
18
+ return `\`\`\`${COMBINED_EVIDENCE_GAPS_LANGUAGE}\n${JSON.stringify(payload)}\n\`\`\``
19
+ }
20
+
21
+ export function normalizeAssistantMarkdown(markdown: string): string {
22
+ return markdown.replace(
23
+ ADJACENT_EVIDENCE_GAPS_PATTERN,
24
+ (match, firstLanguage: string, firstCode: string, secondLanguage: string, secondCode: string) => {
25
+ if (firstLanguage === secondLanguage) {
26
+ return match
27
+ }
28
+
29
+ return buildCombinedEvidenceGapsFence(firstLanguage, firstCode, secondLanguage, secondCode)
30
+ },
31
+ )
32
+ }
33
+
34
+ export function stripEvidenceGapsMarkdown(markdown: string): string {
35
+ return normalizeAssistantMarkdown(markdown)
36
+ .replace(EVIDENCE_GAPS_FENCE_PATTERN, '')
37
+ .replace(/\n{3,}/g, '\n\n')
38
+ .trim()
39
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Convert camelCase, PascalCase, kebab-case, or snake_case to human-readable title case.
3
+ *
4
+ * Examples:
5
+ * memoryRemember → Memory Remember
6
+ * consultSpecialist → Consult Specialist
7
+ * researchTopic → Research Topic
8
+ * memory-block-append → Memory Block Append
9
+ * user_questions → User Questions
10
+ */
11
+ export function toHumanCase(str: string): string {
12
+ return str
13
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
14
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
15
+ .replace(/[-_]/g, ' ')
16
+ .replace(/\b\w/g, (c) => c.toUpperCase())
17
+ .trim()
18
+ }