@mrtdown/core 2.0.0-alpha.2 → 2.0.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/create.d.ts +30 -0
- package/dist/cli/commands/create.js +189 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/list.d.ts +6 -0
- package/dist/cli/commands/list.js +106 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/show.d.ts +6 -0
- package/dist/cli/commands/show.js +156 -0
- package/dist/cli/commands/show.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +6 -0
- package/dist/cli/commands/validate.js +19 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +162 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +20 -11
- package/dist/index.js +20 -11
- package/dist/index.js.map +1 -1
- package/dist/llm/client.d.ts +2 -0
- package/dist/llm/client.js +5 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/llm/common/MemoryStore.d.ts +21 -0
- package/dist/llm/common/MemoryStore.js +100 -0
- package/dist/llm/common/MemoryStore.js.map +1 -0
- package/dist/llm/common/MemoryStore.test.d.ts +1 -0
- package/dist/llm/common/MemoryStore.test.js +225 -0
- package/dist/llm/common/MemoryStore.test.js.map +1 -0
- package/dist/llm/common/formatCurrentState.d.ts +10 -0
- package/dist/llm/common/formatCurrentState.js +342 -0
- package/dist/llm/common/formatCurrentState.js.map +1 -0
- package/dist/llm/common/tool.d.ts +32 -0
- package/dist/llm/common/tool.js +6 -0
- package/dist/llm/common/tool.js.map +1 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/eval.test.d.ts +1 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/eval.test.js +433 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/eval.test.js.map +1 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/index.d.ts +18 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/index.js +153 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/index.js.map +1 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/prompt.d.ts +1 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/prompt.js +168 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/prompt.js.map +1 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindLinesTool.d.ts +19 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindLinesTool.js +65 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindLinesTool.js.map +1 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindServicesTool.d.ts +21 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindServicesTool.js +115 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindServicesTool.js.map +1 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindStationsTool.d.ts +24 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindStationsTool.js +110 -0
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindStationsTool.js.map +1 -0
- package/dist/llm/functions/generateIssueTitleAndSlug/index.d.ts +14 -0
- package/dist/llm/functions/generateIssueTitleAndSlug/index.js +38 -0
- package/dist/llm/functions/generateIssueTitleAndSlug/index.js.map +1 -0
- package/dist/llm/functions/generateIssueTitleAndSlug/prompt.d.ts +1 -0
- package/dist/llm/functions/generateIssueTitleAndSlug/prompt.js +23 -0
- package/dist/llm/functions/generateIssueTitleAndSlug/prompt.js.map +1 -0
- package/dist/llm/functions/translate/index.d.ts +1 -0
- package/dist/llm/functions/translate/index.js +59 -0
- package/dist/llm/functions/translate/index.js.map +1 -0
- package/dist/llm/functions/triageNewEvidence/eval.test.d.ts +1 -0
- package/dist/llm/functions/triageNewEvidence/eval.test.js +139 -0
- package/dist/llm/functions/triageNewEvidence/eval.test.js.map +1 -0
- package/dist/llm/functions/triageNewEvidence/index.d.ts +37 -0
- package/dist/llm/functions/triageNewEvidence/index.js +121 -0
- package/dist/llm/functions/triageNewEvidence/index.js.map +1 -0
- package/dist/llm/functions/triageNewEvidence/prompt.d.ts +1 -0
- package/dist/llm/functions/triageNewEvidence/prompt.js +60 -0
- package/dist/llm/functions/triageNewEvidence/prompt.js.map +1 -0
- package/dist/llm/functions/triageNewEvidence/tools/FindIssuesTool.d.ts +19 -0
- package/dist/llm/functions/triageNewEvidence/tools/FindIssuesTool.js +65 -0
- package/dist/llm/functions/triageNewEvidence/tools/FindIssuesTool.js.map +1 -0
- package/dist/llm/functions/triageNewEvidence/tools/GetIssueTool.d.ts +19 -0
- package/dist/llm/functions/triageNewEvidence/tools/GetIssueTool.js +37 -0
- package/dist/llm/functions/triageNewEvidence/tools/GetIssueTool.js.map +1 -0
- package/dist/scripts/ingestViaWebhook.d.ts +1 -0
- package/dist/scripts/ingestViaWebhook.js +9 -0
- package/dist/scripts/ingestViaWebhook.js.map +1 -0
- package/dist/validators/buildContext.d.ts +7 -0
- package/dist/validators/buildContext.js +164 -0
- package/dist/validators/buildContext.js.map +1 -0
- package/dist/validators/index.d.ts +17 -0
- package/dist/validators/index.js +58 -0
- package/dist/validators/index.js.map +1 -0
- package/dist/validators/issue.d.ts +13 -0
- package/dist/validators/issue.js +220 -0
- package/dist/validators/issue.js.map +1 -0
- package/dist/validators/landmark.d.ts +7 -0
- package/dist/validators/landmark.js +43 -0
- package/dist/validators/landmark.js.map +1 -0
- package/dist/validators/line.d.ts +8 -0
- package/dist/validators/line.js +87 -0
- package/dist/validators/line.js.map +1 -0
- package/dist/validators/operator.d.ts +7 -0
- package/dist/validators/operator.js +43 -0
- package/dist/validators/operator.js.map +1 -0
- package/dist/validators/service.d.ts +8 -0
- package/dist/validators/service.js +87 -0
- package/dist/validators/service.js.map +1 -0
- package/dist/validators/station.d.ts +8 -0
- package/dist/validators/station.js +93 -0
- package/dist/validators/station.js.map +1 -0
- package/dist/validators/town.d.ts +7 -0
- package/dist/validators/town.js +43 -0
- package/dist/validators/town.js.map +1 -0
- package/dist/validators/types.d.ts +19 -0
- package/dist/validators/types.js +2 -0
- package/dist/validators/types.js.map +1 -0
- package/dist/validators/utils.d.ts +2 -0
- package/dist/validators/utils.js +9 -0
- package/dist/validators/utils.js.map +1 -0
- package/package.json +11 -7
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { DateTime } from 'luxon';\nimport type {\n ParsedResponse,\n ResponseInputItem,\n} from 'openai/resources/responses/responses.mjs';\nimport z from 'zod';\nimport { openAiClient } from '#llm/client.js';\nimport type { MRTDownRepository } from '#repo/MRTDownRepository.js';\nimport { IssueIdSchema } from '#schema/issue/id.js';\nimport { IssueTypeSchema } from '#schema/issue/issueType.js';\nimport { assert } from '#util/assert.js';\nimport type { ToolRegistry } from '../../common/tool.js';\nimport { buildSystemPrompt } from './prompt.js';\nimport { FindIssuesTool } from './tools/FindIssuesTool.js';\nimport { GetIssueTool } from './tools/GetIssueTool.js';\n\nconst TOOL_CALL_LIMIT = 5;\n\nconst ResponseSchema = z.object({\n result: z.discriminatedUnion('type', [\n z.object({\n kind: z.literal('part-of-existing-issue'),\n issueId: IssueIdSchema,\n }),\n z.object({\n kind: z.literal('part-of-new-issue'),\n issueType: IssueTypeSchema,\n }),\n z.object({\n kind: z.literal('irrelevant-content'),\n }),\n ]),\n});\n\nexport type TriageNewEvidenceParams = {\n newEvidence: {\n ts: string;\n text: string;\n };\n repo: MRTDownRepository;\n};\n\nexport type TriageNewEvidenceResult = z.infer<typeof ResponseSchema>;\n\nexport async function triageNewEvidence(params: TriageNewEvidenceParams) {\n const evidenceTs = DateTime.fromISO(params.newEvidence.ts);\n assert(evidenceTs.isValid, `Invalid date: ${params.newEvidence.ts}`);\n\n const findIssuesTool = new FindIssuesTool(params.repo);\n const getIssueTool = new GetIssueTool(params.repo);\n const toolRegistry: ToolRegistry = {\n [findIssuesTool.name]: findIssuesTool,\n [getIssueTool.name]: getIssueTool,\n };\n\n const systemPrompt = buildSystemPrompt();\n\n const context: ResponseInputItem[] = [\n {\n role: 'user',\n content: `\nEvidence: ${params.newEvidence.text}\n\nTimestamp: ${evidenceTs.toISO({ includeOffset: true })}\n`.trim(),\n },\n ];\n\n let toolCallCount = 0;\n\n let response: ParsedResponse<z.infer<typeof ResponseSchema>>;\n do {\n response = await openAiClient.responses.parse({\n model: 'gpt-5-mini',\n input: context,\n instructions: systemPrompt,\n text: {\n format: {\n type: 'json_schema',\n name: 'Response',\n strict: true,\n schema: z.toJSONSchema(ResponseSchema),\n },\n },\n tools: Object.values(toolRegistry).map((tool) => {\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.paramsSchema,\n strict: true,\n };\n }),\n // Don't persist conversation with OpenAI, but include reasoning content to\n // continue the thread with the same reasoning.\n store: false,\n include: ['reasoning.encrypted_content'],\n });\n\n for (const item of response.output) {\n switch (item.type) {\n case 'function_call': {\n /**\n * Prevent the `parsed_arguments` field from being included\n * https://github.com/openai/openai-python/issues/2374\n */\n context.push({\n type: 'function_call',\n id: item.id,\n call_id: item.call_id,\n name: item.name,\n arguments: item.arguments,\n });\n\n if (toolCallCount > TOOL_CALL_LIMIT) {\n context.push({\n type: 'function_call_output',\n call_id: item.call_id,\n output: 'Ran out of tool calls. Stop Calling.',\n });\n console.log(\n 'Forced short-circuit, returning error message in tool call result.',\n );\n }\n\n if (item.name in toolRegistry) {\n const tool = toolRegistry[item.name];\n\n const params = tool.parseParams(JSON.parse(item.arguments));\n // Call the tool's run function\n const result = await tool.runner(params);\n\n context.push({\n type: 'function_call_output',\n call_id: item.call_id,\n output: result,\n });\n }\n\n toolCallCount++;\n break;\n }\n default: {\n context.push(item);\n break;\n }\n }\n }\n } while (response.output.some((item) => item.type === 'function_call'));\n\n assert(response.output_parsed != null, 'Response output parsed is null');\n\n return response.output_parsed;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function buildSystemPrompt(): string;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export function buildSystemPrompt() {
|
|
2
|
+
return `
|
|
3
|
+
You are an expert at triaging new evidence into an existing issue or a new issue.
|
|
4
|
+
|
|
5
|
+
Your task: Triage the new evidence into an existing issue or a new issue.
|
|
6
|
+
|
|
7
|
+
ISSUE TYPES:
|
|
8
|
+
- disruption: Service disruptions (e.g. train delays, line faults, operational failures)
|
|
9
|
+
- maintenance: Planned maintenance works (e.g. system upgrades, infrastructure maintenance)
|
|
10
|
+
- infra: Infrastructure issues (e.g. station lift outages, platform door faults, facility breakdowns)
|
|
11
|
+
|
|
12
|
+
DECISION PROCESS:
|
|
13
|
+
1. Extract key information from evidence: affected service/line, location, issue type, time window
|
|
14
|
+
2. Use findIssues tool to search for related issues by service name or line
|
|
15
|
+
3. Use getIssue tool to review each candidate issue's scope, periods, and effects
|
|
16
|
+
4. Compare evidence location and timing with existing issue scope
|
|
17
|
+
5. Return appropriate classification with clear reasoning
|
|
18
|
+
|
|
19
|
+
CLASSIFICATION RULES:
|
|
20
|
+
|
|
21
|
+
Part of Existing Issue:
|
|
22
|
+
- Evidence must match the service/line AND have geographic overlap (stations or segments)
|
|
23
|
+
- Temporal proximity matters: evidence should occur during or immediately adjacent to the issue's period
|
|
24
|
+
- Multiple separate incidents on the same service are separate issues (not continuous scope expansion)
|
|
25
|
+
|
|
26
|
+
Part of New Issue:
|
|
27
|
+
- Evidence describes a distinct incident not covered by existing issues
|
|
28
|
+
- Different geographic location on the same service (e.g. different stations/segments)
|
|
29
|
+
- Different service/line altogether
|
|
30
|
+
- Different time period or issue type
|
|
31
|
+
|
|
32
|
+
Irrelevant Content:
|
|
33
|
+
- Opinion or commentary without operational details
|
|
34
|
+
- General statements without specific service/location/time information
|
|
35
|
+
- Marketing or non-operational content
|
|
36
|
+
|
|
37
|
+
SPECIFIC GUIDANCE BY ISSUE TYPE:
|
|
38
|
+
|
|
39
|
+
DISRUPTIONS:
|
|
40
|
+
- Location specificity is CRITICAL - different stations or segments are separate incidents
|
|
41
|
+
- Same service line alone is NOT sufficient for matching
|
|
42
|
+
- Must have EXACT geographic overlap: if evidence mentions different station pair, it's a new issue
|
|
43
|
+
- Examples: "fault between A and B" overlaps with existing issue only if existing covers A-B segment
|
|
44
|
+
- A fault "between B and C" on the same line is a different incident, even if it shares one endpoint station
|
|
45
|
+
|
|
46
|
+
MAINTENANCE:
|
|
47
|
+
- Service-level planned works that affect operating hours or service availability
|
|
48
|
+
- Examples: early line closures, reduced service windows, system upgrades affecting all trains
|
|
49
|
+
- NOT about specific facility repairs (those are infra)
|
|
50
|
+
|
|
51
|
+
INFRASTRUCTURE:
|
|
52
|
+
- Specific facility or asset breakdowns that need repair or renewal
|
|
53
|
+
- Examples: lift outages, platform screen door faults/renewal, escalator repairs, door malfunctions
|
|
54
|
+
- Facility-specific: affects particular station and facility type (e.g. lift at Station X)
|
|
55
|
+
- Can be scheduled (renewal works) or unplanned (breakdowns)
|
|
56
|
+
- Link to existing issue if same station, same facility, service still active
|
|
57
|
+
- Different station or facility type = new issue
|
|
58
|
+
`.trim();
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/prompt.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB;IAC/B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwDR,CAAC,IAAI,EAAE,CAAC;AACT,CAAC","sourcesContent":["export function buildSystemPrompt() {\n return `\nYou are an expert at triaging new evidence into an existing issue or a new issue.\n\nYour task: Triage the new evidence into an existing issue or a new issue.\n\nISSUE TYPES:\n- disruption: Service disruptions (e.g. train delays, line faults, operational failures)\n- maintenance: Planned maintenance works (e.g. system upgrades, infrastructure maintenance)\n- infra: Infrastructure issues (e.g. station lift outages, platform door faults, facility breakdowns)\n\nDECISION PROCESS:\n1. Extract key information from evidence: affected service/line, location, issue type, time window\n2. Use findIssues tool to search for related issues by service name or line\n3. Use getIssue tool to review each candidate issue's scope, periods, and effects\n4. Compare evidence location and timing with existing issue scope\n5. Return appropriate classification with clear reasoning\n\nCLASSIFICATION RULES:\n\nPart of Existing Issue:\n- Evidence must match the service/line AND have geographic overlap (stations or segments)\n- Temporal proximity matters: evidence should occur during or immediately adjacent to the issue's period\n- Multiple separate incidents on the same service are separate issues (not continuous scope expansion)\n\nPart of New Issue:\n- Evidence describes a distinct incident not covered by existing issues\n- Different geographic location on the same service (e.g. different stations/segments)\n- Different service/line altogether\n- Different time period or issue type\n\nIrrelevant Content:\n- Opinion or commentary without operational details\n- General statements without specific service/location/time information\n- Marketing or non-operational content\n\nSPECIFIC GUIDANCE BY ISSUE TYPE:\n\nDISRUPTIONS:\n- Location specificity is CRITICAL - different stations or segments are separate incidents\n- Same service line alone is NOT sufficient for matching\n- Must have EXACT geographic overlap: if evidence mentions different station pair, it's a new issue\n- Examples: \"fault between A and B\" overlaps with existing issue only if existing covers A-B segment\n- A fault \"between B and C\" on the same line is a different incident, even if it shares one endpoint station\n\nMAINTENANCE:\n- Service-level planned works that affect operating hours or service availability\n- Examples: early line closures, reduced service windows, system upgrades affecting all trains\n- NOT about specific facility repairs (those are infra)\n\nINFRASTRUCTURE:\n- Specific facility or asset breakdowns that need repair or renewal\n- Examples: lift outages, platform screen door faults/renewal, escalator repairs, door malfunctions\n- Facility-specific: affects particular station and facility type (e.g. lift at Station X)\n- Can be scheduled (renewal works) or unplanned (breakdowns)\n- Link to existing issue if same station, same facility, service still active\n- Different station or facility type = new issue\n`.trim();\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
import { Tool } from '#llm/common/tool.js';
|
|
3
|
+
import type { MRTDownRepository } from '#repo/MRTDownRepository.js';
|
|
4
|
+
declare const FindIssuesToolParametersSchema: z.ZodObject<{
|
|
5
|
+
query: z.ZodString;
|
|
6
|
+
}, z.z.core.$strip>;
|
|
7
|
+
type FindIssuesToolParameters = z.infer<typeof FindIssuesToolParametersSchema>;
|
|
8
|
+
export declare class FindIssuesTool extends Tool<FindIssuesToolParameters> {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
private readonly repo;
|
|
12
|
+
constructor(repo: MRTDownRepository);
|
|
13
|
+
get paramsSchema(): {
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
};
|
|
16
|
+
parseParams(params: unknown): FindIssuesToolParameters;
|
|
17
|
+
runner(params: FindIssuesToolParameters): Promise<string>;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { gfmToMarkdown } from 'mdast-util-gfm';
|
|
2
|
+
import { toMarkdown } from 'mdast-util-to-markdown';
|
|
3
|
+
import z from 'zod';
|
|
4
|
+
import { Tool } from '#llm/common/tool.js';
|
|
5
|
+
const FindIssuesToolParametersSchema = z.object({
|
|
6
|
+
query: z.string().describe('Plain text to search for issues.'),
|
|
7
|
+
});
|
|
8
|
+
export class FindIssuesTool extends Tool {
|
|
9
|
+
name = 'findIssues';
|
|
10
|
+
description = 'Find issues by query';
|
|
11
|
+
repo;
|
|
12
|
+
constructor(repo) {
|
|
13
|
+
super();
|
|
14
|
+
this.repo = repo;
|
|
15
|
+
}
|
|
16
|
+
get paramsSchema() {
|
|
17
|
+
return z.toJSONSchema(FindIssuesToolParametersSchema);
|
|
18
|
+
}
|
|
19
|
+
parseParams(params) {
|
|
20
|
+
return FindIssuesToolParametersSchema.parse(params);
|
|
21
|
+
}
|
|
22
|
+
async runner(params) {
|
|
23
|
+
console.log('[findIssues] Calling tool with parameters:', params);
|
|
24
|
+
const issues = this.repo.issues.searchByQuery(params.query);
|
|
25
|
+
const issueTable = {
|
|
26
|
+
type: 'table',
|
|
27
|
+
children: [
|
|
28
|
+
{
|
|
29
|
+
type: 'tableRow',
|
|
30
|
+
children: [
|
|
31
|
+
{
|
|
32
|
+
type: 'tableCell',
|
|
33
|
+
children: [{ type: 'text', value: 'Issue ID' }],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: 'tableCell',
|
|
37
|
+
children: [{ type: 'text', value: 'Issue Title' }],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
for (const issue of issues) {
|
|
44
|
+
issueTable.children.push({
|
|
45
|
+
type: 'tableRow',
|
|
46
|
+
children: [
|
|
47
|
+
{
|
|
48
|
+
type: 'tableCell',
|
|
49
|
+
children: [{ type: 'text', value: issue.issue.id }],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: 'tableCell',
|
|
53
|
+
children: [{ type: 'text', value: issue.issue.title['en-SG'] }],
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const output = toMarkdown(issueTable, {
|
|
59
|
+
extensions: [gfmToMarkdown()],
|
|
60
|
+
});
|
|
61
|
+
console.log(`[findIssues] Response output:\n${output}`);
|
|
62
|
+
return output;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=FindIssuesTool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FindIssuesTool.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/tools/FindIssuesTool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAG3C,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;CAC/D,CAAC,CAAC;AAGH,MAAM,OAAO,cAAe,SAAQ,IAA8B;IACzD,IAAI,GAAG,YAAY,CAAC;IACpB,WAAW,GAAG,sBAAsB,CAAC;IAC3B,IAAI,CAAoB;IAEzC,YAAY,IAAuB;QACjC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,IAAW,YAAY;QACrB,OAAO,CAAC,CAAC,YAAY,CAAC,8BAA8B,CAAC,CAAC;IACxD,CAAC;IAEM,WAAW,CAAC,MAAe;QAChC,OAAO,8BAA8B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACtD,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,MAAgC;QAClD,OAAO,CAAC,GAAG,CAAC,4CAA4C,EAAE,MAAM,CAAC,CAAC;QAElE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAE5D,MAAM,UAAU,GAAU;YACxB,IAAI,EAAE,OAAO;YACb,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,UAAU;oBAChB,QAAQ,EAAE;wBACR;4BACE,IAAI,EAAE,WAAW;4BACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;yBAChD;wBACD;4BACE,IAAI,EAAE,WAAW;4BACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;yBACnD;qBACF;iBACF;aACF;SACF,CAAC;QAEF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACvB,IAAI,EAAE,UAAU;gBAChB,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,WAAW;wBACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;qBACpD;oBACD;wBACE,IAAI,EAAE,WAAW;wBACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;qBAChE;iBACF;aACF,CAAC,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,EAAE;YACpC,UAAU,EAAE,CAAC,aAAa,EAAE,CAAC;SAC9B,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,kCAAkC,MAAM,EAAE,CAAC,CAAC;QAExD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF","sourcesContent":["import type { Table } from 'mdast';\nimport { gfmToMarkdown } from 'mdast-util-gfm';\nimport { toMarkdown } from 'mdast-util-to-markdown';\nimport z from 'zod';\nimport { Tool } from '#llm/common/tool.js';\nimport type { MRTDownRepository } from '#repo/MRTDownRepository.js';\n\nconst FindIssuesToolParametersSchema = z.object({\n query: z.string().describe('Plain text to search for issues.'),\n});\ntype FindIssuesToolParameters = z.infer<typeof FindIssuesToolParametersSchema>;\n\nexport class FindIssuesTool extends Tool<FindIssuesToolParameters> {\n public name = 'findIssues';\n public description = 'Find issues by query';\n private readonly repo: MRTDownRepository;\n\n constructor(repo: MRTDownRepository) {\n super();\n this.repo = repo;\n }\n\n public get paramsSchema(): { [key: string]: unknown } {\n return z.toJSONSchema(FindIssuesToolParametersSchema);\n }\n\n public parseParams(params: unknown): FindIssuesToolParameters {\n return FindIssuesToolParametersSchema.parse(params);\n }\n\n public async runner(params: FindIssuesToolParameters): Promise<string> {\n console.log('[findIssues] Calling tool with parameters:', params);\n\n const issues = this.repo.issues.searchByQuery(params.query);\n\n const issueTable: Table = {\n type: 'table',\n children: [\n {\n type: 'tableRow',\n children: [\n {\n type: 'tableCell',\n children: [{ type: 'text', value: 'Issue ID' }],\n },\n {\n type: 'tableCell',\n children: [{ type: 'text', value: 'Issue Title' }],\n },\n ],\n },\n ],\n };\n\n for (const issue of issues) {\n issueTable.children.push({\n type: 'tableRow',\n children: [\n {\n type: 'tableCell',\n children: [{ type: 'text', value: issue.issue.id }],\n },\n {\n type: 'tableCell',\n children: [{ type: 'text', value: issue.issue.title['en-SG'] }],\n },\n ],\n });\n }\n\n const output = toMarkdown(issueTable, {\n extensions: [gfmToMarkdown()],\n });\n console.log(`[findIssues] Response output:\\n${output}`);\n\n return output;\n }\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
import type { MRTDownRepository } from '#repo/MRTDownRepository.js';
|
|
3
|
+
import { Tool } from '../../../common/tool.js';
|
|
4
|
+
declare const GetIssueToolParametersSchema: z.ZodObject<{
|
|
5
|
+
issueId: z.ZodString;
|
|
6
|
+
}, z.z.core.$strip>;
|
|
7
|
+
type GetIssueToolParameters = z.infer<typeof GetIssueToolParametersSchema>;
|
|
8
|
+
export declare class GetIssueTool extends Tool<GetIssueToolParameters> {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
private readonly repo;
|
|
12
|
+
constructor(repo: MRTDownRepository);
|
|
13
|
+
get paramsSchema(): {
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
};
|
|
16
|
+
parseParams(params: unknown): GetIssueToolParameters;
|
|
17
|
+
runner(params: GetIssueToolParameters): Promise<string>;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
import { deriveCurrentState } from '#repo/issue/helpers/deriveCurrentState.js';
|
|
3
|
+
import { formatCurrentState } from '../../../common/formatCurrentState.js';
|
|
4
|
+
import { Tool } from '../../../common/tool.js';
|
|
5
|
+
const GetIssueToolParametersSchema = z.object({
|
|
6
|
+
issueId: z.string(),
|
|
7
|
+
});
|
|
8
|
+
export class GetIssueTool extends Tool {
|
|
9
|
+
name = 'getIssue';
|
|
10
|
+
description = 'Get an issue by ID';
|
|
11
|
+
repo;
|
|
12
|
+
constructor(repo) {
|
|
13
|
+
super();
|
|
14
|
+
this.repo = repo;
|
|
15
|
+
}
|
|
16
|
+
get paramsSchema() {
|
|
17
|
+
return z.toJSONSchema(GetIssueToolParametersSchema);
|
|
18
|
+
}
|
|
19
|
+
parseParams(params) {
|
|
20
|
+
return GetIssueToolParametersSchema.parse(params);
|
|
21
|
+
}
|
|
22
|
+
async runner(params) {
|
|
23
|
+
console.log('[getIssue] Calling tool with parameters:', params);
|
|
24
|
+
const issueBundle = this.repo.issues.get(params.issueId);
|
|
25
|
+
if (issueBundle == null) {
|
|
26
|
+
return `Issue ${params.issueId} not found`;
|
|
27
|
+
}
|
|
28
|
+
const currentState = deriveCurrentState(issueBundle);
|
|
29
|
+
const output = formatCurrentState({
|
|
30
|
+
state: currentState,
|
|
31
|
+
evidence: issueBundle.evidence,
|
|
32
|
+
});
|
|
33
|
+
console.log(`[getIssue] Response output:\n${output}`);
|
|
34
|
+
return output;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=GetIssueTool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GetIssueTool.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/tools/GetIssueTool.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAE/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAE/C,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACpB,CAAC,CAAC;AAGH,MAAM,OAAO,YAAa,SAAQ,IAA4B;IACrD,IAAI,GAAG,UAAU,CAAC;IAClB,WAAW,GAAG,oBAAoB,CAAC;IACzB,IAAI,CAAoB;IAEzC,YAAY,IAAuB;QACjC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,IAAW,YAAY;QACrB,OAAO,CAAC,CAAC,YAAY,CAAC,4BAA4B,CAAC,CAAC;IACtD,CAAC;IAEM,WAAW,CAAC,MAAe;QAChC,OAAO,4BAA4B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACpD,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,MAA8B;QAChD,OAAO,CAAC,GAAG,CAAC,0CAA0C,EAAE,MAAM,CAAC,CAAC;QAEhE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzD,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YACxB,OAAO,SAAS,MAAM,CAAC,OAAO,YAAY,CAAC;QAC7C,CAAC;QAED,MAAM,YAAY,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAErD,MAAM,MAAM,GAAG,kBAAkB,CAAC;YAChC,KAAK,EAAE,YAAY;YACnB,QAAQ,EAAE,WAAW,CAAC,QAAQ;SAC/B,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,gCAAgC,MAAM,EAAE,CAAC,CAAC;QAEtD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF","sourcesContent":["import z from 'zod';\nimport { deriveCurrentState } from '#repo/issue/helpers/deriveCurrentState.js';\nimport type { MRTDownRepository } from '#repo/MRTDownRepository.js';\nimport { formatCurrentState } from '../../../common/formatCurrentState.js';\nimport { Tool } from '../../../common/tool.js';\n\nconst GetIssueToolParametersSchema = z.object({\n issueId: z.string(),\n});\ntype GetIssueToolParameters = z.infer<typeof GetIssueToolParametersSchema>;\n\nexport class GetIssueTool extends Tool<GetIssueToolParameters> {\n public name = 'getIssue';\n public description = 'Get an issue by ID';\n private readonly repo: MRTDownRepository;\n\n constructor(repo: MRTDownRepository) {\n super();\n this.repo = repo;\n }\n\n public get paramsSchema(): { [key: string]: unknown } {\n return z.toJSONSchema(GetIssueToolParametersSchema);\n }\n\n public parseParams(params: unknown): GetIssueToolParameters {\n return GetIssueToolParametersSchema.parse(params);\n }\n\n public async runner(params: GetIssueToolParameters): Promise<string> {\n console.log('[getIssue] Calling tool with parameters:', params);\n\n const issueBundle = this.repo.issues.get(params.issueId);\n if (issueBundle == null) {\n return `Issue ${params.issueId} not found`;\n }\n\n const currentState = deriveCurrentState(issueBundle);\n\n const output = formatCurrentState({\n state: currentState,\n evidence: issueBundle.evidence,\n });\n console.log(`[getIssue] Response output:\\n${output}`);\n\n return output;\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { assert } from '../util/assert.js';
|
|
2
|
+
import { ingestContent } from '../util/ingestContent/index.js';
|
|
3
|
+
const { MESSAGE } = process.env;
|
|
4
|
+
assert(MESSAGE != null, 'Expected MESSAGE env var');
|
|
5
|
+
const message = JSON.parse(MESSAGE);
|
|
6
|
+
for (const content of message.content) {
|
|
7
|
+
await ingestContent(content);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=ingestViaWebhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingestViaWebhook.js","sourceRoot":"/","sources":["scripts/ingestViaWebhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;AAChC,MAAM,CAAC,OAAO,IAAI,IAAI,EAAE,0BAA0B,CAAC,CAAC;AAEpD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAEjC,CAAC;AACF,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IACtC,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC","sourcesContent":["import { assert } from '../util/assert.js';\nimport { ingestContent } from '../util/ingestContent/index.js';\nimport type { IngestContent } from '../util/ingestContent/types.js';\n\nconst { MESSAGE } = process.env;\nassert(MESSAGE != null, 'Expected MESSAGE env var');\n\nconst message = JSON.parse(MESSAGE) as {\n content: IngestContent[];\n};\nfor (const content of message.content) {\n await ingestContent(content);\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IStore } from '../repo/common/store.js';
|
|
2
|
+
import type { ValidationContext } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Loads all entity IDs from the store into a ValidationContext.
|
|
5
|
+
* Use this to build context once, then pass it to validators for relationship checks.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildContext(store: IStore): ValidationContext;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { NdJson } from 'json-nd';
|
|
3
|
+
import z from 'zod';
|
|
4
|
+
import { LandmarkSchema } from '#schema/Landmark.js';
|
|
5
|
+
import { LineSchema } from '#schema/Line.js';
|
|
6
|
+
import { OperatorSchema } from '#schema/Operator.js';
|
|
7
|
+
import { ServiceSchema } from '#schema/Service.js';
|
|
8
|
+
import { StationSchema } from '#schema/Station.js';
|
|
9
|
+
import { TownSchema } from '#schema/Town.js';
|
|
10
|
+
import { DIR_ISSUE, DIR_LANDMARK, DIR_LINE, DIR_OPERATOR, DIR_SERVICE, DIR_STATION, DIR_TOWN, } from '../constants.js';
|
|
11
|
+
import { loadJson } from './utils.js';
|
|
12
|
+
/**
|
|
13
|
+
* Loads all entity IDs from the store into a ValidationContext.
|
|
14
|
+
* Use this to build context once, then pass it to validators for relationship checks.
|
|
15
|
+
*/
|
|
16
|
+
export function buildContext(store) {
|
|
17
|
+
const ctx = {
|
|
18
|
+
townIds: new Set(),
|
|
19
|
+
landmarkIds: new Set(),
|
|
20
|
+
operatorIds: new Set(),
|
|
21
|
+
lineIds: new Set(),
|
|
22
|
+
serviceIds: new Set(),
|
|
23
|
+
stationIds: new Set(),
|
|
24
|
+
evidenceIdsByIssue: new Map(),
|
|
25
|
+
};
|
|
26
|
+
try {
|
|
27
|
+
for (const file of store.listDir(DIR_TOWN)) {
|
|
28
|
+
if (!file.endsWith('.json'))
|
|
29
|
+
continue;
|
|
30
|
+
const path = join(DIR_TOWN, file);
|
|
31
|
+
const raw = loadJson(store, path);
|
|
32
|
+
const parsed = TownSchema.safeParse(raw);
|
|
33
|
+
if (parsed.success)
|
|
34
|
+
ctx.townIds.add(parsed.data.id);
|
|
35
|
+
else
|
|
36
|
+
console.warn(`[buildContext] Skipping invalid town: ${path}`, parsed.error?.message ?? parsed.error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.warn('[buildContext] Failed to load towns:', err instanceof Error ? err.message : err);
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
for (const file of store.listDir(DIR_LANDMARK)) {
|
|
44
|
+
if (!file.endsWith('.json'))
|
|
45
|
+
continue;
|
|
46
|
+
const path = join(DIR_LANDMARK, file);
|
|
47
|
+
const raw = loadJson(store, path);
|
|
48
|
+
const parsed = LandmarkSchema.safeParse(raw);
|
|
49
|
+
if (parsed.success)
|
|
50
|
+
ctx.landmarkIds.add(parsed.data.id);
|
|
51
|
+
else
|
|
52
|
+
console.warn(`[buildContext] Skipping invalid landmark: ${path}`, parsed.error?.message ?? parsed.error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
console.warn('[buildContext] Failed to load landmarks:', err instanceof Error ? err.message : err);
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
for (const file of store.listDir(DIR_OPERATOR)) {
|
|
60
|
+
if (!file.endsWith('.json'))
|
|
61
|
+
continue;
|
|
62
|
+
const path = join(DIR_OPERATOR, file);
|
|
63
|
+
const raw = loadJson(store, path);
|
|
64
|
+
const parsed = OperatorSchema.safeParse(raw);
|
|
65
|
+
if (parsed.success)
|
|
66
|
+
ctx.operatorIds.add(parsed.data.id);
|
|
67
|
+
else
|
|
68
|
+
console.warn(`[buildContext] Skipping invalid operator: ${path}`, parsed.error?.message ?? parsed.error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
console.warn('[buildContext] Failed to load operators:', err instanceof Error ? err.message : err);
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
for (const file of store.listDir(DIR_LINE)) {
|
|
76
|
+
if (!file.endsWith('.json'))
|
|
77
|
+
continue;
|
|
78
|
+
const path = join(DIR_LINE, file);
|
|
79
|
+
const raw = loadJson(store, path);
|
|
80
|
+
const parsed = LineSchema.safeParse(raw);
|
|
81
|
+
if (parsed.success)
|
|
82
|
+
ctx.lineIds.add(parsed.data.id);
|
|
83
|
+
else
|
|
84
|
+
console.warn(`[buildContext] Skipping invalid line: ${path}`, parsed.error?.message ?? parsed.error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
console.warn('[buildContext] Failed to load lines:', err instanceof Error ? err.message : err);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
for (const file of store.listDir(DIR_STATION)) {
|
|
92
|
+
if (!file.endsWith('.json'))
|
|
93
|
+
continue;
|
|
94
|
+
const path = join(DIR_STATION, file);
|
|
95
|
+
const raw = loadJson(store, path);
|
|
96
|
+
const parsed = StationSchema.safeParse(raw);
|
|
97
|
+
if (parsed.success)
|
|
98
|
+
ctx.stationIds.add(parsed.data.id);
|
|
99
|
+
else
|
|
100
|
+
console.warn(`[buildContext] Skipping invalid station: ${path}`, parsed.error?.message ?? parsed.error);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
console.warn('[buildContext] Failed to load stations:', err instanceof Error ? err.message : err);
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
for (const file of store.listDir(DIR_SERVICE)) {
|
|
108
|
+
if (!file.endsWith('.json'))
|
|
109
|
+
continue;
|
|
110
|
+
const path = join(DIR_SERVICE, file);
|
|
111
|
+
const raw = loadJson(store, path);
|
|
112
|
+
const parsed = ServiceSchema.safeParse(raw);
|
|
113
|
+
if (parsed.success)
|
|
114
|
+
ctx.serviceIds.add(parsed.data.id);
|
|
115
|
+
else
|
|
116
|
+
console.warn(`[buildContext] Skipping invalid service: ${path}`, parsed.error?.message ?? parsed.error);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
console.warn('[buildContext] Failed to load services:', err instanceof Error ? err.message : err);
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const years = store.listDir(DIR_ISSUE);
|
|
124
|
+
for (const year of years) {
|
|
125
|
+
if (!/^\d{4}$/.test(year))
|
|
126
|
+
continue;
|
|
127
|
+
const monthsPath = join(DIR_ISSUE, year);
|
|
128
|
+
const months = store.listDir(monthsPath);
|
|
129
|
+
for (const month of months) {
|
|
130
|
+
if (!/^\d{2}$/.test(month))
|
|
131
|
+
continue;
|
|
132
|
+
const issuesPath = join(monthsPath, month);
|
|
133
|
+
const issues = store.listDir(issuesPath);
|
|
134
|
+
for (const issueId of issues) {
|
|
135
|
+
const relBase = join(DIR_ISSUE, year, month, issueId);
|
|
136
|
+
const evidencePath = join(relBase, 'evidence.ndjson');
|
|
137
|
+
const evidenceIds = new Set();
|
|
138
|
+
try {
|
|
139
|
+
const content = store.readText(evidencePath).trim();
|
|
140
|
+
if (content) {
|
|
141
|
+
const parsed = NdJson.parse(content);
|
|
142
|
+
for (const row of parsed) {
|
|
143
|
+
const idParsed = z.object({ id: z.string() }).safeParse(row);
|
|
144
|
+
if (idParsed.success)
|
|
145
|
+
evidenceIds.add(idParsed.data.id);
|
|
146
|
+
else
|
|
147
|
+
console.warn(`[buildContext] Skipping invalid evidence row in ${evidencePath}:`, idParsed.error?.message ?? idParsed.error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
console.warn(`[buildContext] Failed to read evidence: ${evidencePath}`, err instanceof Error ? err.message : err);
|
|
153
|
+
}
|
|
154
|
+
ctx.evidenceIdsByIssue.set(relBase, evidenceIds);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
console.warn('[buildContext] Failed to load issues/evidence:', err instanceof Error ? err.message : err);
|
|
161
|
+
}
|
|
162
|
+
return ctx;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=buildContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildContext.js","sourceRoot":"/","sources":["validators/buildContext.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from 'node:path';\nimport { NdJson } from 'json-nd';\nimport z from 'zod';\nimport { LandmarkSchema } from '#schema/Landmark.js';\nimport { LineSchema } from '#schema/Line.js';\nimport { OperatorSchema } from '#schema/Operator.js';\nimport { ServiceSchema } from '#schema/Service.js';\nimport { StationSchema } from '#schema/Station.js';\nimport { TownSchema } from '#schema/Town.js';\nimport {\n DIR_ISSUE,\n DIR_LANDMARK,\n DIR_LINE,\n DIR_OPERATOR,\n DIR_SERVICE,\n DIR_STATION,\n DIR_TOWN,\n} from '../constants.js';\nimport type { IStore } from '../repo/common/store.js';\nimport type { ValidationContext } from './types.js';\nimport { loadJson } from './utils.js';\n\n/**\n * Loads all entity IDs from the store into a ValidationContext.\n * Use this to build context once, then pass it to validators for relationship checks.\n */\nexport function buildContext(store: IStore): ValidationContext {\n const ctx: ValidationContext = {\n townIds: new Set(),\n landmarkIds: new Set(),\n operatorIds: new Set(),\n lineIds: new Set(),\n serviceIds: new Set(),\n stationIds: new Set(),\n evidenceIdsByIssue: new Map(),\n };\n\n try {\n for (const file of store.listDir(DIR_TOWN)) {\n if (!file.endsWith('.json')) continue;\n const path = join(DIR_TOWN, file);\n const raw = loadJson<unknown>(store, path);\n const parsed = TownSchema.safeParse(raw);\n if (parsed.success) ctx.townIds.add(parsed.data.id);\n else\n console.warn(\n `[buildContext] Skipping invalid town: ${path}`,\n parsed.error?.message ?? parsed.error,\n );\n }\n } catch (err) {\n console.warn(\n '[buildContext] Failed to load towns:',\n err instanceof Error ? err.message : err,\n );\n }\n\n try {\n for (const file of store.listDir(DIR_LANDMARK)) {\n if (!file.endsWith('.json')) continue;\n const path = join(DIR_LANDMARK, file);\n const raw = loadJson<unknown>(store, path);\n const parsed = LandmarkSchema.safeParse(raw);\n if (parsed.success) ctx.landmarkIds.add(parsed.data.id);\n else\n console.warn(\n `[buildContext] Skipping invalid landmark: ${path}`,\n parsed.error?.message ?? parsed.error,\n );\n }\n } catch (err) {\n console.warn(\n '[buildContext] Failed to load landmarks:',\n err instanceof Error ? err.message : err,\n );\n }\n\n try {\n for (const file of store.listDir(DIR_OPERATOR)) {\n if (!file.endsWith('.json')) continue;\n const path = join(DIR_OPERATOR, file);\n const raw = loadJson<unknown>(store, path);\n const parsed = OperatorSchema.safeParse(raw);\n if (parsed.success) ctx.operatorIds.add(parsed.data.id);\n else\n console.warn(\n `[buildContext] Skipping invalid operator: ${path}`,\n parsed.error?.message ?? parsed.error,\n );\n }\n } catch (err) {\n console.warn(\n '[buildContext] Failed to load operators:',\n err instanceof Error ? err.message : err,\n );\n }\n\n try {\n for (const file of store.listDir(DIR_LINE)) {\n if (!file.endsWith('.json')) continue;\n const path = join(DIR_LINE, file);\n const raw = loadJson<unknown>(store, path);\n const parsed = LineSchema.safeParse(raw);\n if (parsed.success) ctx.lineIds.add(parsed.data.id);\n else\n console.warn(\n `[buildContext] Skipping invalid line: ${path}`,\n parsed.error?.message ?? parsed.error,\n );\n }\n } catch (err) {\n console.warn(\n '[buildContext] Failed to load lines:',\n err instanceof Error ? err.message : err,\n );\n }\n\n try {\n for (const file of store.listDir(DIR_STATION)) {\n if (!file.endsWith('.json')) continue;\n const path = join(DIR_STATION, file);\n const raw = loadJson<unknown>(store, path);\n const parsed = StationSchema.safeParse(raw);\n if (parsed.success) ctx.stationIds.add(parsed.data.id);\n else\n console.warn(\n `[buildContext] Skipping invalid station: ${path}`,\n parsed.error?.message ?? parsed.error,\n );\n }\n } catch (err) {\n console.warn(\n '[buildContext] Failed to load stations:',\n err instanceof Error ? err.message : err,\n );\n }\n\n try {\n for (const file of store.listDir(DIR_SERVICE)) {\n if (!file.endsWith('.json')) continue;\n const path = join(DIR_SERVICE, file);\n const raw = loadJson<unknown>(store, path);\n const parsed = ServiceSchema.safeParse(raw);\n if (parsed.success) ctx.serviceIds.add(parsed.data.id);\n else\n console.warn(\n `[buildContext] Skipping invalid service: ${path}`,\n parsed.error?.message ?? parsed.error,\n );\n }\n } catch (err) {\n console.warn(\n '[buildContext] Failed to load services:',\n err instanceof Error ? err.message : err,\n );\n }\n\n try {\n const years = store.listDir(DIR_ISSUE);\n for (const year of years) {\n if (!/^\\d{4}$/.test(year)) continue;\n const monthsPath = join(DIR_ISSUE, year);\n const months = store.listDir(monthsPath);\n for (const month of months) {\n if (!/^\\d{2}$/.test(month)) continue;\n const issuesPath = join(monthsPath, month);\n const issues = store.listDir(issuesPath);\n for (const issueId of issues) {\n const relBase = join(DIR_ISSUE, year, month, issueId);\n const evidencePath = join(relBase, 'evidence.ndjson');\n const evidenceIds = new Set<string>();\n try {\n const content = store.readText(evidencePath).trim();\n if (content) {\n const parsed = NdJson.parse(content);\n for (const row of parsed) {\n const idParsed = z.object({ id: z.string() }).safeParse(row);\n if (idParsed.success) evidenceIds.add(idParsed.data.id);\n else\n console.warn(\n `[buildContext] Skipping invalid evidence row in ${evidencePath}:`,\n idParsed.error?.message ?? idParsed.error,\n );\n }\n }\n } catch (err) {\n console.warn(\n `[buildContext] Failed to read evidence: ${evidencePath}`,\n err instanceof Error ? err.message : err,\n );\n }\n ctx.evidenceIdsByIssue.set(relBase, evidenceIds);\n }\n }\n }\n } catch (err) {\n console.warn(\n '[buildContext] Failed to load issues/evidence:',\n err instanceof Error ? err.message : err,\n );\n }\n\n return ctx;\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { IStore } from '../repo/common/store.js';
|
|
2
|
+
import type { ValidationError } from './types.js';
|
|
3
|
+
export type ValidationScope = 'town' | 'landmark' | 'operator' | 'station' | 'line' | 'service' | 'issue';
|
|
4
|
+
export interface ValidateOptions {
|
|
5
|
+
/** When set, only run validators for these entity types. */
|
|
6
|
+
scope?: ValidationScope[];
|
|
7
|
+
}
|
|
8
|
+
export declare function validateAll(store: IStore, options?: ValidateOptions): ValidationError[];
|
|
9
|
+
export { buildContext } from './buildContext.js';
|
|
10
|
+
export { validateIssue, validateIssues } from './issue.js';
|
|
11
|
+
export { validateLandmarks } from './landmark.js';
|
|
12
|
+
export { validateLines, validateLinesRelationships, } from './line.js';
|
|
13
|
+
export { validateOperators } from './operator.js';
|
|
14
|
+
export { validateServices, validateServicesRelationships, } from './service.js';
|
|
15
|
+
export { validateStations, validateStationsRelationships, } from './station.js';
|
|
16
|
+
export { validateTowns } from './town.js';
|
|
17
|
+
export type { ValidationContext, ValidationError } from './types.js';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { buildContext } from './buildContext.js';
|
|
2
|
+
import { validateIssues } from './issue.js';
|
|
3
|
+
import { validateLandmarks } from './landmark.js';
|
|
4
|
+
import { validateLines, validateLinesRelationships } from './line.js';
|
|
5
|
+
import { validateOperators } from './operator.js';
|
|
6
|
+
import { validateServices, validateServicesRelationships } from './service.js';
|
|
7
|
+
import { validateStations, validateStationsRelationships } from './station.js';
|
|
8
|
+
import { validateTowns } from './town.js';
|
|
9
|
+
const SCOPE_NEEDS_CONTEXT = [
|
|
10
|
+
'station',
|
|
11
|
+
'line',
|
|
12
|
+
'service',
|
|
13
|
+
'issue',
|
|
14
|
+
];
|
|
15
|
+
function inScope(scope, type) {
|
|
16
|
+
return !scope || scope.has(type);
|
|
17
|
+
}
|
|
18
|
+
export function validateAll(store, options) {
|
|
19
|
+
const scope = options?.scope;
|
|
20
|
+
const scopeSet = scope && scope.length > 0 ? new Set(scope) : null;
|
|
21
|
+
const needsContext = !scopeSet || SCOPE_NEEDS_CONTEXT.some((t) => scopeSet.has(t));
|
|
22
|
+
const ctx = needsContext ? buildContext(store) : null;
|
|
23
|
+
const allErrors = [];
|
|
24
|
+
if (inScope(scopeSet, 'town'))
|
|
25
|
+
allErrors.push(...validateTowns(store));
|
|
26
|
+
if (inScope(scopeSet, 'landmark'))
|
|
27
|
+
allErrors.push(...validateLandmarks(store));
|
|
28
|
+
if (inScope(scopeSet, 'operator'))
|
|
29
|
+
allErrors.push(...validateOperators(store));
|
|
30
|
+
if (inScope(scopeSet, 'line')) {
|
|
31
|
+
allErrors.push(...validateLines(store));
|
|
32
|
+
if (ctx)
|
|
33
|
+
allErrors.push(...validateLinesRelationships(store, ctx));
|
|
34
|
+
}
|
|
35
|
+
if (inScope(scopeSet, 'station')) {
|
|
36
|
+
allErrors.push(...validateStations(store));
|
|
37
|
+
if (ctx)
|
|
38
|
+
allErrors.push(...validateStationsRelationships(store, ctx));
|
|
39
|
+
}
|
|
40
|
+
if (inScope(scopeSet, 'service')) {
|
|
41
|
+
allErrors.push(...validateServices(store));
|
|
42
|
+
if (ctx)
|
|
43
|
+
allErrors.push(...validateServicesRelationships(store, ctx));
|
|
44
|
+
}
|
|
45
|
+
if (inScope(scopeSet, 'issue') && ctx) {
|
|
46
|
+
allErrors.push(...validateIssues(store, ctx));
|
|
47
|
+
}
|
|
48
|
+
return allErrors;
|
|
49
|
+
}
|
|
50
|
+
export { buildContext } from './buildContext.js';
|
|
51
|
+
export { validateIssue, validateIssues } from './issue.js';
|
|
52
|
+
export { validateLandmarks } from './landmark.js';
|
|
53
|
+
export { validateLines, validateLinesRelationships, } from './line.js';
|
|
54
|
+
export { validateOperators } from './operator.js';
|
|
55
|
+
export { validateServices, validateServicesRelationships, } from './service.js';
|
|
56
|
+
export { validateStations, validateStationsRelationships, } from './station.js';
|
|
57
|
+
export { validateTowns } from './town.js';
|
|
58
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"/","sources":["validators/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IStore } from '../repo/common/store.js';\nimport { buildContext } from './buildContext.js';\nimport { validateIssues } from './issue.js';\nimport { validateLandmarks } from './landmark.js';\nimport { validateLines, validateLinesRelationships } from './line.js';\nimport { validateOperators } from './operator.js';\nimport { validateServices, validateServicesRelationships } from './service.js';\nimport { validateStations, validateStationsRelationships } from './station.js';\nimport { validateTowns } from './town.js';\nimport type { ValidationError } from './types.js';\n\nexport type ValidationScope =\n | 'town'\n | 'landmark'\n | 'operator'\n | 'station'\n | 'line'\n | 'service'\n | 'issue';\n\nexport interface ValidateOptions {\n /** When set, only run validators for these entity types. */\n scope?: ValidationScope[];\n}\n\nconst SCOPE_NEEDS_CONTEXT: ValidationScope[] = [\n 'station',\n 'line',\n 'service',\n 'issue',\n];\n\nfunction inScope(\n scope: Set<ValidationScope> | null,\n type: ValidationScope,\n): boolean {\n return !scope || scope.has(type);\n}\n\nexport function validateAll(\n store: IStore,\n options?: ValidateOptions,\n): ValidationError[] {\n const scope = options?.scope;\n const scopeSet = scope && scope.length > 0 ? new Set(scope) : null;\n const needsContext =\n !scopeSet || SCOPE_NEEDS_CONTEXT.some((t) => scopeSet.has(t));\n const ctx = needsContext ? buildContext(store) : null;\n\n const allErrors: ValidationError[] = [];\n\n if (inScope(scopeSet, 'town')) allErrors.push(...validateTowns(store));\n if (inScope(scopeSet, 'landmark'))\n allErrors.push(...validateLandmarks(store));\n if (inScope(scopeSet, 'operator'))\n allErrors.push(...validateOperators(store));\n if (inScope(scopeSet, 'line')) {\n allErrors.push(...validateLines(store));\n if (ctx) allErrors.push(...validateLinesRelationships(store, ctx));\n }\n if (inScope(scopeSet, 'station')) {\n allErrors.push(...validateStations(store));\n if (ctx) allErrors.push(...validateStationsRelationships(store, ctx));\n }\n if (inScope(scopeSet, 'service')) {\n allErrors.push(...validateServices(store));\n if (ctx) allErrors.push(...validateServicesRelationships(store, ctx));\n }\n if (inScope(scopeSet, 'issue') && ctx) {\n allErrors.push(...validateIssues(store, ctx));\n }\n\n return allErrors;\n}\n\nexport { buildContext } from './buildContext.js';\nexport { validateIssue, validateIssues } from './issue.js';\nexport { validateLandmarks } from './landmark.js';\nexport {\n validateLines,\n validateLinesRelationships,\n} from './line.js';\nexport { validateOperators } from './operator.js';\nexport {\n validateServices,\n validateServicesRelationships,\n} from './service.js';\nexport {\n validateStations,\n validateStationsRelationships,\n} from './station.js';\nexport { validateTowns } from './town.js';\nexport type { ValidationContext, ValidationError } from './types.js';\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ImpactEvent } from '#schema/issue/impactEvent.js';
|
|
2
|
+
import type { IStore } from '../repo/common/store.js';
|
|
3
|
+
import type { ValidationContext, ValidationError } from './types.js';
|
|
4
|
+
export declare function validateIssueSchema(data: unknown): ValidationError[];
|
|
5
|
+
export declare function validateEvidenceSchema(data: unknown): ValidationError[];
|
|
6
|
+
export declare function validateImpactEventSchema(data: unknown): ValidationError[];
|
|
7
|
+
export declare function validateImpactEventRelationships(event: ImpactEvent, evidenceIds: Set<string>, file: string, lineNum: number, ctx?: ValidationContext): ValidationError[];
|
|
8
|
+
/**
|
|
9
|
+
* Validates a single issue at the given path (e.g. "issue/2025/03/2025-03-11-x").
|
|
10
|
+
* Pass ctx for relationship validation (serviceIds, stationIds, evidenceIds).
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateIssue(store: IStore, relBase: string, ctx?: ValidationContext): ValidationError[];
|
|
13
|
+
export declare function validateIssues(store: IStore, ctx?: ValidationContext): ValidationError[];
|