@codazen/harmonica-mcp 3.8.6 → 3.9.1
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/index.js +874 -279
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21542,25 +21542,25 @@ async function validateAuth(client, orgIdentifier, isOrgSlug, userId) {
|
|
|
21542
21542
|
}
|
|
21543
21543
|
|
|
21544
21544
|
// ../../libs/harmonica-services/src/mcp/org-guard.ts
|
|
21545
|
-
async function
|
|
21546
|
-
const
|
|
21547
|
-
if (!
|
|
21548
|
-
throw new Error(`
|
|
21545
|
+
async function assertSystemInOrg(client, systemId, orgId) {
|
|
21546
|
+
const system = await client.getSystem(systemId);
|
|
21547
|
+
if (!system) {
|
|
21548
|
+
throw new Error(`System not found: "${systemId}"`);
|
|
21549
21549
|
}
|
|
21550
|
-
if (
|
|
21550
|
+
if (system.orgId !== orgId) {
|
|
21551
21551
|
throw new Error(
|
|
21552
|
-
`
|
|
21552
|
+
`System "${systemId}" does not belong to the configured organization. Access denied.`
|
|
21553
21553
|
);
|
|
21554
21554
|
}
|
|
21555
21555
|
}
|
|
21556
|
-
async function
|
|
21556
|
+
async function assertRevisionInSystem(client, revisionId, systemId) {
|
|
21557
21557
|
const revision = await client.getRevision(revisionId);
|
|
21558
21558
|
if (!revision) {
|
|
21559
21559
|
throw new Error(`Revision not found: "${revisionId}"`);
|
|
21560
21560
|
}
|
|
21561
21561
|
if (revision.systemId !== systemId) {
|
|
21562
21562
|
throw new Error(
|
|
21563
|
-
`Revision "${revisionId}" does not belong to
|
|
21563
|
+
`Revision "${revisionId}" does not belong to system "${systemId}". Access denied.`
|
|
21564
21564
|
);
|
|
21565
21565
|
}
|
|
21566
21566
|
}
|
|
@@ -21575,18 +21575,6 @@ async function assertBeatInOrg(client, beatId, orgId) {
|
|
|
21575
21575
|
);
|
|
21576
21576
|
}
|
|
21577
21577
|
}
|
|
21578
|
-
async function fetchProjectInOrg(client, systemId, orgId) {
|
|
21579
|
-
const project = await client.getSystem(systemId);
|
|
21580
|
-
if (!project) {
|
|
21581
|
-
throw new Error(`Project not found: "${systemId}"`);
|
|
21582
|
-
}
|
|
21583
|
-
if (project.orgId !== orgId) {
|
|
21584
|
-
throw new Error(
|
|
21585
|
-
`Project "${systemId}" does not belong to the configured organization. Access denied.`
|
|
21586
|
-
);
|
|
21587
|
-
}
|
|
21588
|
-
return project;
|
|
21589
|
-
}
|
|
21590
21578
|
async function fetchSystemInOrg(client, systemId, orgId) {
|
|
21591
21579
|
const system = await client.getSystem(systemId);
|
|
21592
21580
|
if (!system) {
|
|
@@ -21979,7 +21967,7 @@ function registerActivityTools(server, ctx, client) {
|
|
|
21979
21967
|
importance: external_exports.enum(IMPORTANCE_VALUES).optional().describe("Filter by minimum importance level")
|
|
21980
21968
|
},
|
|
21981
21969
|
async ({ systemId, limit, importance }) => {
|
|
21982
|
-
await
|
|
21970
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
21983
21971
|
const { entries, hasMore } = await client.listSystemActivities(systemId, {
|
|
21984
21972
|
limit: limit ?? 30,
|
|
21985
21973
|
minImportance: importance
|
|
@@ -22011,7 +21999,7 @@ function registerAnalysisTools(server, ctx, client) {
|
|
|
22011
21999
|
minClusterSize: external_exports.coerce.number().int().min(2).optional().default(3).describe("Minimum cluster size. Default 3")
|
|
22012
22000
|
},
|
|
22013
22001
|
async ({ systemId, entityType, threshold, minClusterSize }) => {
|
|
22014
|
-
await
|
|
22002
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
22015
22003
|
const result = await client.clusterEntities(systemId, entityType, { threshold, minClusterSize });
|
|
22016
22004
|
if (result.clusters.length === 0) {
|
|
22017
22005
|
return {
|
|
@@ -22043,7 +22031,7 @@ Analyzed: ${result.totalAnalyzed} | Clusters: ${result.clusters.length} | Noise:
|
|
|
22043
22031
|
minClusterSize: external_exports.coerce.number().int().min(2).optional().default(2).describe("Minimum cluster size. Default 2")
|
|
22044
22032
|
},
|
|
22045
22033
|
async ({ systemId, analysisType, entityType, threshold, minClusterSize }) => {
|
|
22046
|
-
await
|
|
22034
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
22047
22035
|
const result = await client.runClusteredAnalysis(systemId, analysisType, {
|
|
22048
22036
|
entityType,
|
|
22049
22037
|
threshold,
|
|
@@ -22078,7 +22066,7 @@ function registerAuditIntegrityTools(server, ctx, client) {
|
|
|
22078
22066
|
},
|
|
22079
22067
|
async ({ systemId, limit }) => {
|
|
22080
22068
|
try {
|
|
22081
|
-
await
|
|
22069
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
22082
22070
|
const report = await client.verifyAuditIntegrity(systemId, { limit });
|
|
22083
22071
|
const lines = [
|
|
22084
22072
|
`## Audit Integrity Report`,
|
|
@@ -22207,7 +22195,7 @@ function registerBaselineTools(server, ctx, client) {
|
|
|
22207
22195
|
},
|
|
22208
22196
|
async ({ systemId, beatIds, runChecks, localPath }) => {
|
|
22209
22197
|
try {
|
|
22210
|
-
await
|
|
22198
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
22211
22199
|
const task = await client.baselineSystem(systemId, { beatIds, runChecks, localPath });
|
|
22212
22200
|
return {
|
|
22213
22201
|
content: [{
|
|
@@ -22296,7 +22284,7 @@ Rationale: ${rationale}`;
|
|
|
22296
22284
|
},
|
|
22297
22285
|
async ({ systemId, items, reason }) => {
|
|
22298
22286
|
try {
|
|
22299
|
-
await
|
|
22287
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
22300
22288
|
const result = await client.bulkReassignRevisions(items, reason, ctx.user.userId);
|
|
22301
22289
|
const lines = [
|
|
22302
22290
|
`# Bulk reassignment complete: ${result.reassigned.length} succeeded, ${result.failed.length} failed`,
|
|
@@ -22335,7 +22323,7 @@ Rationale: ${rationale}`;
|
|
|
22335
22323
|
},
|
|
22336
22324
|
async ({ systemId, revisionId, toBeatId, reason }) => {
|
|
22337
22325
|
try {
|
|
22338
|
-
await
|
|
22326
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
22339
22327
|
const result = await client.reassignRevision(revisionId, toBeatId, reason, ctx.user.userId);
|
|
22340
22328
|
const text = [
|
|
22341
22329
|
`Revision reassigned successfully.`,
|
|
@@ -22853,7 +22841,7 @@ function formatNoteList(notes) {
|
|
|
22853
22841
|
return parts.join("\n");
|
|
22854
22842
|
}
|
|
22855
22843
|
function formatDocumentList(docs, allNotes) {
|
|
22856
|
-
if (docs.length === 0) return "No documents uploaded for this
|
|
22844
|
+
if (docs.length === 0) return "No documents uploaded for this system.";
|
|
22857
22845
|
const lines = docs.map((doc) => {
|
|
22858
22846
|
const meta = doc.documentMeta;
|
|
22859
22847
|
const status = meta?.documentStatus ?? "unknown";
|
|
@@ -23103,7 +23091,7 @@ function registerBeatTools(server, ctx, client) {
|
|
|
23103
23091
|
includeArchived: external_exports.boolean().optional().default(false).describe("Include archived and deprecated beats (excluded by default)")
|
|
23104
23092
|
},
|
|
23105
23093
|
async ({ systemId, offset = 0, limit = 100, includeArchived }) => {
|
|
23106
|
-
await
|
|
23094
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
23107
23095
|
let beats = await client.listSystemBeats(systemId);
|
|
23108
23096
|
if (!includeArchived) {
|
|
23109
23097
|
beats = beats.filter((b) => b.beatStatus !== "archived");
|
|
@@ -23169,7 +23157,7 @@ function registerBeatTools(server, ctx, client) {
|
|
|
23169
23157
|
},
|
|
23170
23158
|
async ({ systemId, title, description, tags, priority, estimatedEffort, beatId, humanAssignee, agentAssignee, branch }) => {
|
|
23171
23159
|
try {
|
|
23172
|
-
await
|
|
23160
|
+
await fetchSystemInOrg(client, systemId, ctx.orgId);
|
|
23173
23161
|
const resolvedBeatId = beatId ?? await client.getNextBeatId(systemId);
|
|
23174
23162
|
const beat = await client.createBeat({
|
|
23175
23163
|
beatId: resolvedBeatId,
|
|
@@ -23286,7 +23274,7 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
|
|
|
23286
23274
|
},
|
|
23287
23275
|
async ({ systemId, beatId, status, includeArchived }) => {
|
|
23288
23276
|
try {
|
|
23289
|
-
await
|
|
23277
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
23290
23278
|
const revisions = beatId ? await client.listBeatRevisions(systemId, beatId, { status, includeArchived }) : await client.listSystemRevisions(systemId, { status, includeArchived });
|
|
23291
23279
|
return { content: [{ type: "text", text: formatRevisionSummaryTable(revisions) }] };
|
|
23292
23280
|
} catch (err) {
|
|
@@ -23927,7 +23915,7 @@ function registerCheckTools(server, ctx, client) {
|
|
|
23927
23915
|
} else {
|
|
23928
23916
|
const result = await client.listSystemChecks(systemId, checkType, { limit: 200 });
|
|
23929
23917
|
checks = result.checks;
|
|
23930
|
-
scope = `
|
|
23918
|
+
scope = `system ${systemId}`;
|
|
23931
23919
|
}
|
|
23932
23920
|
if (checks.length === 0) {
|
|
23933
23921
|
return { content: [{ type: "text", text: `No checks found for ${scope}.` }] };
|
|
@@ -23950,9 +23938,9 @@ function registerCheckTools(server, ctx, client) {
|
|
|
23950
23938
|
try {
|
|
23951
23939
|
const checks = await client.listLatestSystemChecks(systemId);
|
|
23952
23940
|
if (checks.length === 0) {
|
|
23953
|
-
return { content: [{ type: "text", text: `No checks found for
|
|
23941
|
+
return { content: [{ type: "text", text: `No checks found for system ${systemId}.` }] };
|
|
23954
23942
|
}
|
|
23955
|
-
const text = formatCheckList(checks, `latest per target \xB7
|
|
23943
|
+
const text = formatCheckList(checks, `latest per target \xB7 system ${systemId}`);
|
|
23956
23944
|
return { content: [{ type: "text", text }] };
|
|
23957
23945
|
} catch (err) {
|
|
23958
23946
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -23972,7 +23960,7 @@ function registerCheckTools(server, ctx, client) {
|
|
|
23972
23960
|
},
|
|
23973
23961
|
async ({ checkType, targetId, systemId, branch, localPath }) => {
|
|
23974
23962
|
try {
|
|
23975
|
-
await
|
|
23963
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
23976
23964
|
const targetError = checkType === "build_quality" ? buildQualityTargetError(targetId) : checkType === "beat_version_quality" || checkType === "plan_quality" ? beatVersionQualityTargetError(targetId) : null;
|
|
23977
23965
|
if (targetError) {
|
|
23978
23966
|
return { content: [{ type: "text", text: targetError }], isError: true };
|
|
@@ -24560,7 +24548,7 @@ function registerDropQualityTools(server, ctx, client) {
|
|
|
24560
24548
|
},
|
|
24561
24549
|
async ({ dropId, systemId, wait }) => {
|
|
24562
24550
|
try {
|
|
24563
|
-
await
|
|
24551
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
24564
24552
|
const drop = await client.getDrop(dropId);
|
|
24565
24553
|
if (!drop) {
|
|
24566
24554
|
return { content: [{ type: "text", text: `Drop "${dropId}" not found.` }], isError: true };
|
|
@@ -24690,7 +24678,7 @@ function registerDropTools(server, ctx, client) {
|
|
|
24690
24678
|
);
|
|
24691
24679
|
server.tool(
|
|
24692
24680
|
"create_drop",
|
|
24693
|
-
"Create a Drop (release container) in an Account. Drops group Beat Revisions for coordinated release. The dropCode is auto-generated as D-NNN per Account. Drops are cross-
|
|
24681
|
+
"Create a Drop (release container) in an Account. Drops group Beat Revisions for coordinated release. The dropCode is auto-generated as D-NNN per Account. Drops are cross-system: a single Drop can include Revisions from any System in the Account.",
|
|
24694
24682
|
{
|
|
24695
24683
|
accountId: external_exports.string().describe("The account ID"),
|
|
24696
24684
|
name: external_exports.string().min(1).max(200).describe('Drop name (e.g. "MVP", "Phase 2", "Hotfix 2026-Q2")'),
|
|
@@ -24954,8 +24942,8 @@ function registerEmbeddingTools(server, ctx, client) {
|
|
|
24954
24942
|
systemId: external_exports.string().describe("The system ID to embed entities for")
|
|
24955
24943
|
},
|
|
24956
24944
|
async ({ systemId }) => {
|
|
24957
|
-
await
|
|
24958
|
-
const result = await client.
|
|
24945
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
24946
|
+
const result = await client.embedSystemEntities(systemId);
|
|
24959
24947
|
const text = `## Embedding Results
|
|
24960
24948
|
|
|
24961
24949
|
- Embedded: ${result.embedded}
|
|
@@ -24986,7 +24974,7 @@ function registerEmbeddingTools(server, ctx, client) {
|
|
|
24986
24974
|
}
|
|
24987
24975
|
let scope;
|
|
24988
24976
|
if (systemId) {
|
|
24989
|
-
await
|
|
24977
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
24990
24978
|
scope = { systemId };
|
|
24991
24979
|
} else if (teamspaceId) {
|
|
24992
24980
|
await assertTeamspaceInOrg(client, teamspaceId, ctx.orgId);
|
|
@@ -25012,7 +25000,7 @@ function registerEmbeddingTools(server, ctx, client) {
|
|
|
25012
25000
|
beatId: external_exports.string().optional().describe("Restrict results to notes belonging to a specific beat (DynamoDB post-filter)")
|
|
25013
25001
|
},
|
|
25014
25002
|
async ({ noteId, systemId, threshold, limit, includeArchived = false, noteStatus, beatId }) => {
|
|
25015
|
-
await
|
|
25003
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25016
25004
|
const ids = Array.isArray(noteId) ? noteId : [noteId];
|
|
25017
25005
|
if (ids.length === 1) {
|
|
25018
25006
|
const results = await client.findSimilarNotes(ids[0], systemId, { threshold, limit, includeArchived, noteStatus, beatId });
|
|
@@ -25053,7 +25041,7 @@ function registerEmbeddingTools(server, ctx, client) {
|
|
|
25053
25041
|
limit: external_exports.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results to return. Default 10")
|
|
25054
25042
|
},
|
|
25055
25043
|
async ({ noteId, systemId, threshold, limit }) => {
|
|
25056
|
-
await
|
|
25044
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25057
25045
|
const results = await client.findRelatedBeats(noteId, systemId, { threshold, limit });
|
|
25058
25046
|
const text = formatSimilarityResults(results, noteId, "Related Beats");
|
|
25059
25047
|
return { content: [{ type: "text", text }] };
|
|
@@ -25069,7 +25057,7 @@ function registerEmbeddingTools(server, ctx, client) {
|
|
|
25069
25057
|
includeLinked: external_exports.coerce.boolean().optional().default(false).describe("Include notes that already have dependsOnNotes links. Default false (excludes them). Set true for thematic grouping pass.")
|
|
25070
25058
|
},
|
|
25071
25059
|
async ({ systemId, threshold, minClusterSize, includeLinked }) => {
|
|
25072
|
-
await
|
|
25060
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25073
25061
|
const result = await client.clusterNotes(systemId, { threshold, minClusterSize, excludeAlreadyLinked: !includeLinked });
|
|
25074
25062
|
if (result.clusters.length === 0) {
|
|
25075
25063
|
return { content: [{ type: "text", text: `No clusters found among ${result.totalAnalyzed} analyzed notes. Try lowering the threshold.` }] };
|
|
@@ -25100,7 +25088,7 @@ ${members}`;
|
|
|
25100
25088
|
includeLinked: external_exports.coerce.boolean().optional().default(false).describe("Include notes that already have dependsOnNotes links. Default false.")
|
|
25101
25089
|
},
|
|
25102
25090
|
async ({ systemId, threshold, minClusterSize, includeLinked }) => {
|
|
25103
|
-
await
|
|
25091
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25104
25092
|
const result = await client.consolidateNotes(systemId, { threshold, minClusterSize, includeLinked });
|
|
25105
25093
|
if (result.proposals.length === 0) {
|
|
25106
25094
|
return { content: [{ type: "text", text: `No consolidation candidates found among ${result.totalAnalyzed} analyzed notes.` }] };
|
|
@@ -25341,7 +25329,7 @@ function registerLayerTools(server, ctx, client) {
|
|
|
25341
25329
|
},
|
|
25342
25330
|
async ({ systemId }) => {
|
|
25343
25331
|
try {
|
|
25344
|
-
await
|
|
25332
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25345
25333
|
} catch (err) {
|
|
25346
25334
|
const message = err instanceof Error ? err.message : String(err);
|
|
25347
25335
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -25366,7 +25354,7 @@ function registerLayerTools(server, ctx, client) {
|
|
|
25366
25354
|
},
|
|
25367
25355
|
async ({ systemId, layerId }) => {
|
|
25368
25356
|
try {
|
|
25369
|
-
await
|
|
25357
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25370
25358
|
} catch (err) {
|
|
25371
25359
|
const message = err instanceof Error ? err.message : String(err);
|
|
25372
25360
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -25401,7 +25389,7 @@ function registerLayerTools(server, ctx, client) {
|
|
|
25401
25389
|
},
|
|
25402
25390
|
async ({ systemId, layerKey, name, description, parentLayerId, repoPatterns }) => {
|
|
25403
25391
|
try {
|
|
25404
|
-
await
|
|
25392
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25405
25393
|
} catch (err) {
|
|
25406
25394
|
const message = err instanceof Error ? err.message : String(err);
|
|
25407
25395
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -25440,7 +25428,7 @@ ${formatLayerDetail(layer)}` }] };
|
|
|
25440
25428
|
},
|
|
25441
25429
|
async ({ systemId, layerId, name, description, repoPatterns }) => {
|
|
25442
25430
|
try {
|
|
25443
|
-
await
|
|
25431
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25444
25432
|
} catch (err) {
|
|
25445
25433
|
const message = err instanceof Error ? err.message : String(err);
|
|
25446
25434
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -25484,7 +25472,7 @@ ${formatLayerDetail(layer)}` }] };
|
|
|
25484
25472
|
},
|
|
25485
25473
|
async ({ systemId, layerId, status, reason }) => {
|
|
25486
25474
|
try {
|
|
25487
|
-
await
|
|
25475
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25488
25476
|
} catch (err) {
|
|
25489
25477
|
const message = err instanceof Error ? err.message : String(err);
|
|
25490
25478
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -25828,7 +25816,7 @@ function registerNextActionsTools(server, ctx, client) {
|
|
|
25828
25816
|
},
|
|
25829
25817
|
async ({ systemId, limit }) => {
|
|
25830
25818
|
try {
|
|
25831
|
-
await
|
|
25819
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25832
25820
|
const actions = await client.getNextActions(systemId, { limit });
|
|
25833
25821
|
return { content: [{ type: "text", text: formatNextActions(actions) }] };
|
|
25834
25822
|
} catch (err) {
|
|
@@ -25845,7 +25833,7 @@ function registerNextActionsTools(server, ctx, client) {
|
|
|
25845
25833
|
},
|
|
25846
25834
|
async ({ systemId }) => {
|
|
25847
25835
|
try {
|
|
25848
|
-
await
|
|
25836
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25849
25837
|
const actions = await client.listTriageActions(systemId);
|
|
25850
25838
|
return { content: [{ type: "text", text: formatTriageActions(actions) }] };
|
|
25851
25839
|
} catch (err) {
|
|
@@ -25863,7 +25851,7 @@ function registerNextActionsTools(server, ctx, client) {
|
|
|
25863
25851
|
},
|
|
25864
25852
|
async ({ systemId, actionKey }) => {
|
|
25865
25853
|
try {
|
|
25866
|
-
await
|
|
25854
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
25867
25855
|
await client.dismissTriageAction(systemId, actionKey, ctx.user?.userId ?? "mcp");
|
|
25868
25856
|
return { content: [{ type: "text", text: `Action ${actionKey} dismissed.` }] };
|
|
25869
25857
|
} catch (err) {
|
|
@@ -26336,18 +26324,24 @@ var NoteStatusSchema = external_exports.enum([
|
|
|
26336
26324
|
]);
|
|
26337
26325
|
var SystemStatusSchema = external_exports.enum(["active", "archived", "draft"]);
|
|
26338
26326
|
var ChangeImportanceSchema = external_exports.enum(["minor", "moderate", "major"]);
|
|
26327
|
+
var normalizeSystemId = (raw) => {
|
|
26328
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return raw;
|
|
26329
|
+
const b = raw;
|
|
26330
|
+
const id = b["systemId"] || b["projectId"];
|
|
26331
|
+
if (!id) return raw;
|
|
26332
|
+
return { ...b, systemId: id };
|
|
26333
|
+
};
|
|
26339
26334
|
var BeatApiSchema = external_exports.preprocess(
|
|
26340
26335
|
(raw) => {
|
|
26341
26336
|
if (typeof raw !== "object" || raw === null) return raw;
|
|
26342
26337
|
const b = raw;
|
|
26343
|
-
const id = b["systemId"]
|
|
26338
|
+
const id = b["systemId"] || b["projectId"];
|
|
26344
26339
|
if (!id) return raw;
|
|
26345
|
-
return { ...b, systemId: id
|
|
26340
|
+
return { ...b, systemId: id };
|
|
26346
26341
|
},
|
|
26347
26342
|
external_exports.object({
|
|
26348
26343
|
beatId: external_exports.string(),
|
|
26349
26344
|
systemId: external_exports.string(),
|
|
26350
|
-
projectId: external_exports.string(),
|
|
26351
26345
|
/** Source project's title; attached by the flat /api/beats endpoint for
|
|
26352
26346
|
* multi-project (teamspace/org) responses. */
|
|
26353
26347
|
projectName: external_exports.string().nullish(),
|
|
@@ -26373,35 +26367,38 @@ var RevisionScmSchema = external_exports.object({
|
|
|
26373
26367
|
state: external_exports.enum(["draft", "open", "merged", "closed"]),
|
|
26374
26368
|
mergeSha: external_exports.string().optional()
|
|
26375
26369
|
});
|
|
26376
|
-
var RevisionApiSchema = external_exports.
|
|
26377
|
-
|
|
26378
|
-
|
|
26379
|
-
|
|
26380
|
-
|
|
26381
|
-
|
|
26382
|
-
|
|
26383
|
-
|
|
26384
|
-
|
|
26385
|
-
|
|
26386
|
-
|
|
26387
|
-
|
|
26388
|
-
|
|
26389
|
-
|
|
26390
|
-
|
|
26391
|
-
|
|
26392
|
-
|
|
26393
|
-
|
|
26394
|
-
|
|
26395
|
-
|
|
26396
|
-
|
|
26397
|
-
|
|
26398
|
-
|
|
26399
|
-
|
|
26370
|
+
var RevisionApiSchema = external_exports.preprocess(
|
|
26371
|
+
normalizeSystemId,
|
|
26372
|
+
external_exports.object({
|
|
26373
|
+
revisionId: external_exports.string(),
|
|
26374
|
+
beatId: external_exports.string(),
|
|
26375
|
+
systemId: external_exports.string(),
|
|
26376
|
+
status: RevisionStatusSchema,
|
|
26377
|
+
title: external_exports.string().default(""),
|
|
26378
|
+
versionNumber: external_exports.number(),
|
|
26379
|
+
changeSummary: external_exports.string().nullish(),
|
|
26380
|
+
changeImportance: ChangeImportanceSchema.nullish(),
|
|
26381
|
+
primaryUser: external_exports.string(),
|
|
26382
|
+
description: external_exports.string().nullish(),
|
|
26383
|
+
tags: external_exports.array(external_exports.string()).nullish(),
|
|
26384
|
+
priority: external_exports.number().nullish(),
|
|
26385
|
+
estimatedEffort: external_exports.string().nullish(),
|
|
26386
|
+
humanAssignee: HumanAssigneeSchema.nullish(),
|
|
26387
|
+
agentAssignee: AgentAssigneeSchema.nullish(),
|
|
26388
|
+
scm: RevisionScmSchema.optional(),
|
|
26389
|
+
// The Layer this Revision was created under (immutable). Absent on Revisions
|
|
26390
|
+
// predating Layers — never backfilled — so consumers group those as "Unassigned".
|
|
26391
|
+
layerId: external_exports.string().nullish(),
|
|
26392
|
+
createdAt: external_exports.string(),
|
|
26393
|
+
updatedAt: external_exports.string()
|
|
26394
|
+
}).passthrough()
|
|
26395
|
+
);
|
|
26396
|
+
var NoteApiSchema = external_exports.preprocess(normalizeSystemId, external_exports.object({
|
|
26400
26397
|
noteId: external_exports.string(),
|
|
26401
26398
|
noteType: NoteTypeSchema,
|
|
26402
26399
|
status: NoteStatusSchema,
|
|
26403
26400
|
content: external_exports.string(),
|
|
26404
|
-
|
|
26401
|
+
systemId: external_exports.string().nullish(),
|
|
26405
26402
|
beatId: external_exports.string().nullish(),
|
|
26406
26403
|
revisionId: external_exports.string().nullish(),
|
|
26407
26404
|
rationale: external_exports.string().nullish(),
|
|
@@ -26412,18 +26409,21 @@ var NoteApiSchema = external_exports.object({
|
|
|
26412
26409
|
agentAssignee: AgentAssigneeSchema.nullish(),
|
|
26413
26410
|
createdAt: external_exports.string(),
|
|
26414
26411
|
updatedAt: external_exports.string()
|
|
26415
|
-
}).passthrough();
|
|
26416
|
-
var SystemApiSchema = external_exports.
|
|
26417
|
-
|
|
26418
|
-
|
|
26419
|
-
|
|
26420
|
-
|
|
26421
|
-
|
|
26422
|
-
|
|
26423
|
-
|
|
26424
|
-
|
|
26425
|
-
|
|
26426
|
-
|
|
26412
|
+
}).passthrough());
|
|
26413
|
+
var SystemApiSchema = external_exports.preprocess(
|
|
26414
|
+
normalizeSystemId,
|
|
26415
|
+
external_exports.object({
|
|
26416
|
+
systemId: external_exports.string(),
|
|
26417
|
+
title: external_exports.string(),
|
|
26418
|
+
status: SystemStatusSchema,
|
|
26419
|
+
description: external_exports.string().optional(),
|
|
26420
|
+
strategy: external_exports.string().optional(),
|
|
26421
|
+
orgId: external_exports.string().optional(),
|
|
26422
|
+
ownerUserId: external_exports.string().optional(),
|
|
26423
|
+
createdAt: external_exports.string(),
|
|
26424
|
+
updatedAt: external_exports.string()
|
|
26425
|
+
}).passthrough()
|
|
26426
|
+
);
|
|
26427
26427
|
var BeatsResponseSchema = external_exports.object({
|
|
26428
26428
|
beats: external_exports.array(BeatApiSchema),
|
|
26429
26429
|
total: external_exports.number().optional(),
|
|
@@ -26608,37 +26608,43 @@ var CHECK_TARGET_TYPES = [
|
|
|
26608
26608
|
"organization"
|
|
26609
26609
|
];
|
|
26610
26610
|
var CheckTargetTypeSchema = external_exports.enum(CHECK_TARGET_TYPES);
|
|
26611
|
-
var CheckApiSchema = external_exports.
|
|
26612
|
-
|
|
26613
|
-
|
|
26614
|
-
|
|
26615
|
-
|
|
26616
|
-
|
|
26617
|
-
|
|
26618
|
-
|
|
26619
|
-
|
|
26620
|
-
|
|
26621
|
-
|
|
26622
|
-
|
|
26623
|
-
|
|
26624
|
-
|
|
26625
|
-
|
|
26626
|
-
|
|
26627
|
-
|
|
26628
|
-
|
|
26629
|
-
|
|
26630
|
-
|
|
26631
|
-
|
|
26632
|
-
|
|
26633
|
-
|
|
26634
|
-
|
|
26635
|
-
|
|
26636
|
-
|
|
26637
|
-
|
|
26638
|
-
|
|
26639
|
-
|
|
26640
|
-
|
|
26641
|
-
|
|
26611
|
+
var CheckApiSchema = external_exports.preprocess(
|
|
26612
|
+
normalizeSystemId,
|
|
26613
|
+
external_exports.object({
|
|
26614
|
+
checkId: external_exports.string(),
|
|
26615
|
+
checkType: CheckTypeSchema,
|
|
26616
|
+
targetType: CheckTargetTypeSchema,
|
|
26617
|
+
targetId: external_exports.string(),
|
|
26618
|
+
systemId: external_exports.string(),
|
|
26619
|
+
overallScore: external_exports.number(),
|
|
26620
|
+
dimensions: external_exports.array(CheckDimensionScoreSchema),
|
|
26621
|
+
summary: external_exports.string(),
|
|
26622
|
+
topSuggestion: external_exports.string().optional(),
|
|
26623
|
+
gaps: external_exports.array(CheckGapSchema).optional(),
|
|
26624
|
+
taskId: external_exports.string().optional(),
|
|
26625
|
+
triggeredBy: external_exports.string().optional(),
|
|
26626
|
+
createdAt: external_exports.string(),
|
|
26627
|
+
updatedAt: external_exports.string()
|
|
26628
|
+
}).passthrough()
|
|
26629
|
+
);
|
|
26630
|
+
var SystemCheckApiSchema = external_exports.preprocess(
|
|
26631
|
+
normalizeSystemId,
|
|
26632
|
+
external_exports.object({
|
|
26633
|
+
checkId: external_exports.string(),
|
|
26634
|
+
checkType: CheckTypeSchema,
|
|
26635
|
+
targetType: CheckTargetTypeSchema,
|
|
26636
|
+
targetId: external_exports.string(),
|
|
26637
|
+
systemId: external_exports.string(),
|
|
26638
|
+
overallScore: external_exports.number(),
|
|
26639
|
+
summary: external_exports.string(),
|
|
26640
|
+
triggeredBy: external_exports.string().optional(),
|
|
26641
|
+
createdAt: external_exports.string(),
|
|
26642
|
+
updatedAt: external_exports.string(),
|
|
26643
|
+
/** Populated only when the API was called with ?includeDimensions=true.
|
|
26644
|
+
* Read-time enrichment from the underlying Check entity. */
|
|
26645
|
+
dimensions: external_exports.array(CheckDimensionScoreSchema).optional()
|
|
26646
|
+
}).passthrough()
|
|
26647
|
+
);
|
|
26642
26648
|
var CheckResponseSchema = external_exports.object({ check: CheckApiSchema }).passthrough();
|
|
26643
26649
|
var ChecksResponseSchema = external_exports.object({ checks: external_exports.array(SystemCheckApiSchema), nextCursor: external_exports.string().optional() }).passthrough();
|
|
26644
26650
|
var TaskStatusSchema2 = external_exports.enum(["pending", "running", "completed", "failed"]);
|
|
@@ -26683,21 +26689,24 @@ var OrgMemberSchema = external_exports.object({
|
|
|
26683
26689
|
var OrganizationsResponseSchema = external_exports.array(OrganizationSummarySchema);
|
|
26684
26690
|
var OrgMembersResponseSchema = external_exports.array(OrgMemberSchema);
|
|
26685
26691
|
var WrappedOrgMembersResponseSchema = external_exports.object({ members: OrgMembersResponseSchema });
|
|
26686
|
-
var ActivitySchema = external_exports.
|
|
26687
|
-
|
|
26688
|
-
|
|
26689
|
-
|
|
26690
|
-
|
|
26691
|
-
|
|
26692
|
-
|
|
26693
|
-
|
|
26694
|
-
|
|
26695
|
-
|
|
26696
|
-
|
|
26697
|
-
|
|
26698
|
-
|
|
26699
|
-
|
|
26700
|
-
|
|
26692
|
+
var ActivitySchema = external_exports.preprocess(
|
|
26693
|
+
normalizeSystemId,
|
|
26694
|
+
external_exports.object({
|
|
26695
|
+
activityId: external_exports.string(),
|
|
26696
|
+
systemId: external_exports.string(),
|
|
26697
|
+
action: external_exports.string(),
|
|
26698
|
+
importance: external_exports.enum(["minor", "moderate", "major"]),
|
|
26699
|
+
reason: external_exports.string(),
|
|
26700
|
+
// actorType is typed as string (not a strict enum) because the API accepts values
|
|
26701
|
+
// beyond 'user'/'system' (e.g. 'human', 'agent') depending on the call site.
|
|
26702
|
+
actorType: external_exports.string().optional(),
|
|
26703
|
+
actorId: external_exports.string().optional(),
|
|
26704
|
+
affectedBeatIds: external_exports.array(external_exports.string()).optional(),
|
|
26705
|
+
affectedBeatTitles: external_exports.array(external_exports.string()).optional(),
|
|
26706
|
+
metadata: external_exports.record(external_exports.unknown()).optional(),
|
|
26707
|
+
createdAt: external_exports.string()
|
|
26708
|
+
}).passthrough()
|
|
26709
|
+
);
|
|
26701
26710
|
var ActivitiesResponseSchema = external_exports.object({
|
|
26702
26711
|
activities: external_exports.array(ActivitySchema),
|
|
26703
26712
|
hasMore: external_exports.boolean(),
|
|
@@ -26725,26 +26734,29 @@ var NotificationsResponseSchema = external_exports.object({
|
|
|
26725
26734
|
nextCursor: external_exports.string().optional()
|
|
26726
26735
|
}).passthrough();
|
|
26727
26736
|
var UnreadCountResponseSchema = external_exports.object({ count: external_exports.number() }).passthrough();
|
|
26728
|
-
var TriageActionSchema = external_exports.
|
|
26729
|
-
|
|
26730
|
-
|
|
26731
|
-
|
|
26732
|
-
|
|
26733
|
-
|
|
26734
|
-
|
|
26735
|
-
|
|
26736
|
-
|
|
26737
|
-
|
|
26738
|
-
|
|
26739
|
-
|
|
26740
|
-
|
|
26741
|
-
|
|
26742
|
-
|
|
26743
|
-
|
|
26744
|
-
|
|
26745
|
-
|
|
26746
|
-
|
|
26747
|
-
|
|
26737
|
+
var TriageActionSchema = external_exports.preprocess(
|
|
26738
|
+
normalizeSystemId,
|
|
26739
|
+
external_exports.object({
|
|
26740
|
+
actionKey: external_exports.string(),
|
|
26741
|
+
systemId: external_exports.string(),
|
|
26742
|
+
entityId: external_exports.string().optional(),
|
|
26743
|
+
entityType: external_exports.enum(["beat", "revision", "note"]).optional(),
|
|
26744
|
+
beatId: external_exports.string().optional(),
|
|
26745
|
+
category: external_exports.string(),
|
|
26746
|
+
priority: external_exports.string(),
|
|
26747
|
+
beatStatus: external_exports.string().optional(),
|
|
26748
|
+
title: external_exports.string(),
|
|
26749
|
+
description: external_exports.string(),
|
|
26750
|
+
status: external_exports.enum(["open", "acted", "dismissed", "resolved"]),
|
|
26751
|
+
decision: external_exports.enum(["auto-execute", "escalate", "skip"]).optional(),
|
|
26752
|
+
taskId: external_exports.string().optional(),
|
|
26753
|
+
taskStatus: external_exports.string().optional(),
|
|
26754
|
+
firstSeenAt: external_exports.string(),
|
|
26755
|
+
lastSeenAt: external_exports.string(),
|
|
26756
|
+
dismissedBy: external_exports.string().optional(),
|
|
26757
|
+
dismissedAt: external_exports.string().optional()
|
|
26758
|
+
}).passthrough()
|
|
26759
|
+
);
|
|
26748
26760
|
var TriageActionsResponseSchema = external_exports.object({
|
|
26749
26761
|
actions: external_exports.array(TriageActionSchema)
|
|
26750
26762
|
}).passthrough();
|
|
@@ -26761,15 +26773,15 @@ var NextActionSchema = external_exports.object({
|
|
|
26761
26773
|
var NextActionsResponseSchema = external_exports.object({
|
|
26762
26774
|
actions: external_exports.array(NextActionSchema)
|
|
26763
26775
|
}).passthrough();
|
|
26764
|
-
var SearchResultSchema = external_exports.object({
|
|
26776
|
+
var SearchResultSchema = external_exports.preprocess(normalizeSystemId, external_exports.object({
|
|
26765
26777
|
entityId: external_exports.string(),
|
|
26766
|
-
entityType: external_exports.enum(["beat", "note", "revision", "beatVersion", "drop", "project"]),
|
|
26778
|
+
entityType: external_exports.enum(["beat", "note", "revision", "beatVersion", "drop", "project", "system"]),
|
|
26767
26779
|
score: external_exports.number(),
|
|
26768
26780
|
snippet: external_exports.string(),
|
|
26769
|
-
|
|
26781
|
+
systemId: external_exports.string().optional(),
|
|
26770
26782
|
projectName: external_exports.string().optional(),
|
|
26771
26783
|
teamspaceId: external_exports.string().optional()
|
|
26772
|
-
}).passthrough();
|
|
26784
|
+
}).passthrough());
|
|
26773
26785
|
var SearchScopeSchema = external_exports.object({
|
|
26774
26786
|
kind: external_exports.enum(["project", "teamspace", "org"]),
|
|
26775
26787
|
id: external_exports.string()
|
|
@@ -26809,31 +26821,40 @@ var LandscapeResponseSchema = external_exports.object({
|
|
|
26809
26821
|
points: external_exports.array(LandscapePointSchema),
|
|
26810
26822
|
links: external_exports.array(external_exports.object({ from: external_exports.string(), to: external_exports.string() }).passthrough()),
|
|
26811
26823
|
clusters: external_exports.array(LandscapeClusterSchema),
|
|
26812
|
-
scopeBoundary: external_exports.
|
|
26813
|
-
|
|
26814
|
-
|
|
26815
|
-
|
|
26816
|
-
|
|
26817
|
-
|
|
26818
|
-
|
|
26824
|
+
scopeBoundary: external_exports.preprocess(
|
|
26825
|
+
(raw) => {
|
|
26826
|
+
if (typeof raw !== "object" || raw === null) return raw;
|
|
26827
|
+
const o = raw;
|
|
26828
|
+
const id = o["systemId"] ?? o["projectId"];
|
|
26829
|
+
if (!id) return raw;
|
|
26830
|
+
return { ...o, systemId: id };
|
|
26831
|
+
},
|
|
26832
|
+
external_exports.object({
|
|
26833
|
+
systemId: external_exports.string(),
|
|
26834
|
+
cx: external_exports.number(),
|
|
26835
|
+
cy: external_exports.number(),
|
|
26836
|
+
radius: external_exports.number(),
|
|
26837
|
+
label: external_exports.string()
|
|
26838
|
+
}).passthrough()
|
|
26839
|
+
).optional(),
|
|
26819
26840
|
meta: LandscapeMetaSchema
|
|
26820
26841
|
}).passthrough();
|
|
26821
26842
|
var ClusterDescriptionSchema = external_exports.object({
|
|
26822
26843
|
title: external_exports.string(),
|
|
26823
26844
|
description: external_exports.string()
|
|
26824
26845
|
}).passthrough();
|
|
26825
|
-
var BeatVersionItemSchema = external_exports.object({
|
|
26846
|
+
var BeatVersionItemSchema = external_exports.preprocess(normalizeSystemId, external_exports.object({
|
|
26826
26847
|
beatVersionId: external_exports.string(),
|
|
26827
26848
|
beatId: external_exports.string(),
|
|
26828
|
-
|
|
26849
|
+
systemId: external_exports.string().optional(),
|
|
26829
26850
|
status: external_exports.string()
|
|
26830
|
-
}).passthrough();
|
|
26851
|
+
}).passthrough());
|
|
26831
26852
|
var BeatVersionsResponseSchema = external_exports.object({
|
|
26832
26853
|
beatVersions: external_exports.array(BeatVersionItemSchema)
|
|
26833
26854
|
}).passthrough();
|
|
26834
26855
|
var ConversationSummaryBaseSchema = external_exports.object({
|
|
26835
26856
|
conversationId: external_exports.string(),
|
|
26836
|
-
|
|
26857
|
+
systemId: external_exports.string().optional(),
|
|
26837
26858
|
teamspaceId: external_exports.string().optional(),
|
|
26838
26859
|
userId: external_exports.string(),
|
|
26839
26860
|
conversationType: external_exports.string(),
|
|
@@ -26846,7 +26867,7 @@ var ConversationSummaryBaseSchema = external_exports.object({
|
|
|
26846
26867
|
createdAt: external_exports.string(),
|
|
26847
26868
|
updatedAt: external_exports.string()
|
|
26848
26869
|
});
|
|
26849
|
-
var ConversationSummarySchema = ConversationSummaryBaseSchema.passthrough();
|
|
26870
|
+
var ConversationSummarySchema = external_exports.preprocess(normalizeSystemId, ConversationSummaryBaseSchema.passthrough());
|
|
26850
26871
|
var StoredMessageSchema = external_exports.object({
|
|
26851
26872
|
messageId: external_exports.string(),
|
|
26852
26873
|
conversationId: external_exports.string(),
|
|
@@ -26952,6 +26973,13 @@ var WorkItemApiSchema = external_exports.object({
|
|
|
26952
26973
|
costPerValueUnit: external_exports.number().optional(),
|
|
26953
26974
|
/** Optional capability-lineage link to a Beat Version (v2, N-4E09-6574). */
|
|
26954
26975
|
beatVersionId: external_exports.string().optional(),
|
|
26976
|
+
/**
|
|
26977
|
+
* Optional link to the Work Order this item delivers part of (B-331,
|
|
26978
|
+
* N-4E09-7160). Absent means unattached, which is a supported state — a
|
|
26979
|
+
* reader grouping a Track's work by Order must render those as their own
|
|
26980
|
+
* named group, not hide them.
|
|
26981
|
+
*/
|
|
26982
|
+
workOrderId: external_exports.string().min(1).optional(),
|
|
26955
26983
|
createdBy: external_exports.string().optional(),
|
|
26956
26984
|
version: external_exports.number().optional(),
|
|
26957
26985
|
createdAt: external_exports.string(),
|
|
@@ -26963,6 +26991,93 @@ var WorkItemEstimateHintApiSchema = external_exports.object({
|
|
|
26963
26991
|
});
|
|
26964
26992
|
var WorkItemsResponseSchema = external_exports.object({ workItems: external_exports.array(WorkItemApiSchema) }).passthrough();
|
|
26965
26993
|
var WorkItemResponseSchema = external_exports.object({ workItem: WorkItemApiSchema, estimateHint: WorkItemEstimateHintApiSchema.optional() }).passthrough();
|
|
26994
|
+
var WORK_INTAKE_STATUSES = [
|
|
26995
|
+
"requested",
|
|
26996
|
+
"needs_decision",
|
|
26997
|
+
"accepted",
|
|
26998
|
+
"done"
|
|
26999
|
+
];
|
|
27000
|
+
var WorkIntakeStatusApiSchema = external_exports.enum(WORK_INTAKE_STATUSES);
|
|
27001
|
+
var NEEDS_DECISION_REASONS = [
|
|
27002
|
+
"out_of_scope",
|
|
27003
|
+
"exceeds_capacity",
|
|
27004
|
+
"incoherent_with_decision"
|
|
27005
|
+
];
|
|
27006
|
+
var NeedsDecisionReasonApiSchema = external_exports.enum(NEEDS_DECISION_REASONS);
|
|
27007
|
+
var WORK_ORDER_SIZES = [
|
|
27008
|
+
"XS",
|
|
27009
|
+
"S",
|
|
27010
|
+
"M",
|
|
27011
|
+
"L",
|
|
27012
|
+
"XL"
|
|
27013
|
+
];
|
|
27014
|
+
var WorkOrderSizeApiSchema = external_exports.enum(WORK_ORDER_SIZES);
|
|
27015
|
+
var WORK_REQUEST_ORIGINS = [
|
|
27016
|
+
"client",
|
|
27017
|
+
"internal"
|
|
27018
|
+
];
|
|
27019
|
+
var WorkRequestOriginApiSchema = external_exports.enum(WORK_REQUEST_ORIGINS);
|
|
27020
|
+
var WorkRequestApiSchema = external_exports.object({
|
|
27021
|
+
workRequestId: external_exports.string(),
|
|
27022
|
+
movementId: external_exports.string(),
|
|
27023
|
+
teamspaceId: external_exports.string(),
|
|
27024
|
+
orgId: external_exports.string(),
|
|
27025
|
+
title: external_exports.string(),
|
|
27026
|
+
/** The ask as written, in the requester's own language. */
|
|
27027
|
+
body: external_exports.string().optional(),
|
|
27028
|
+
requester: HumanAssigneeApiSchema,
|
|
27029
|
+
/** Where the ask entered — one entity covers both, only the entry point differs. */
|
|
27030
|
+
origin: WorkRequestOriginApiSchema,
|
|
27031
|
+
createdBy: external_exports.string().optional(),
|
|
27032
|
+
version: external_exports.number().optional(),
|
|
27033
|
+
createdAt: external_exports.string(),
|
|
27034
|
+
updatedAt: external_exports.string()
|
|
27035
|
+
}).passthrough();
|
|
27036
|
+
var WorkOrderApiSchema = external_exports.object({
|
|
27037
|
+
workOrderId: external_exports.string(),
|
|
27038
|
+
/** Immutable lineage back to the ask this Order was split from. */
|
|
27039
|
+
workRequestId: external_exports.string(),
|
|
27040
|
+
/** Mutable — an Order moves Tracks when an early split guess turns out wrong. */
|
|
27041
|
+
trackId: external_exports.string(),
|
|
27042
|
+
teamspaceId: external_exports.string(),
|
|
27043
|
+
orgId: external_exports.string(),
|
|
27044
|
+
title: external_exports.string(),
|
|
27045
|
+
description: external_exports.string().optional(),
|
|
27046
|
+
status: WorkIntakeStatusApiSchema,
|
|
27047
|
+
/** Present if and only if status is `needs_decision` — the reason IS the routing. */
|
|
27048
|
+
needsDecisionReason: NeedsDecisionReasonApiSchema.optional(),
|
|
27049
|
+
/** Absent when the Order arrived before it was sized, which the composition counts. */
|
|
27050
|
+
size: WorkOrderSizeApiSchema.optional(),
|
|
27051
|
+
createdBy: external_exports.string().optional(),
|
|
27052
|
+
version: external_exports.number().optional(),
|
|
27053
|
+
createdAt: external_exports.string(),
|
|
27054
|
+
updatedAt: external_exports.string()
|
|
27055
|
+
}).passthrough();
|
|
27056
|
+
var WorkRequestSizeEntryApiSchema = external_exports.object({
|
|
27057
|
+
size: WorkOrderSizeApiSchema,
|
|
27058
|
+
count: external_exports.number()
|
|
27059
|
+
});
|
|
27060
|
+
var WorkRequestSizeCompositionApiSchema = external_exports.object({
|
|
27061
|
+
/** Largest first, so a display reads "a large and a medium". */
|
|
27062
|
+
entries: external_exports.array(WorkRequestSizeEntryApiSchema),
|
|
27063
|
+
/** Orders carrying no usable size — counted, never silently dropped. */
|
|
27064
|
+
unsized: external_exports.number(),
|
|
27065
|
+
/** Sized orders only. */
|
|
27066
|
+
totalValueUnits: external_exports.number()
|
|
27067
|
+
});
|
|
27068
|
+
var WorkRequestOrderApiSchema = WorkOrderApiSchema.extend({
|
|
27069
|
+
/** Absent — not null — when the Track no longer resolves. */
|
|
27070
|
+
trackTitle: external_exports.string().optional()
|
|
27071
|
+
}).passthrough();
|
|
27072
|
+
var WorkRequestsResponseSchema = external_exports.object({ workRequests: external_exports.array(WorkRequestApiSchema) }).passthrough();
|
|
27073
|
+
var WorkOrdersResponseSchema = external_exports.object({ workOrders: external_exports.array(WorkOrderApiSchema) }).passthrough();
|
|
27074
|
+
var WorkRequestResponseSchema = external_exports.object({ workRequest: WorkRequestApiSchema }).passthrough();
|
|
27075
|
+
var WorkOrderResponseSchema = external_exports.object({ workOrder: WorkOrderApiSchema }).passthrough();
|
|
27076
|
+
var WorkRequestDetailResponseSchema = external_exports.object({
|
|
27077
|
+
workRequest: WorkRequestApiSchema,
|
|
27078
|
+
orders: external_exports.array(WorkRequestOrderApiSchema),
|
|
27079
|
+
size: WorkRequestSizeCompositionApiSchema
|
|
27080
|
+
}).passthrough();
|
|
26966
27081
|
var AccountStatusSchema = external_exports.enum(["active", "inactive", "archived"]);
|
|
26967
27082
|
var AccountApiSchema = external_exports.object({
|
|
26968
27083
|
accountId: external_exports.string(),
|
|
@@ -26999,14 +27114,13 @@ var LayerApiSchema = external_exports.preprocess(
|
|
|
26999
27114
|
(raw) => {
|
|
27000
27115
|
if (typeof raw !== "object" || raw === null) return raw;
|
|
27001
27116
|
const l = raw;
|
|
27002
|
-
const id = l["systemId"]
|
|
27117
|
+
const id = l["systemId"] || l["projectId"];
|
|
27003
27118
|
if (!id) return raw;
|
|
27004
|
-
return { ...l, systemId: id
|
|
27119
|
+
return { ...l, systemId: id };
|
|
27005
27120
|
},
|
|
27006
27121
|
external_exports.object({
|
|
27007
27122
|
layerId: external_exports.string(),
|
|
27008
27123
|
systemId: external_exports.string(),
|
|
27009
|
-
projectId: external_exports.string(),
|
|
27010
27124
|
layerKey: external_exports.string(),
|
|
27011
27125
|
name: external_exports.string(),
|
|
27012
27126
|
description: external_exports.string().nullish(),
|
|
@@ -27058,11 +27172,11 @@ var DELIVERABLE_GROUP_STATUSES = [
|
|
|
27058
27172
|
"cancelled"
|
|
27059
27173
|
];
|
|
27060
27174
|
var DeliverableGroupStatusSchema = external_exports.enum(DELIVERABLE_GROUP_STATUSES);
|
|
27061
|
-
var DeliverableGroupApiSchema = external_exports.object({
|
|
27175
|
+
var DeliverableGroupApiSchema = external_exports.preprocess(normalizeSystemId, external_exports.object({
|
|
27062
27176
|
deliverableGroupId: external_exports.string(),
|
|
27063
27177
|
groupCode: external_exports.string(),
|
|
27064
27178
|
teamspaceId: external_exports.string(),
|
|
27065
|
-
|
|
27179
|
+
systemId: external_exports.string().nullish(),
|
|
27066
27180
|
movementId: external_exports.string().nullish(),
|
|
27067
27181
|
orgId: external_exports.string(),
|
|
27068
27182
|
title: external_exports.string(),
|
|
@@ -27079,7 +27193,7 @@ var DeliverableGroupApiSchema = external_exports.object({
|
|
|
27079
27193
|
version: external_exports.number().nullish(),
|
|
27080
27194
|
createdAt: external_exports.string(),
|
|
27081
27195
|
updatedAt: external_exports.string()
|
|
27082
|
-
}).passthrough();
|
|
27196
|
+
}).passthrough());
|
|
27083
27197
|
var DeliverableGroupsResponseSchema = external_exports.object({
|
|
27084
27198
|
deliverableGroups: external_exports.array(DeliverableGroupApiSchema)
|
|
27085
27199
|
}).passthrough();
|
|
@@ -27096,27 +27210,33 @@ var MovementDropSummaryApiSchema = external_exports.object({
|
|
|
27096
27210
|
var MovementDropsResponseSchema = external_exports.object({
|
|
27097
27211
|
drops: external_exports.array(MovementDropSummaryApiSchema)
|
|
27098
27212
|
}).passthrough();
|
|
27099
|
-
var WorkPlanBeatVersionApiSchema = external_exports.
|
|
27100
|
-
|
|
27101
|
-
|
|
27102
|
-
|
|
27103
|
-
|
|
27104
|
-
|
|
27105
|
-
|
|
27106
|
-
|
|
27107
|
-
|
|
27108
|
-
|
|
27109
|
-
|
|
27110
|
-
|
|
27111
|
-
|
|
27112
|
-
|
|
27113
|
-
|
|
27213
|
+
var WorkPlanBeatVersionApiSchema = external_exports.preprocess(
|
|
27214
|
+
normalizeSystemId,
|
|
27215
|
+
external_exports.object({
|
|
27216
|
+
beatVersionId: external_exports.string(),
|
|
27217
|
+
beatId: external_exports.string(),
|
|
27218
|
+
systemId: external_exports.string(),
|
|
27219
|
+
versionNumber: external_exports.number(),
|
|
27220
|
+
title: external_exports.string(),
|
|
27221
|
+
status: BeatVersionStatusSchema,
|
|
27222
|
+
// Derived Value Units (VU = Δf × W), read-only. 0 when the BV has no confirmed
|
|
27223
|
+
// value-velocity inputs. Computed server-side from the parent Beat's weight.
|
|
27224
|
+
// `.default(0)` tolerates a rolling deploy where an older API omits the field —
|
|
27225
|
+
// validatedApiFetch would otherwise throw and drop the whole hook into error.
|
|
27226
|
+
valueUnits: external_exports.number().default(0),
|
|
27227
|
+
createdAt: external_exports.string(),
|
|
27228
|
+
updatedAt: external_exports.string()
|
|
27229
|
+
}).passthrough()
|
|
27230
|
+
);
|
|
27114
27231
|
var DeliverableGroupBeatVersionsResponseSchema = external_exports.object({
|
|
27115
27232
|
beatVersions: external_exports.array(WorkPlanBeatVersionApiSchema)
|
|
27116
27233
|
}).passthrough();
|
|
27117
27234
|
var RevisionClaimActorSchema = external_exports.union([
|
|
27118
27235
|
external_exports.object({ kind: external_exports.literal("user"), userId: external_exports.string() }),
|
|
27119
|
-
external_exports.
|
|
27236
|
+
external_exports.preprocess(
|
|
27237
|
+
normalizeSystemId,
|
|
27238
|
+
external_exports.object({ kind: external_exports.literal("agent"), agentId: external_exports.string(), systemId: external_exports.string() })
|
|
27239
|
+
)
|
|
27120
27240
|
]);
|
|
27121
27241
|
var RevisionClaimSchema = external_exports.object({
|
|
27122
27242
|
claimId: external_exports.string(),
|
|
@@ -27127,33 +27247,39 @@ var RevisionClaimSchema = external_exports.object({
|
|
|
27127
27247
|
var RevisionClaimsResponseSchema = external_exports.object({
|
|
27128
27248
|
claims: external_exports.array(RevisionClaimSchema)
|
|
27129
27249
|
}).passthrough();
|
|
27130
|
-
var ProposedRevisionSchema = external_exports.
|
|
27131
|
-
|
|
27132
|
-
|
|
27133
|
-
|
|
27134
|
-
|
|
27135
|
-
|
|
27136
|
-
|
|
27137
|
-
|
|
27138
|
-
|
|
27250
|
+
var ProposedRevisionSchema = external_exports.preprocess(
|
|
27251
|
+
normalizeSystemId,
|
|
27252
|
+
external_exports.object({
|
|
27253
|
+
revisionId: external_exports.string(),
|
|
27254
|
+
title: external_exports.string(),
|
|
27255
|
+
state: external_exports.string(),
|
|
27256
|
+
beatId: external_exports.string(),
|
|
27257
|
+
beatTitle: external_exports.string().optional(),
|
|
27258
|
+
systemId: external_exports.string(),
|
|
27259
|
+
projectTitle: external_exports.string().optional()
|
|
27260
|
+
}).passthrough()
|
|
27261
|
+
);
|
|
27139
27262
|
var ReleaseIntervalSchema = external_exports.object({
|
|
27140
27263
|
averageIntervalDays: external_exports.number(),
|
|
27141
27264
|
sampleSize: external_exports.number(),
|
|
27142
27265
|
isFallback: external_exports.boolean()
|
|
27143
27266
|
}).passthrough();
|
|
27144
|
-
var EligibleBeatVersionForDropSchema = external_exports.
|
|
27145
|
-
|
|
27146
|
-
|
|
27147
|
-
|
|
27148
|
-
|
|
27149
|
-
|
|
27150
|
-
|
|
27151
|
-
|
|
27152
|
-
|
|
27153
|
-
|
|
27154
|
-
|
|
27155
|
-
|
|
27156
|
-
}).
|
|
27267
|
+
var EligibleBeatVersionForDropSchema = external_exports.preprocess(
|
|
27268
|
+
normalizeSystemId,
|
|
27269
|
+
external_exports.object({
|
|
27270
|
+
beatVersionId: external_exports.string(),
|
|
27271
|
+
title: external_exports.string(),
|
|
27272
|
+
state: external_exports.string(),
|
|
27273
|
+
beatId: external_exports.string(),
|
|
27274
|
+
beatTitle: external_exports.string().optional(),
|
|
27275
|
+
systemId: external_exports.string(),
|
|
27276
|
+
projectTitle: external_exports.string().optional(),
|
|
27277
|
+
createdAt: external_exports.string(),
|
|
27278
|
+
updatedAt: external_exports.string(),
|
|
27279
|
+
humanAssignee: external_exports.object({ email: external_exports.string(), name: external_exports.string() }).optional(),
|
|
27280
|
+
currentDropAssignment: external_exports.object({ dropId: external_exports.string(), dropCode: external_exports.string().optional(), state: external_exports.string() }).optional()
|
|
27281
|
+
}).passthrough()
|
|
27282
|
+
);
|
|
27157
27283
|
var ScoredDropCandidateSchema = external_exports.object({
|
|
27158
27284
|
bv: EligibleBeatVersionForDropSchema,
|
|
27159
27285
|
// Score fields are nullable — the ranking engine returns null when there is
|
|
@@ -27393,7 +27519,7 @@ ${formatDeliveryPolicy(
|
|
|
27393
27519
|
function registerNoteTools(server, ctx, client) {
|
|
27394
27520
|
server.tool(
|
|
27395
27521
|
"list_notes",
|
|
27396
|
-
"List Notes for a system. Without beatId: returns all notes (system-level + beat-level). With beatId: returns only notes scoped to that beat. Filterable by type, status, revisionId, beatVersionId, and significance. Use offset/limit to paginate beat-scoped results; use cursor for
|
|
27522
|
+
"List Notes for a system. Without beatId: returns all notes (system-level + beat-level). With beatId: returns only notes scoped to that beat. Filterable by type, status, revisionId, beatVersionId, and significance. Use offset/limit to paginate beat-scoped results; use cursor for system-wide results.",
|
|
27397
27523
|
{
|
|
27398
27524
|
systemId: external_exports.string().describe("The system ID"),
|
|
27399
27525
|
beatId: external_exports.string().optional().describe("Scope to a specific beat (omit for all system notes)"),
|
|
@@ -27405,7 +27531,7 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27405
27531
|
significance: external_exports.enum(DECISION_SIGNIFICANCE_VALUES2).optional().describe("Filter decision Notes by significance tier (strategic | structural | implementation). Notes without a significance value are treated as implementation."),
|
|
27406
27532
|
offset: external_exports.coerce.number().int().min(0).default(0).describe("Notes to skip for pagination (applies to beat-scoped and source-document-filtered results only)"),
|
|
27407
27533
|
limit: external_exports.coerce.number().int().min(1).max(500).default(100).describe("Max notes to return (default 100, max 500)"),
|
|
27408
|
-
cursor: external_exports.string().optional().describe("Opaque pagination cursor from a previous response \u2014 use for
|
|
27534
|
+
cursor: external_exports.string().optional().describe("Opaque pagination cursor from a previous response \u2014 use for system-wide note pagination (no beatId)"),
|
|
27409
27535
|
orderBy: external_exports.enum(["newest", "oldest"]).optional().describe("Sort order \u2014 newest or oldest first. Applies to beat-scoped queries (beatId provided); omit to use DynamoDB scan order.")
|
|
27410
27536
|
},
|
|
27411
27537
|
async ({ systemId, beatId, noteType, status, revisionId, beatVersionId, sourceDocumentNoteId, significance, offset = 0, limit = 100, cursor, orderBy }) => {
|
|
@@ -27420,7 +27546,7 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27420
27546
|
return { content: [{ type: "text", text: "orderBy requires beatId \u2014 it is only supported for beat-scoped queries." }], isError: true };
|
|
27421
27547
|
}
|
|
27422
27548
|
try {
|
|
27423
|
-
await
|
|
27549
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
27424
27550
|
} catch (err) {
|
|
27425
27551
|
const message = err instanceof Error ? err.message : String(err);
|
|
27426
27552
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -27463,7 +27589,7 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27463
27589
|
allNotesResult = await client.listAllSystemNotes(systemId, filters, SOURCE_DOC_FETCH_LIMIT);
|
|
27464
27590
|
} catch (err) {
|
|
27465
27591
|
const message = err instanceof Error ? err.message : String(err);
|
|
27466
|
-
return { content: [{ type: "text", text: `Failed to list
|
|
27592
|
+
return { content: [{ type: "text", text: `Failed to list system notes: ${message}` }], isError: true };
|
|
27467
27593
|
}
|
|
27468
27594
|
const { notes: allNotes, hasMore: corpusTruncated } = allNotesResult;
|
|
27469
27595
|
const filtered = allNotes.filter((n) => n.sourceSubmissionId === sourceDocumentNoteId);
|
|
@@ -27471,7 +27597,7 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27471
27597
|
page = filtered.slice(offset, offset + limit);
|
|
27472
27598
|
hasMore = offset + limit < total;
|
|
27473
27599
|
const truncationWarning = corpusTruncated ? `
|
|
27474
|
-
> \u26A0\uFE0F
|
|
27600
|
+
> \u26A0\uFE0F System has >5000 notes; sourceDocumentNoteId filter may be incomplete.
|
|
27475
27601
|
` : "";
|
|
27476
27602
|
header = (hasMore ? `Showing ${offset + 1}\u2013${offset + page.length} of ${total} notes. Use offset=${offset + limit} to continue.
|
|
27477
27603
|
|
|
@@ -27484,7 +27610,7 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27484
27610
|
projectNotesResult = await client.listAllSystemNotes(systemId, filters, limit, cursor);
|
|
27485
27611
|
} catch (err) {
|
|
27486
27612
|
const message = err instanceof Error ? err.message : String(err);
|
|
27487
|
-
return { content: [{ type: "text", text: `Failed to list
|
|
27613
|
+
return { content: [{ type: "text", text: `Failed to list system notes: ${message}` }], isError: true };
|
|
27488
27614
|
}
|
|
27489
27615
|
const { notes: rawNotes, cursor: nextCursor, hasMore: serviceHasMore } = projectNotesResult;
|
|
27490
27616
|
hasMore = serviceHasMore;
|
|
@@ -27566,11 +27692,11 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27566
27692
|
systemId: external_exports.string().describe("The system ID")
|
|
27567
27693
|
},
|
|
27568
27694
|
async ({ systemId }) => {
|
|
27569
|
-
await
|
|
27695
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
27570
27696
|
const DOCUMENT_FETCH_LIMIT = 5e3;
|
|
27571
27697
|
const { notes: docs, hasMore: truncated } = await client.listAllSystemNotes(systemId, { noteType: "document" }, DOCUMENT_FETCH_LIMIT);
|
|
27572
27698
|
const visibleDocs = docs.filter((d) => d.status !== DISMISSED_NOTE_STATUS);
|
|
27573
|
-
const truncationNotice = truncated && visibleDocs.length > 0 ? "\n\n> \u26A0\uFE0F This
|
|
27699
|
+
const truncationNotice = truncated && visibleDocs.length > 0 ? "\n\n> \u26A0\uFE0F This system has more than 5000 documents. Some documents may not be shown.\n" : "";
|
|
27574
27700
|
const text = formatDocumentList(visibleDocs) + truncationNotice;
|
|
27575
27701
|
return { content: [{ type: "text", text }] };
|
|
27576
27702
|
}
|
|
@@ -27601,7 +27727,7 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27601
27727
|
}
|
|
27602
27728
|
return { content: [{ type: "text", text: formatNoteDetail(note2) }] };
|
|
27603
27729
|
}
|
|
27604
|
-
await
|
|
27730
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
27605
27731
|
const note = await client.getNote(noteId);
|
|
27606
27732
|
if (!note) {
|
|
27607
27733
|
return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
|
|
@@ -27619,8 +27745,8 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27619
27745
|
"Create a new Note. Provide exactly one of systemId, teamspaceId, movementId, or none (org-level): systemId scopes the note to a system (optionally with beatId), teamspaceId scopes it to a teamspace (no cascade to child systems), movementId scopes it to a Movement (commercial SOW container \u2014 engagement-level knowledge), and omitting all creates an org-level note that applies across all systems.",
|
|
27620
27746
|
{
|
|
27621
27747
|
systemId: external_exports.string().optional().describe("The system ID (omit for teamspace-, movement-, or org-level note)"),
|
|
27622
|
-
teamspaceId: external_exports.string().optional().describe("The teamspace ID (omit for
|
|
27623
|
-
movementId: external_exports.string().min(1).optional().describe("The Movement ID (omit for
|
|
27748
|
+
teamspaceId: external_exports.string().optional().describe("The teamspace ID (omit for system-, movement-, or org-level note)"),
|
|
27749
|
+
movementId: external_exports.string().min(1).optional().describe("The Movement ID (omit for system-, teamspace-, or org-level note)"),
|
|
27624
27750
|
noteType: external_exports.enum(NOTE_TYPE_VALUES).describe("The type of note"),
|
|
27625
27751
|
content: external_exports.string().describe("The note content"),
|
|
27626
27752
|
beatId: external_exports.string().optional().describe("Beat ID if this note is beat-scoped (requires systemId)"),
|
|
@@ -27640,7 +27766,7 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27640
27766
|
const assumptionMeta = noteType === "assumption" && confidence !== void 0 ? { confidence } : void 0;
|
|
27641
27767
|
let note;
|
|
27642
27768
|
if (systemId) {
|
|
27643
|
-
await
|
|
27769
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
27644
27770
|
const projectCode = systemId.substring(0, 4).toUpperCase();
|
|
27645
27771
|
const noteId = await client.getNextNoteId(projectCode, systemId);
|
|
27646
27772
|
note = await client.createNote({
|
|
@@ -27725,7 +27851,7 @@ ${text}` }] };
|
|
|
27725
27851
|
content: external_exports.string().optional().describe("Updated content"),
|
|
27726
27852
|
rationale: external_exports.string().optional().describe("Why this note exists or why this decision was made"),
|
|
27727
27853
|
response: external_exports.string().optional().describe("User response (for resolving assumptions)"),
|
|
27728
|
-
beatId: external_exports.string().nullable().optional().describe("Move this note to a different beat, or null to unscope it back to
|
|
27854
|
+
beatId: external_exports.string().nullable().optional().describe("Move this note to a different beat, or null to unscope it back to system level (updates all index items transactionally)"),
|
|
27729
27855
|
revisionId: external_exports.string().optional().describe("Revision ID to associate this note with (revision-scoped assumptions, decisions, or guidance)"),
|
|
27730
27856
|
dependsOnNotes: external_exports.array(external_exports.string()).optional().describe("Note IDs this note depends on (child \u2192 parent links for consolidation)"),
|
|
27731
27857
|
humanAssignee: humanAssigneeSchema.nullable().optional().describe("Assign or reassign this note to a person (null to clear)"),
|
|
@@ -27768,7 +27894,7 @@ ${text}` }] };
|
|
|
27768
27894
|
|
|
27769
27895
|
${formatNoteDetail(updated2)}` }] };
|
|
27770
27896
|
}
|
|
27771
|
-
await
|
|
27897
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
27772
27898
|
const updated = await client.updateNote(noteId, {
|
|
27773
27899
|
...noteType !== void 0 && { noteType },
|
|
27774
27900
|
status,
|
|
@@ -27810,7 +27936,7 @@ ${text}` }] };
|
|
|
27810
27936
|
},
|
|
27811
27937
|
async ({ systemId, noteIds, noteType, status, content, response, humanAssignee, agentAssignee }) => {
|
|
27812
27938
|
try {
|
|
27813
|
-
await
|
|
27939
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
27814
27940
|
const result = await client.bulkUpdateNotes(noteIds, {
|
|
27815
27941
|
...noteType !== void 0 && { noteType },
|
|
27816
27942
|
status,
|
|
@@ -27849,7 +27975,7 @@ ${text}` }] };
|
|
|
27849
27975
|
},
|
|
27850
27976
|
async ({ systemId, noteId, targetBeatId, targetRevisionId, reason }) => {
|
|
27851
27977
|
try {
|
|
27852
|
-
await
|
|
27978
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
27853
27979
|
const updated = await client.reassignNote(noteId, targetBeatId, reason, ctx.user.userId, targetRevisionId);
|
|
27854
27980
|
if (!updated) {
|
|
27855
27981
|
return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
|
|
@@ -27871,7 +27997,7 @@ ${text}` }] };
|
|
|
27871
27997
|
},
|
|
27872
27998
|
async ({ systemId, noteId, reason }) => {
|
|
27873
27999
|
try {
|
|
27874
|
-
await
|
|
28000
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
27875
28001
|
const note = await client.getNote(noteId);
|
|
27876
28002
|
if (!note || (note.systemId ?? note.projectId) !== systemId) {
|
|
27877
28003
|
return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
|
|
@@ -27898,7 +28024,7 @@ ${text}` }] };
|
|
|
27898
28024
|
},
|
|
27899
28025
|
async ({ systemId, noteId, reason }) => {
|
|
27900
28026
|
try {
|
|
27901
|
-
await
|
|
28027
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
27902
28028
|
const note = await client.getNote(noteId);
|
|
27903
28029
|
if (!note || (note.systemId ?? note.projectId) !== systemId) {
|
|
27904
28030
|
return { content: [{ type: "text", text: `Document not found: "${noteId}"` }], isError: true };
|
|
@@ -27928,7 +28054,7 @@ ${text}` }] };
|
|
|
27928
28054
|
systemId: external_exports.string().describe("The system ID")
|
|
27929
28055
|
},
|
|
27930
28056
|
async ({ systemId }) => {
|
|
27931
|
-
await
|
|
28057
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
27932
28058
|
const assumptions = await client.getKeyAssumptions(systemId);
|
|
27933
28059
|
if (assumptions.length === 0) {
|
|
27934
28060
|
return { content: [{ type: "text", text: "No unvalidated assumptions found." }] };
|
|
@@ -28631,7 +28757,7 @@ function registerOnboardingTools(server, ctx, client) {
|
|
|
28631
28757
|
analysisJson: external_exports.string().optional().describe("JSON-serialized DiscoveryAnalysis from initiate_teamspace_onboarding. Pass this to produce Beats grounded in the actual discovery data.")
|
|
28632
28758
|
},
|
|
28633
28759
|
async ({ systemId, systemTitle, projectDescription, analysisJson }) => {
|
|
28634
|
-
await
|
|
28760
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
28635
28761
|
const result = await client.draftOnboardingBeats(systemId, { systemTitle, projectDescription, analysisJson });
|
|
28636
28762
|
if (!result.suggestions || result.suggestions.length === 0) {
|
|
28637
28763
|
return {
|
|
@@ -28660,7 +28786,7 @@ function registerOnboardingTools(server, ctx, client) {
|
|
|
28660
28786
|
server.tool(
|
|
28661
28787
|
"create_teamspace_onboarding_batch",
|
|
28662
28788
|
[
|
|
28663
|
-
"Create a Teamspace,
|
|
28789
|
+
"Create a Teamspace, Systems, and Notes",
|
|
28664
28790
|
"from a PM-approved onboarding payload in a single batch operation.",
|
|
28665
28791
|
"Call this after the PM approves the pre-flight review card from initiate_teamspace_onboarding.",
|
|
28666
28792
|
"Returns a creation receipt with IDs and counts for all created entities.",
|
|
@@ -28668,8 +28794,8 @@ function registerOnboardingTools(server, ctx, client) {
|
|
|
28668
28794
|
].join(" "),
|
|
28669
28795
|
{
|
|
28670
28796
|
teamspaceName: external_exports.string().min(1).max(200).describe("Teamspace display name"),
|
|
28671
|
-
projects: external_exports.array(OnboardingProjectInputSchema).min(1).describe("Phase 1
|
|
28672
|
-
teamspaceNotes: external_exports.array(OnboardingNoteInputSchema).describe("Constraints, assumptions, and context notes at the Teamspace/
|
|
28797
|
+
projects: external_exports.array(OnboardingProjectInputSchema).min(1).describe("Phase 1 systems to create, each linked to the Teamspace"),
|
|
28798
|
+
teamspaceNotes: external_exports.array(OnboardingNoteInputSchema).describe("Constraints, assumptions, and context notes at the Teamspace/system level")
|
|
28673
28799
|
},
|
|
28674
28800
|
async ({
|
|
28675
28801
|
teamspaceName,
|
|
@@ -28710,7 +28836,7 @@ Error: ${msg}`);
|
|
|
28710
28836
|
const msg = err instanceof Error ? err.message : String(err);
|
|
28711
28837
|
lines.push(`
|
|
28712
28838
|
Projects created: ${createdProjects.length}`);
|
|
28713
|
-
lines.unshift(`\u26A0\uFE0F Onboarding batch partially created \u2014 stopped at:
|
|
28839
|
+
lines.unshift(`\u26A0\uFE0F Onboarding batch partially created \u2014 stopped at: system "${p.title}"
|
|
28714
28840
|
Error: ${msg}`);
|
|
28715
28841
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
28716
28842
|
}
|
|
@@ -28932,7 +29058,7 @@ function registerOrganizationTools(server, ctx, client) {
|
|
|
28932
29058
|
if (system.orgId !== ctx.orgId) {
|
|
28933
29059
|
return { content: [{ type: "text", text: `System "${systemId}" does not belong to the configured organization.` }], isError: true };
|
|
28934
29060
|
}
|
|
28935
|
-
const result = await client.
|
|
29061
|
+
const result = await client.transferSystem(ctx.orgId, systemId, targetOrgId);
|
|
28936
29062
|
if (result.error) {
|
|
28937
29063
|
return { content: [{ type: "text", text: result.error }], isError: true };
|
|
28938
29064
|
}
|
|
@@ -29083,7 +29209,7 @@ function registerPlanQualityTools(server, ctx, client) {
|
|
|
29083
29209
|
isError: true
|
|
29084
29210
|
};
|
|
29085
29211
|
}
|
|
29086
|
-
await
|
|
29212
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
29087
29213
|
const opts = branch || localPath ? { branch, localPath } : void 0;
|
|
29088
29214
|
const task = await client.runCheck(systemId, "beat_version_quality", beatVersionId, opts);
|
|
29089
29215
|
if (!wait) {
|
|
@@ -29230,7 +29356,7 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
|
|
|
29230
29356
|
},
|
|
29231
29357
|
async ({ systemId, wait }) => {
|
|
29232
29358
|
try {
|
|
29233
|
-
await
|
|
29359
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
29234
29360
|
const { taskId } = await client.runCheck(systemId, "portfolio_coherence", systemId);
|
|
29235
29361
|
if (!wait) {
|
|
29236
29362
|
return {
|
|
@@ -29267,7 +29393,7 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
|
|
|
29267
29393
|
const archivedBeatIds = [];
|
|
29268
29394
|
let totalNotesReassigned = 0;
|
|
29269
29395
|
try {
|
|
29270
|
-
await
|
|
29396
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
29271
29397
|
const uniqueBeatIds = [...new Set(beatIds)];
|
|
29272
29398
|
const resolvedPrimaryId = primaryBeatId ?? uniqueBeatIds[0];
|
|
29273
29399
|
if (!uniqueBeatIds.includes(resolvedPrimaryId)) {
|
|
@@ -30020,8 +30146,8 @@ function registerRevisionQualityTools(server, ctx, client) {
|
|
|
30020
30146
|
isError: true
|
|
30021
30147
|
};
|
|
30022
30148
|
}
|
|
30023
|
-
await
|
|
30024
|
-
await
|
|
30149
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
30150
|
+
await assertRevisionInSystem(client, revisionId, systemId);
|
|
30025
30151
|
const task = await client.runCheck(systemId, "revision_quality", revisionId);
|
|
30026
30152
|
if (!wait) {
|
|
30027
30153
|
return {
|
|
@@ -30326,7 +30452,7 @@ function registerSnapshotTools(server, ctx, client) {
|
|
|
30326
30452
|
"Export a complete system snapshot for environment sync. On a remote server (production/staging) returns an S3 presigned URL; on a local server writes to a temp file and returns the path.",
|
|
30327
30453
|
{ systemId: external_exports.string().describe("The system ID to export") },
|
|
30328
30454
|
async ({ systemId }) => {
|
|
30329
|
-
await
|
|
30455
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
30330
30456
|
const result = await client.exportSystemSnapshot(systemId);
|
|
30331
30457
|
if (typeof result === "string") {
|
|
30332
30458
|
if (!result.startsWith("https://")) {
|
|
@@ -30574,7 +30700,7 @@ function registerSubscriptionTools(server, ctx, client) {
|
|
|
30574
30700
|
},
|
|
30575
30701
|
async ({ entityType, entityId, systemId }) => {
|
|
30576
30702
|
try {
|
|
30577
|
-
await
|
|
30703
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
30578
30704
|
await client.subscribe(ctx.user.userId, entityType, entityId, systemId, "manual");
|
|
30579
30705
|
return {
|
|
30580
30706
|
content: [{
|
|
@@ -30638,7 +30764,7 @@ function registerSubscriptionTools(server, ctx, client) {
|
|
|
30638
30764
|
}
|
|
30639
30765
|
|
|
30640
30766
|
// ../../libs/harmonica-services/src/mcp/tools/system-lifecycle-tools.ts
|
|
30641
|
-
function
|
|
30767
|
+
function registerSystemLifecycleTools(server, ctx, client) {
|
|
30642
30768
|
server.tool(
|
|
30643
30769
|
"transition_system_lifecycle",
|
|
30644
30770
|
"Advance a System's capability-maturity lifecycle state. Allowed transitions follow the state machine (Concept \u2192 Incubating \u2192 Piloting \u2192 Activated \u2192 Commercializing \u2192 Scaled, plus paused/killed/sunset/archived exits). The activated \u2192 commercializing edge requires a `decisionNoteId` pointing to a system-scoped Decision Note (the governance review). Other gates are advisory.",
|
|
@@ -30744,7 +30870,7 @@ function registerSystemTools(server, ctx, client) {
|
|
|
30744
30870
|
}) => {
|
|
30745
30871
|
try {
|
|
30746
30872
|
const [project, org] = await Promise.all([
|
|
30747
|
-
|
|
30873
|
+
fetchSystemInOrg(client, systemId, ctx.orgId),
|
|
30748
30874
|
client.getOrg(ctx.orgId)
|
|
30749
30875
|
]);
|
|
30750
30876
|
const notes = await client.listSystemNotes(systemId, {
|
|
@@ -30788,7 +30914,7 @@ function registerSystemTools(server, ctx, client) {
|
|
|
30788
30914
|
if (Object.keys(nonEmpty).length === 0) {
|
|
30789
30915
|
return { content: [{ type: "text", text: "No updates provided." }], isError: true };
|
|
30790
30916
|
}
|
|
30791
|
-
await
|
|
30917
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
30792
30918
|
if (nonEmpty["accountId"] === "") delete nonEmpty["accountId"];
|
|
30793
30919
|
if (typeof updates.teamspaceId === "string") {
|
|
30794
30920
|
const teamspace = await client.getTeamspace(updates.teamspaceId);
|
|
@@ -31127,7 +31253,7 @@ function registerValueVelocityTools(server, ctx, client) {
|
|
|
31127
31253
|
},
|
|
31128
31254
|
async ({ systemId, lens, withLeverage }) => {
|
|
31129
31255
|
try {
|
|
31130
|
-
await
|
|
31256
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
31131
31257
|
const [rankedResult, staleWResult] = await Promise.allSettled([
|
|
31132
31258
|
withLeverage ? client.rankSystemBeatVersionsWithLeverage(systemId, lens) : client.rankSystemBeatVersions(systemId, lens),
|
|
31133
31259
|
client.getStaleWBeatIds(systemId)
|
|
@@ -31234,7 +31360,7 @@ function registerValueVelocityTools(server, ctx, client) {
|
|
|
31234
31360
|
},
|
|
31235
31361
|
async ({ systemId, beatId }) => {
|
|
31236
31362
|
try {
|
|
31237
|
-
await
|
|
31363
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
31238
31364
|
const actorId = resolveActorId(ctx);
|
|
31239
31365
|
if (!actorId) {
|
|
31240
31366
|
return { content: [{ type: "text", text: "Actor identity unavailable: user has no email or userId." }], isError: true };
|
|
@@ -31417,6 +31543,7 @@ function registerWorkItemTools(server, _ctx, client) {
|
|
|
31417
31543
|
wi.valueUnits !== void 0 ? `Value Units: ${wi.valueUnits}` : null,
|
|
31418
31544
|
wi.costPerValueUnit !== void 0 ? `Cost per Value Unit: ${wi.costPerValueUnit}` : null,
|
|
31419
31545
|
wi.beatVersionId ? `Linked Beat Version: ${wi.beatVersionId}` : null,
|
|
31546
|
+
wi.workOrderId ? `Work Order: ${wi.workOrderId}` : "Work Order: none (unattached)",
|
|
31420
31547
|
estimateHint ? `Estimate Hint: ${estimateHint.hours}h (source: ${estimateHint.source})` : null,
|
|
31421
31548
|
wi.createdBy ? `Created By: ${wi.createdBy}` : null,
|
|
31422
31549
|
`Created: ${wi.createdAt}`,
|
|
@@ -31427,7 +31554,7 @@ function registerWorkItemTools(server, _ctx, client) {
|
|
|
31427
31554
|
);
|
|
31428
31555
|
server.tool(
|
|
31429
31556
|
"create_work_item",
|
|
31430
|
-
'Create a work item under a Track (DeliverableGroup). A work item is an owned, statused unit of operational work \u2014 it is NOT a Beat. Starts in "not_started" status. Optionally link it to a Beat Version for capability lineage (v2, N-4E09-6574) \u2014 the link is one-directional and never required.',
|
|
31557
|
+
'Create a work item under a Track (DeliverableGroup). A work item is an owned, statused unit of operational work \u2014 it is NOT a Beat. Starts in "not_started" status. Optionally link it to a Beat Version for capability lineage (v2, N-4E09-6574) \u2014 the link is one-directional and never required. Optionally link it to a Work Order it delivers part of; leaving it unattached is a supported state, not a gap to fill (N-4E09-7160).',
|
|
31431
31558
|
{
|
|
31432
31559
|
trackId: external_exports.string().describe("The Track (DeliverableGroup) ID this work item belongs to"),
|
|
31433
31560
|
title: external_exports.string().min(1).max(300).describe("Work item title"),
|
|
@@ -31438,9 +31565,10 @@ function registerWorkItemTools(server, _ctx, client) {
|
|
|
31438
31565
|
valueUnits: external_exports.number().nonnegative().optional().describe("Optional Value Units (commercial lens) \u2014 independent of effortSize"),
|
|
31439
31566
|
costPerValueUnit: external_exports.number().nonnegative().optional().describe("Optional price per Value Unit"),
|
|
31440
31567
|
createdBy: external_exports.string().optional().describe("Optional creator identity for audit"),
|
|
31441
|
-
beatVersionId: external_exports.string().min(1).optional().describe("Optional Beat Version to link for capability lineage. Must exist.")
|
|
31568
|
+
beatVersionId: external_exports.string().min(1).optional().describe("Optional Beat Version to link for capability lineage. Must exist."),
|
|
31569
|
+
workOrderId: external_exports.string().min(1).max(128).optional().describe("Optional Work Order this item delivers part of. Must exist. Omit when there is no Order \u2014 unattached work is legitimate, so do not invent one.")
|
|
31442
31570
|
},
|
|
31443
|
-
async ({ trackId, title, ownerName, ownerEmail, committedEstimateHours, effortSize, valueUnits, costPerValueUnit, createdBy, beatVersionId }) => {
|
|
31571
|
+
async ({ trackId, title, ownerName, ownerEmail, committedEstimateHours, effortSize, valueUnits, costPerValueUnit, createdBy, beatVersionId, workOrderId }) => {
|
|
31444
31572
|
if (ownerName === void 0 !== (ownerEmail === void 0)) {
|
|
31445
31573
|
return {
|
|
31446
31574
|
content: [{ type: "text", text: "Setting the owner requires both ownerName and ownerEmail. Omit both to create an unowned work item." }],
|
|
@@ -31456,7 +31584,8 @@ function registerWorkItemTools(server, _ctx, client) {
|
|
|
31456
31584
|
...valueUnits !== void 0 && { valueUnits },
|
|
31457
31585
|
...costPerValueUnit !== void 0 && { costPerValueUnit },
|
|
31458
31586
|
...createdBy !== void 0 && { createdBy },
|
|
31459
|
-
...beatVersionId !== void 0 && { beatVersionId }
|
|
31587
|
+
...beatVersionId !== void 0 && { beatVersionId },
|
|
31588
|
+
...workOrderId !== void 0 && { workOrderId }
|
|
31460
31589
|
});
|
|
31461
31590
|
return {
|
|
31462
31591
|
content: [
|
|
@@ -31470,7 +31599,7 @@ function registerWorkItemTools(server, _ctx, client) {
|
|
|
31470
31599
|
);
|
|
31471
31600
|
server.tool(
|
|
31472
31601
|
"update_work_item",
|
|
31473
|
-
"Update mutable content fields on a work item (title, owner, committedEstimateHours, actualHours, effortSize, valueUnits, costPerValueUnit, beatVersionId). To move it between Tracks use move_work_item; to change its status use transition_work_item_status.",
|
|
31602
|
+
"Update mutable content fields on a work item (title, owner, committedEstimateHours, actualHours, effortSize, valueUnits, costPerValueUnit, beatVersionId, workOrderId). To move it between Tracks use move_work_item; to change its status use transition_work_item_status.",
|
|
31474
31603
|
{
|
|
31475
31604
|
workItemId: external_exports.string().describe("The work item ID"),
|
|
31476
31605
|
title: external_exports.string().min(1).max(300).optional().describe("New title"),
|
|
@@ -31482,9 +31611,10 @@ function registerWorkItemTools(server, _ctx, client) {
|
|
|
31482
31611
|
effortSize: external_exports.enum(WORK_ITEM_EFFORT_SIZE_ENUM).nullable().optional().describe("Rough t-shirt effort size (delivery lens). Pass null to clear; omit to leave untouched."),
|
|
31483
31612
|
valueUnits: external_exports.number().nonnegative().nullable().optional().describe("Value Units (commercial lens). Pass null to clear; omit to leave untouched."),
|
|
31484
31613
|
costPerValueUnit: external_exports.number().nonnegative().nullable().optional().describe("Price per Value Unit. Pass null to clear; omit to leave untouched."),
|
|
31485
|
-
beatVersionId: external_exports.string().min(1).nullable().optional().describe("Link to a Beat Version for capability lineage. Pass null to unlink; omit to leave the current link untouched.")
|
|
31614
|
+
beatVersionId: external_exports.string().min(1).nullable().optional().describe("Link to a Beat Version for capability lineage. Pass null to unlink; omit to leave the current link untouched."),
|
|
31615
|
+
workOrderId: external_exports.string().min(1).max(128).nullable().optional().describe("Link to the Work Order this item delivers part of. Pass null to unlink, leaving the item unattached; omit to leave the current link untouched.")
|
|
31486
31616
|
},
|
|
31487
|
-
async ({ workItemId, title, ownerName, ownerEmail, unassignOwner, committedEstimateHours, actualHours, effortSize, valueUnits, costPerValueUnit, beatVersionId }) => {
|
|
31617
|
+
async ({ workItemId, title, ownerName, ownerEmail, unassignOwner, committedEstimateHours, actualHours, effortSize, valueUnits, costPerValueUnit, beatVersionId, workOrderId }) => {
|
|
31488
31618
|
const hasOwnerUpdate = ownerName !== void 0 || ownerEmail !== void 0;
|
|
31489
31619
|
if (hasOwnerUpdate && (ownerName === void 0 || ownerEmail === void 0)) {
|
|
31490
31620
|
return {
|
|
@@ -31498,7 +31628,9 @@ function registerWorkItemTools(server, _ctx, client) {
|
|
|
31498
31628
|
isError: true
|
|
31499
31629
|
};
|
|
31500
31630
|
}
|
|
31501
|
-
const hasUpdates = title !== void 0 || hasOwnerUpdate || unassignOwner === true || committedEstimateHours !== void 0 || actualHours !== void 0 || effortSize !== void 0 || valueUnits !== void 0 || costPerValueUnit !== void 0 || beatVersionId !== void 0
|
|
31631
|
+
const hasUpdates = title !== void 0 || hasOwnerUpdate || unassignOwner === true || committedEstimateHours !== void 0 || actualHours !== void 0 || effortSize !== void 0 || valueUnits !== void 0 || costPerValueUnit !== void 0 || beatVersionId !== void 0 || // `!== undefined`, never truthiness: an unlink arrives as `null`, and
|
|
31632
|
+
// a truthy check here would report "no updates provided" and drop it.
|
|
31633
|
+
workOrderId !== void 0;
|
|
31502
31634
|
if (!hasUpdates) {
|
|
31503
31635
|
return {
|
|
31504
31636
|
content: [{ type: "text", text: `No updates provided for work item: ${workItemId}` }],
|
|
@@ -31514,7 +31646,8 @@ function registerWorkItemTools(server, _ctx, client) {
|
|
|
31514
31646
|
...effortSize !== void 0 && { effortSize },
|
|
31515
31647
|
...valueUnits !== void 0 && { valueUnits },
|
|
31516
31648
|
...costPerValueUnit !== void 0 && { costPerValueUnit },
|
|
31517
|
-
...beatVersionId !== void 0 && { beatVersionId }
|
|
31649
|
+
...beatVersionId !== void 0 && { beatVersionId },
|
|
31650
|
+
...workOrderId !== void 0 && { workOrderId }
|
|
31518
31651
|
});
|
|
31519
31652
|
if (!wi) {
|
|
31520
31653
|
return {
|
|
@@ -31579,6 +31712,337 @@ function registerWorkItemTools(server, _ctx, client) {
|
|
|
31579
31712
|
);
|
|
31580
31713
|
}
|
|
31581
31714
|
|
|
31715
|
+
// ../../libs/harmonica-services/src/mcp/tools/work-order-tools.ts
|
|
31716
|
+
var WORK_INTAKE_STATUS_ENUM = [
|
|
31717
|
+
"requested",
|
|
31718
|
+
"needs_decision",
|
|
31719
|
+
"accepted",
|
|
31720
|
+
"done"
|
|
31721
|
+
];
|
|
31722
|
+
var NEEDS_DECISION_REASON_ENUM = [
|
|
31723
|
+
"out_of_scope",
|
|
31724
|
+
"exceeds_capacity",
|
|
31725
|
+
"incoherent_with_decision"
|
|
31726
|
+
];
|
|
31727
|
+
var WORK_ORDER_SIZE_ENUM = [
|
|
31728
|
+
"XS",
|
|
31729
|
+
"S",
|
|
31730
|
+
"M",
|
|
31731
|
+
"L",
|
|
31732
|
+
"XL"
|
|
31733
|
+
];
|
|
31734
|
+
function oneLine(value) {
|
|
31735
|
+
return value.replace(/[\r\n]+/g, " ");
|
|
31736
|
+
}
|
|
31737
|
+
function formatOrderLine(o) {
|
|
31738
|
+
const reason = o.needsDecisionReason ? ` [${o.needsDecisionReason}]` : "";
|
|
31739
|
+
return `[${o.workOrderId}] ${oneLine(o.title)} \u2014 ${o.status}${reason}, ${o.size ?? "unsized"}, on ${o.trackId}`;
|
|
31740
|
+
}
|
|
31741
|
+
function registerWorkOrderTools(server, _ctx, client) {
|
|
31742
|
+
server.tool(
|
|
31743
|
+
"list_track_work_orders",
|
|
31744
|
+
'List the Work Orders placed on a Track (DeliverableGroup). A Work Order is the internal unit of agreed work. Recognition cues: "what is committed on this track", "list the orders" \u2192 list_track_work_orders.',
|
|
31745
|
+
{
|
|
31746
|
+
trackId: external_exports.string().describe("The Track (DeliverableGroup) ID")
|
|
31747
|
+
},
|
|
31748
|
+
async ({ trackId }) => {
|
|
31749
|
+
const orders = await client.listTrackWorkOrders(trackId);
|
|
31750
|
+
const text = orders.length === 0 ? "No work orders found." : orders.map(formatOrderLine).join("\n");
|
|
31751
|
+
return { content: [{ type: "text", text }] };
|
|
31752
|
+
}
|
|
31753
|
+
);
|
|
31754
|
+
server.tool(
|
|
31755
|
+
"list_work_request_work_orders",
|
|
31756
|
+
"List the Work Orders one Work Request split into \u2014 the lineage view. A four-way split shows four orders here, each possibly on a different Track and in a different state.",
|
|
31757
|
+
{
|
|
31758
|
+
workRequestId: external_exports.string().describe("The Work Request ID")
|
|
31759
|
+
},
|
|
31760
|
+
async ({ workRequestId }) => {
|
|
31761
|
+
const orders = await client.listWorkRequestWorkOrders(workRequestId);
|
|
31762
|
+
const text = orders.length === 0 ? "No work orders found \u2014 this request has not been split yet." : orders.map(formatOrderLine).join("\n");
|
|
31763
|
+
return { content: [{ type: "text", text }] };
|
|
31764
|
+
}
|
|
31765
|
+
);
|
|
31766
|
+
server.tool(
|
|
31767
|
+
"get_work_order",
|
|
31768
|
+
"Get a Work Order by ID. Returns its title, status (with the routing reason when it is awaiting a decision), t-shirt size, the Track it sits on, and the Work Request it was split from.",
|
|
31769
|
+
{
|
|
31770
|
+
workOrderId: external_exports.string().describe("The Work Order ID (e.g., wo-abc123)")
|
|
31771
|
+
},
|
|
31772
|
+
async ({ workOrderId }) => {
|
|
31773
|
+
const wo = await client.getWorkOrder(workOrderId);
|
|
31774
|
+
if (!wo) {
|
|
31775
|
+
return { content: [{ type: "text", text: `Work order not found: ${workOrderId}` }] };
|
|
31776
|
+
}
|
|
31777
|
+
const lines = [
|
|
31778
|
+
`ID: ${wo.workOrderId}`,
|
|
31779
|
+
`Title: ${oneLine(wo.title)}`,
|
|
31780
|
+
`From Work Request: ${wo.workRequestId}`,
|
|
31781
|
+
`Track: ${wo.trackId}`,
|
|
31782
|
+
`Status: ${wo.status}`,
|
|
31783
|
+
wo.needsDecisionReason ? `Routing Reason: ${wo.needsDecisionReason}` : null,
|
|
31784
|
+
wo.size !== void 0 ? `Size: ${wo.size}` : "Size: unsized",
|
|
31785
|
+
wo.description ? `Description: ${oneLine(wo.description)}` : null,
|
|
31786
|
+
wo.createdBy ? `Created By: ${wo.createdBy}` : null,
|
|
31787
|
+
`Created: ${wo.createdAt}`,
|
|
31788
|
+
`Updated: ${wo.updatedAt}`
|
|
31789
|
+
].filter(Boolean).join("\n");
|
|
31790
|
+
return { content: [{ type: "text", text: lines }] };
|
|
31791
|
+
}
|
|
31792
|
+
);
|
|
31793
|
+
server.tool(
|
|
31794
|
+
"create_work_order",
|
|
31795
|
+
'Split a Work Request into an agreed Work Order on a Track. Called once per piece: a request covering two separate lines of work becomes two orders. Starts in "requested" status \u2014 moving it past the coherence gate is transition_work_order_status. The Track must belong to the same teamspace and Account as the Request; the service refuses a placement that crosses either boundary.',
|
|
31796
|
+
{
|
|
31797
|
+
trackId: external_exports.string().describe("The Track (DeliverableGroup) this order is placed on"),
|
|
31798
|
+
workRequestId: external_exports.string().min(1).describe("The Work Request this order was split from \u2014 immutable lineage"),
|
|
31799
|
+
title: external_exports.string().min(1).max(300).describe("Work order title"),
|
|
31800
|
+
description: external_exports.string().max(1e4).optional().describe("Optional detail beyond the title"),
|
|
31801
|
+
size: external_exports.enum(WORK_ORDER_SIZE_ENUM).optional().describe("Optional t-shirt size. An order can arrive before it is sized; a request's composition counts unsized orders rather than hiding them."),
|
|
31802
|
+
createdBy: external_exports.string().optional().describe("Optional creator identity for audit")
|
|
31803
|
+
},
|
|
31804
|
+
async ({ trackId, workRequestId, title, description, size, createdBy }) => {
|
|
31805
|
+
const wo = await client.createWorkOrder({
|
|
31806
|
+
trackId,
|
|
31807
|
+
workRequestId,
|
|
31808
|
+
title,
|
|
31809
|
+
...description !== void 0 && { description },
|
|
31810
|
+
...size !== void 0 && { size },
|
|
31811
|
+
...createdBy !== void 0 && { createdBy }
|
|
31812
|
+
});
|
|
31813
|
+
return {
|
|
31814
|
+
content: [{ type: "text", text: `Created work order: ${formatOrderLine(wo)}` }]
|
|
31815
|
+
};
|
|
31816
|
+
}
|
|
31817
|
+
);
|
|
31818
|
+
server.tool(
|
|
31819
|
+
"update_work_order",
|
|
31820
|
+
"Update content fields on a Work Order (title, description, size). To move it between Tracks use move_work_order; to change its status use transition_work_order_status. The Work Request it came from cannot be changed \u2014 that lineage is immutable.",
|
|
31821
|
+
{
|
|
31822
|
+
workOrderId: external_exports.string().describe("The Work Order ID"),
|
|
31823
|
+
title: external_exports.string().min(1).max(300).optional().describe("New title"),
|
|
31824
|
+
description: external_exports.string().max(1e4).nullable().optional().describe("New description. Pass null to clear; omit to leave untouched."),
|
|
31825
|
+
size: external_exports.enum(WORK_ORDER_SIZE_ENUM).nullable().optional().describe("New t-shirt size. Pass null to return it to unsized; omit to leave untouched.")
|
|
31826
|
+
},
|
|
31827
|
+
async ({ workOrderId, title, description, size }) => {
|
|
31828
|
+
if (title === void 0 && description === void 0 && size === void 0) {
|
|
31829
|
+
return {
|
|
31830
|
+
content: [
|
|
31831
|
+
{ type: "text", text: "Nothing to update: pass at least one of title, description, or size." }
|
|
31832
|
+
],
|
|
31833
|
+
isError: true
|
|
31834
|
+
};
|
|
31835
|
+
}
|
|
31836
|
+
const wo = await client.updateWorkOrder(workOrderId, {
|
|
31837
|
+
...title !== void 0 && { title },
|
|
31838
|
+
...description !== void 0 && { description },
|
|
31839
|
+
...size !== void 0 && { size }
|
|
31840
|
+
});
|
|
31841
|
+
if (!wo) {
|
|
31842
|
+
return { content: [{ type: "text", text: `Work order not found: ${workOrderId}` }] };
|
|
31843
|
+
}
|
|
31844
|
+
return { content: [{ type: "text", text: `Updated work order: ${formatOrderLine(wo)}` }] };
|
|
31845
|
+
}
|
|
31846
|
+
);
|
|
31847
|
+
server.tool(
|
|
31848
|
+
"move_work_order",
|
|
31849
|
+
"Move a Work Order to a different Track \u2014 the reversal path for a bad early split guess. The destination must belong to the same teamspace and Account as the order's Work Request; the service refuses a placement that crosses either boundary.",
|
|
31850
|
+
{
|
|
31851
|
+
workOrderId: external_exports.string().describe("The Work Order ID"),
|
|
31852
|
+
newTrackId: external_exports.string().describe("The destination Track (DeliverableGroup) ID")
|
|
31853
|
+
},
|
|
31854
|
+
async ({ workOrderId, newTrackId }) => {
|
|
31855
|
+
const wo = await client.moveWorkOrder(workOrderId, newTrackId);
|
|
31856
|
+
if (!wo) {
|
|
31857
|
+
return { content: [{ type: "text", text: `Work order not found: ${workOrderId}` }] };
|
|
31858
|
+
}
|
|
31859
|
+
return {
|
|
31860
|
+
content: [{ type: "text", text: `Moved work order to Track ${wo.trackId}: ${formatOrderLine(wo)}` }]
|
|
31861
|
+
};
|
|
31862
|
+
}
|
|
31863
|
+
);
|
|
31864
|
+
server.tool(
|
|
31865
|
+
"transition_work_order_status",
|
|
31866
|
+
"Move a Work Order through the intake gate: requested \u2192 accepted is the coherence gate itself, and there is no shortcut past it. Routing an order to needs_decision REQUIRES a reason, because the reason is the routing: out_of_scope goes back to the customer, exceeds_capacity goes to the Orchestrator, incoherent_with_decision goes back for reframing. A routed order returns to requested once resolved.",
|
|
31867
|
+
{
|
|
31868
|
+
workOrderId: external_exports.string().describe("The Work Order ID"),
|
|
31869
|
+
status: external_exports.enum(WORK_INTAKE_STATUS_ENUM).describe("Target status"),
|
|
31870
|
+
reason: external_exports.enum(NEEDS_DECISION_REASON_ENUM).optional().describe("Required when \u2014 and only when \u2014 status is needs_decision. The reason IS the routing.")
|
|
31871
|
+
},
|
|
31872
|
+
async ({ workOrderId, status, reason }) => {
|
|
31873
|
+
if (status === "needs_decision" && reason === void 0) {
|
|
31874
|
+
return {
|
|
31875
|
+
content: [
|
|
31876
|
+
{
|
|
31877
|
+
type: "text",
|
|
31878
|
+
text: "Routing an order to needs_decision requires a reason \u2014 the reason is the routing. Pass one of: out_of_scope, exceeds_capacity, incoherent_with_decision."
|
|
31879
|
+
}
|
|
31880
|
+
],
|
|
31881
|
+
isError: true
|
|
31882
|
+
};
|
|
31883
|
+
}
|
|
31884
|
+
if (status !== "needs_decision" && reason !== void 0) {
|
|
31885
|
+
return {
|
|
31886
|
+
content: [
|
|
31887
|
+
{
|
|
31888
|
+
type: "text",
|
|
31889
|
+
text: `A routing reason is only meaningful on a transition to needs_decision, not to ${status}.`
|
|
31890
|
+
}
|
|
31891
|
+
],
|
|
31892
|
+
isError: true
|
|
31893
|
+
};
|
|
31894
|
+
}
|
|
31895
|
+
const wo = await client.transitionWorkOrderStatus(workOrderId, status, reason);
|
|
31896
|
+
if (!wo) {
|
|
31897
|
+
return { content: [{ type: "text", text: `Work order not found: ${workOrderId}` }] };
|
|
31898
|
+
}
|
|
31899
|
+
return {
|
|
31900
|
+
content: [{ type: "text", text: `Transitioned work order: ${formatOrderLine(wo)}` }]
|
|
31901
|
+
};
|
|
31902
|
+
}
|
|
31903
|
+
);
|
|
31904
|
+
}
|
|
31905
|
+
|
|
31906
|
+
// ../../libs/harmonica-services/src/mcp/tools/work-request-tools.ts
|
|
31907
|
+
var WORK_REQUEST_ORIGIN_ENUM = [
|
|
31908
|
+
"client",
|
|
31909
|
+
"internal"
|
|
31910
|
+
];
|
|
31911
|
+
function oneLine2(value) {
|
|
31912
|
+
return value.replace(/[\r\n]+/g, " ");
|
|
31913
|
+
}
|
|
31914
|
+
function formatSizeComposition(size) {
|
|
31915
|
+
const parts = size.entries.map((e) => `${e.count}${e.size}`);
|
|
31916
|
+
if (size.unsized > 0) parts.push(`${size.unsized} unsized`);
|
|
31917
|
+
if (parts.length === 0) return "no orders yet";
|
|
31918
|
+
return `${parts.join(" + ")} (${size.totalValueUnits} value units)`;
|
|
31919
|
+
}
|
|
31920
|
+
function registerWorkRequestTools(server, _ctx, client) {
|
|
31921
|
+
server.tool(
|
|
31922
|
+
"list_movement_work_requests",
|
|
31923
|
+
`List the Work Requests filed against a Movement. A Work Request is the customer's ask in their own language, before it is sized or agreed. Recognition cues: "what has the client asked for", "list the requests on this engagement" \u2192 list_movement_work_requests.`,
|
|
31924
|
+
{
|
|
31925
|
+
movementId: external_exports.string().describe("The Movement (engagement) ID")
|
|
31926
|
+
},
|
|
31927
|
+
async ({ movementId }) => {
|
|
31928
|
+
const requests = await client.listMovementWorkRequests(movementId);
|
|
31929
|
+
const text = requests.length === 0 ? "No work requests found." : requests.map((wr) => `[${wr.workRequestId}] ${oneLine2(wr.title)} \u2014 from ${oneLine2(wr.requester.name)} (${wr.origin})`).join("\n");
|
|
31930
|
+
return { content: [{ type: "text", text }] };
|
|
31931
|
+
}
|
|
31932
|
+
);
|
|
31933
|
+
server.tool(
|
|
31934
|
+
"get_work_request",
|
|
31935
|
+
"Get a Work Request with the Work Orders it split into and their combined size composition. Note there is no request-level status: each Order carries its own, because a split whose halves sit in different states cannot be reduced to one word.",
|
|
31936
|
+
{
|
|
31937
|
+
workRequestId: external_exports.string().describe("The Work Request ID (e.g., wr-abc123)")
|
|
31938
|
+
},
|
|
31939
|
+
async ({ workRequestId }) => {
|
|
31940
|
+
const detail = await client.getWorkRequestDetail(workRequestId);
|
|
31941
|
+
if (!detail) {
|
|
31942
|
+
return { content: [{ type: "text", text: `Work request not found: ${workRequestId}` }] };
|
|
31943
|
+
}
|
|
31944
|
+
const { workRequest: wr, orders, size } = detail;
|
|
31945
|
+
const orderLines = orders.length === 0 ? ["Orders: none yet \u2014 this request has not been split"] : [
|
|
31946
|
+
"Orders:",
|
|
31947
|
+
...orders.map((o) => {
|
|
31948
|
+
const track = o.trackTitle ? `${oneLine2(o.trackTitle)} (${o.trackId})` : o.trackId;
|
|
31949
|
+
const reason = o.needsDecisionReason ? ` [${o.needsDecisionReason}]` : "";
|
|
31950
|
+
const sized = o.size ?? "unsized";
|
|
31951
|
+
return ` [${o.workOrderId}] ${oneLine2(o.title)} \u2014 ${o.status}${reason}, ${sized}, on ${track}`;
|
|
31952
|
+
})
|
|
31953
|
+
];
|
|
31954
|
+
const lines = [
|
|
31955
|
+
`ID: ${wr.workRequestId}`,
|
|
31956
|
+
`Title: ${oneLine2(wr.title)}`,
|
|
31957
|
+
`Movement: ${wr.movementId}`,
|
|
31958
|
+
`Requester: ${oneLine2(wr.requester.name)} <${wr.requester.email}>`,
|
|
31959
|
+
`Origin: ${wr.origin}`,
|
|
31960
|
+
wr.body ? `Ask: ${oneLine2(wr.body)}` : null,
|
|
31961
|
+
`Size: ${formatSizeComposition(size)}`,
|
|
31962
|
+
...orderLines,
|
|
31963
|
+
wr.createdBy ? `Created By: ${wr.createdBy}` : null,
|
|
31964
|
+
`Created: ${wr.createdAt}`,
|
|
31965
|
+
`Updated: ${wr.updatedAt}`
|
|
31966
|
+
].filter(Boolean).join("\n");
|
|
31967
|
+
return { content: [{ type: "text", text: lines }] };
|
|
31968
|
+
}
|
|
31969
|
+
);
|
|
31970
|
+
server.tool(
|
|
31971
|
+
"create_work_request",
|
|
31972
|
+
"File a Work Request against a Movement \u2014 how work arrives, in the requester's own language, before it is sized or agreed. Splitting it into Work Orders is a separate step (create_work_order).",
|
|
31973
|
+
{
|
|
31974
|
+
movementId: external_exports.string().describe("The Movement (engagement) this request is filed against"),
|
|
31975
|
+
title: external_exports.string().min(1).max(300).describe("Short title for the ask"),
|
|
31976
|
+
body: external_exports.string().max(1e4).optional().describe("The ask as written, in the requester's own words"),
|
|
31977
|
+
requesterName: external_exports.string().min(1).describe("Who is asking \u2014 display name"),
|
|
31978
|
+
requesterEmail: external_exports.string().email().describe("Who is asking \u2014 email"),
|
|
31979
|
+
origin: external_exports.enum(WORK_REQUEST_ORIGIN_ENUM).describe('Where the ask came from: "client" or "internal". One entity covers both \u2014 only the entry point differs.'),
|
|
31980
|
+
createdBy: external_exports.string().optional().describe("Optional creator identity for audit")
|
|
31981
|
+
},
|
|
31982
|
+
async ({ movementId, title, body, requesterName, requesterEmail, origin, createdBy }) => {
|
|
31983
|
+
const wr = await client.createWorkRequest({
|
|
31984
|
+
movementId,
|
|
31985
|
+
title,
|
|
31986
|
+
...body !== void 0 && { body },
|
|
31987
|
+
requester: { name: requesterName, email: requesterEmail },
|
|
31988
|
+
origin,
|
|
31989
|
+
...createdBy !== void 0 && { createdBy }
|
|
31990
|
+
});
|
|
31991
|
+
return {
|
|
31992
|
+
content: [
|
|
31993
|
+
{
|
|
31994
|
+
type: "text",
|
|
31995
|
+
text: `Filed work request: [${wr.workRequestId}] ${wr.title} \u2014 from ${wr.requester.name} (${wr.origin})`
|
|
31996
|
+
}
|
|
31997
|
+
]
|
|
31998
|
+
};
|
|
31999
|
+
}
|
|
32000
|
+
);
|
|
32001
|
+
server.tool(
|
|
32002
|
+
"update_work_request",
|
|
32003
|
+
"Reword a Work Request (title, body, requester). A Request cannot be moved to another Movement, and has no status to set \u2014 its Orders carry the state.",
|
|
32004
|
+
{
|
|
32005
|
+
workRequestId: external_exports.string().describe("The Work Request ID"),
|
|
32006
|
+
title: external_exports.string().min(1).max(300).optional().describe("New title"),
|
|
32007
|
+
body: external_exports.string().max(1e4).nullable().optional().describe("The ask as written. Pass null to clear; omit to leave untouched."),
|
|
32008
|
+
requesterName: external_exports.string().min(1).optional().describe("New requester display name (requires requesterEmail too)"),
|
|
32009
|
+
requesterEmail: external_exports.string().email().optional().describe("New requester email (requires requesterName too)")
|
|
32010
|
+
},
|
|
32011
|
+
async ({ workRequestId, title, body, requesterName, requesterEmail }) => {
|
|
32012
|
+
if (requesterName === void 0 !== (requesterEmail === void 0)) {
|
|
32013
|
+
return {
|
|
32014
|
+
content: [
|
|
32015
|
+
{
|
|
32016
|
+
type: "text",
|
|
32017
|
+
text: "Changing the requester requires both requesterName and requesterEmail."
|
|
32018
|
+
}
|
|
32019
|
+
],
|
|
32020
|
+
isError: true
|
|
32021
|
+
};
|
|
32022
|
+
}
|
|
32023
|
+
if (title === void 0 && body === void 0 && requesterName === void 0) {
|
|
32024
|
+
return {
|
|
32025
|
+
content: [
|
|
32026
|
+
{ type: "text", text: "Nothing to update: pass at least one of title, body, or requester." }
|
|
32027
|
+
],
|
|
32028
|
+
isError: true
|
|
32029
|
+
};
|
|
32030
|
+
}
|
|
32031
|
+
const wr = await client.updateWorkRequest(workRequestId, {
|
|
32032
|
+
...title !== void 0 && { title },
|
|
32033
|
+
...body !== void 0 && { body },
|
|
32034
|
+
...requesterName !== void 0 && requesterEmail !== void 0 && { requester: { name: requesterName, email: requesterEmail } }
|
|
32035
|
+
});
|
|
32036
|
+
if (!wr) {
|
|
32037
|
+
return { content: [{ type: "text", text: `Work request not found: ${workRequestId}` }] };
|
|
32038
|
+
}
|
|
32039
|
+
return {
|
|
32040
|
+
content: [{ type: "text", text: `Updated work request: [${wr.workRequestId}] ${wr.title}` }]
|
|
32041
|
+
};
|
|
32042
|
+
}
|
|
32043
|
+
);
|
|
32044
|
+
}
|
|
32045
|
+
|
|
31582
32046
|
// ../../libs/harmonica-services/src/mcp/tools/workflow-tools.ts
|
|
31583
32047
|
var TASK_STATUSES = ["pending", "running", "completed", "failed"];
|
|
31584
32048
|
function registerWorkflowTools(server, ctx, client) {
|
|
@@ -31622,7 +32086,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
31622
32086
|
limit: external_exports.coerce.number().optional().default(20).describe("Max number of jobs to return")
|
|
31623
32087
|
},
|
|
31624
32088
|
async ({ systemId, status, limit }) => {
|
|
31625
|
-
await
|
|
32089
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
31626
32090
|
const tasks = await client.listSystemTasks(systemId, {
|
|
31627
32091
|
status,
|
|
31628
32092
|
limit
|
|
@@ -31680,7 +32144,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
31680
32144
|
limit: external_exports.coerce.number().optional().default(20).describe("Max number of jobs to return")
|
|
31681
32145
|
},
|
|
31682
32146
|
async ({ systemId, status, limit }) => {
|
|
31683
|
-
await
|
|
32147
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
31684
32148
|
const tasks = await client.listSystemTasks(systemId, {
|
|
31685
32149
|
status,
|
|
31686
32150
|
limit
|
|
@@ -31704,7 +32168,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
31704
32168
|
},
|
|
31705
32169
|
async ({ systemId, beatId }) => {
|
|
31706
32170
|
try {
|
|
31707
|
-
await
|
|
32171
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
31708
32172
|
const task = await client.draftCoda(systemId, beatId);
|
|
31709
32173
|
return {
|
|
31710
32174
|
content: [{
|
|
@@ -31737,7 +32201,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
31737
32201
|
},
|
|
31738
32202
|
async ({ systemId, noteIds }) => {
|
|
31739
32203
|
try {
|
|
31740
|
-
await
|
|
32204
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
31741
32205
|
const task = await client.validateAssumptions(systemId, noteIds);
|
|
31742
32206
|
return {
|
|
31743
32207
|
content: [{
|
|
@@ -31772,7 +32236,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
31772
32236
|
},
|
|
31773
32237
|
async ({ systemId, message, revisionId }) => {
|
|
31774
32238
|
try {
|
|
31775
|
-
await
|
|
32239
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
31776
32240
|
const task = await client.startSession(
|
|
31777
32241
|
systemId,
|
|
31778
32242
|
message,
|
|
@@ -31809,7 +32273,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
31809
32273
|
},
|
|
31810
32274
|
async ({ systemId, revisionIds }) => {
|
|
31811
32275
|
try {
|
|
31812
|
-
await
|
|
32276
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
31813
32277
|
const task = await client.planRevisionBatch(systemId, revisionIds);
|
|
31814
32278
|
return {
|
|
31815
32279
|
content: [{
|
|
@@ -31841,7 +32305,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
31841
32305
|
},
|
|
31842
32306
|
async ({ systemId }) => {
|
|
31843
32307
|
try {
|
|
31844
|
-
await
|
|
32308
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
31845
32309
|
const task = await client.runTriage(systemId);
|
|
31846
32310
|
return {
|
|
31847
32311
|
content: [{
|
|
@@ -31887,7 +32351,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
31887
32351
|
},
|
|
31888
32352
|
async ({ systemId, content, filename, encoding }) => {
|
|
31889
32353
|
try {
|
|
31890
|
-
await
|
|
32354
|
+
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
31891
32355
|
const result = encoding === "base64" ? await client.analyzeBinaryDocument(systemId, content, filename) : await client.analyzeDocument(systemId, content, filename);
|
|
31892
32356
|
return {
|
|
31893
32357
|
content: [{
|
|
@@ -31964,7 +32428,7 @@ var TOOL_PROFILES = Object.freeze({
|
|
|
31964
32428
|
registerTeamspaceTools,
|
|
31965
32429
|
registerMembershipTools,
|
|
31966
32430
|
registerSystemTools,
|
|
31967
|
-
|
|
32431
|
+
registerSystemLifecycleTools,
|
|
31968
32432
|
allow(registerBeatVersionTools, ["relocate_beat_version"])
|
|
31969
32433
|
]),
|
|
31970
32434
|
"onboarding": Object.freeze([
|
|
@@ -32002,7 +32466,7 @@ function registerAllTools(server, ctx, client, profile) {
|
|
|
32002
32466
|
return;
|
|
32003
32467
|
}
|
|
32004
32468
|
registerSystemTools(server, ctx, client);
|
|
32005
|
-
|
|
32469
|
+
registerSystemLifecycleTools(server, ctx, client);
|
|
32006
32470
|
registerMembershipTools(server, ctx, client);
|
|
32007
32471
|
registerBeatTools(server, ctx, client);
|
|
32008
32472
|
registerBeatConvergenceTools(server, ctx, client);
|
|
@@ -32043,6 +32507,8 @@ function registerAllTools(server, ctx, client, profile) {
|
|
|
32043
32507
|
registerAccountTools(server, ctx, client);
|
|
32044
32508
|
registerMovementTools(server, ctx, client);
|
|
32045
32509
|
registerWorkItemTools(server, ctx, client);
|
|
32510
|
+
registerWorkRequestTools(server, ctx, client);
|
|
32511
|
+
registerWorkOrderTools(server, ctx, client);
|
|
32046
32512
|
registerBarTools(server, ctx, client);
|
|
32047
32513
|
registerCadenceScheduleTools(server, ctx, client);
|
|
32048
32514
|
registerPulseReportTools(server, ctx, client);
|
|
@@ -32323,7 +32789,7 @@ function createHttpClient(config2) {
|
|
|
32323
32789
|
return { error: String(err) };
|
|
32324
32790
|
}
|
|
32325
32791
|
},
|
|
32326
|
-
|
|
32792
|
+
transferSystem: async (sourceOrgId, projectId, targetOrgId) => {
|
|
32327
32793
|
try {
|
|
32328
32794
|
const result = await request(
|
|
32329
32795
|
"POST",
|
|
@@ -33451,7 +33917,7 @@ function createHttpClient(config2) {
|
|
|
33451
33917
|
return importResult;
|
|
33452
33918
|
},
|
|
33453
33919
|
// Embedding similarity
|
|
33454
|
-
|
|
33920
|
+
embedSystemEntities: (projectId) => request("POST", `/api/systems/${encodeURIComponent(projectId)}/embeddings/generate`),
|
|
33455
33921
|
findSimilarNotes: async (noteId, projectId, options) => {
|
|
33456
33922
|
const qs = buildSimilarityQS(options);
|
|
33457
33923
|
const res = await request("GET", `/api/systems/${encodeURIComponent(projectId)}/notes/${encodeURIComponent(noteId)}/similar${qs}`);
|
|
@@ -33695,7 +34161,7 @@ function createHttpClient(config2) {
|
|
|
33695
34161
|
},
|
|
33696
34162
|
// WorkItems (WorkItem exposure layer — owned, statused unit of operational work under a Track)
|
|
33697
34163
|
createWorkItem: async (input) => {
|
|
33698
|
-
const { trackId, title, owner, committedEstimateHours, effortSize, valueUnits, costPerValueUnit, createdBy, beatVersionId } = input;
|
|
34164
|
+
const { trackId, title, owner, committedEstimateHours, effortSize, valueUnits, costPerValueUnit, createdBy, beatVersionId, workOrderId } = input;
|
|
33699
34165
|
const body = {
|
|
33700
34166
|
title,
|
|
33701
34167
|
...owner !== void 0 && { owner },
|
|
@@ -33704,7 +34170,8 @@ function createHttpClient(config2) {
|
|
|
33704
34170
|
...valueUnits !== void 0 && { valueUnits },
|
|
33705
34171
|
...costPerValueUnit !== void 0 && { costPerValueUnit },
|
|
33706
34172
|
...createdBy !== void 0 && { createdBy },
|
|
33707
|
-
...beatVersionId !== void 0 && { beatVersionId }
|
|
34173
|
+
...beatVersionId !== void 0 && { beatVersionId },
|
|
34174
|
+
...workOrderId !== void 0 && { workOrderId }
|
|
33708
34175
|
};
|
|
33709
34176
|
const res = await request(
|
|
33710
34177
|
"POST",
|
|
@@ -33766,6 +34233,134 @@ function createHttpClient(config2) {
|
|
|
33766
34233
|
if (!parsed.success) throw new Error(`Unexpected WorkItem shape from API: ${parsed.error.message}`);
|
|
33767
34234
|
return parsed.data;
|
|
33768
34235
|
},
|
|
34236
|
+
// Work intake (B-331) — against the endpoints the API layer shipped in v3.
|
|
34237
|
+
createWorkRequest: async (input) => {
|
|
34238
|
+
const { movementId, title, body: askBody, requester, origin, createdBy } = input;
|
|
34239
|
+
const body = {
|
|
34240
|
+
title,
|
|
34241
|
+
...askBody !== void 0 && { body: askBody },
|
|
34242
|
+
requester,
|
|
34243
|
+
origin,
|
|
34244
|
+
...createdBy !== void 0 && { createdBy }
|
|
34245
|
+
};
|
|
34246
|
+
const res = await request(
|
|
34247
|
+
"POST",
|
|
34248
|
+
`/api/movements/${encodeURIComponent(movementId)}/work-requests`,
|
|
34249
|
+
body
|
|
34250
|
+
);
|
|
34251
|
+
if (res === void 0) throw new Error(`Movement ${movementId} not found`);
|
|
34252
|
+
const parsed = WorkRequestApiSchema.safeParse(res.workRequest);
|
|
34253
|
+
if (!parsed.success) throw new Error(`Unexpected WorkRequest shape from API: ${parsed.error.message}`);
|
|
34254
|
+
return parsed.data;
|
|
34255
|
+
},
|
|
34256
|
+
getWorkRequest: async (workRequestId) => {
|
|
34257
|
+
const res = await request(
|
|
34258
|
+
"GET",
|
|
34259
|
+
`/api/work-requests/${encodeURIComponent(workRequestId)}`
|
|
34260
|
+
);
|
|
34261
|
+
if (res === void 0) return void 0;
|
|
34262
|
+
const parsed = WorkRequestApiSchema.safeParse(res.workRequest);
|
|
34263
|
+
if (!parsed.success) throw new Error(`Unexpected WorkRequest shape from API: ${parsed.error.message}`);
|
|
34264
|
+
return parsed.data;
|
|
34265
|
+
},
|
|
34266
|
+
getWorkRequestDetail: async (workRequestId) => {
|
|
34267
|
+
const res = await request("GET", `/api/work-requests/${encodeURIComponent(workRequestId)}`);
|
|
34268
|
+
if (res === void 0) return void 0;
|
|
34269
|
+
const parsed = WorkRequestDetailResponseSchema.safeParse(res);
|
|
34270
|
+
if (!parsed.success) throw new Error(`Unexpected WorkRequest detail shape from API: ${parsed.error.message}`);
|
|
34271
|
+
return parsed.data;
|
|
34272
|
+
},
|
|
34273
|
+
updateWorkRequest: async (workRequestId, updates) => {
|
|
34274
|
+
const res = await request(
|
|
34275
|
+
"PATCH",
|
|
34276
|
+
`/api/work-requests/${encodeURIComponent(workRequestId)}`,
|
|
34277
|
+
updates
|
|
34278
|
+
);
|
|
34279
|
+
if (res === void 0) return void 0;
|
|
34280
|
+
const parsed = WorkRequestApiSchema.safeParse(res.workRequest);
|
|
34281
|
+
if (!parsed.success) throw new Error(`Unexpected WorkRequest shape from API: ${parsed.error.message}`);
|
|
34282
|
+
return parsed.data;
|
|
34283
|
+
},
|
|
34284
|
+
listMovementWorkRequests: async (movementId) => {
|
|
34285
|
+
const res = await request("GET", `/api/movements/${encodeURIComponent(movementId)}/work-requests`);
|
|
34286
|
+
if (res === void 0) throw new Error(`Movement ${movementId} not found`);
|
|
34287
|
+
const parsed = WorkRequestsResponseSchema.safeParse(res);
|
|
34288
|
+
if (!parsed.success) throw new Error(`Unexpected work requests list shape from API: ${parsed.error.message}`);
|
|
34289
|
+
return parsed.data.workRequests;
|
|
34290
|
+
},
|
|
34291
|
+
createWorkOrder: async (input) => {
|
|
34292
|
+
const { trackId, workRequestId, title, description, size, createdBy } = input;
|
|
34293
|
+
const body = {
|
|
34294
|
+
workRequestId,
|
|
34295
|
+
title,
|
|
34296
|
+
...description !== void 0 && { description },
|
|
34297
|
+
...size !== void 0 && { size },
|
|
34298
|
+
...createdBy !== void 0 && { createdBy }
|
|
34299
|
+
};
|
|
34300
|
+
const res = await request(
|
|
34301
|
+
"POST",
|
|
34302
|
+
`/api/deliverable-groups/${encodeURIComponent(trackId)}/work-orders`,
|
|
34303
|
+
body
|
|
34304
|
+
);
|
|
34305
|
+
if (res === void 0) throw new Error(`Track ${trackId} not found`);
|
|
34306
|
+
const parsed = WorkOrderApiSchema.safeParse(res.workOrder);
|
|
34307
|
+
if (!parsed.success) throw new Error(`Unexpected WorkOrder shape from API: ${parsed.error.message}`);
|
|
34308
|
+
return parsed.data;
|
|
34309
|
+
},
|
|
34310
|
+
getWorkOrder: async (workOrderId) => {
|
|
34311
|
+
const res = await request("GET", `/api/work-orders/${encodeURIComponent(workOrderId)}`);
|
|
34312
|
+
if (res === void 0) return void 0;
|
|
34313
|
+
const parsed = WorkOrderApiSchema.safeParse(res.workOrder);
|
|
34314
|
+
if (!parsed.success) throw new Error(`Unexpected WorkOrder shape from API: ${parsed.error.message}`);
|
|
34315
|
+
return parsed.data;
|
|
34316
|
+
},
|
|
34317
|
+
updateWorkOrder: async (workOrderId, updates) => {
|
|
34318
|
+
const res = await request(
|
|
34319
|
+
"PATCH",
|
|
34320
|
+
`/api/work-orders/${encodeURIComponent(workOrderId)}`,
|
|
34321
|
+
updates
|
|
34322
|
+
);
|
|
34323
|
+
if (res === void 0) return void 0;
|
|
34324
|
+
const parsed = WorkOrderApiSchema.safeParse(res.workOrder);
|
|
34325
|
+
if (!parsed.success) throw new Error(`Unexpected WorkOrder shape from API: ${parsed.error.message}`);
|
|
34326
|
+
return parsed.data;
|
|
34327
|
+
},
|
|
34328
|
+
listTrackWorkOrders: async (trackId) => {
|
|
34329
|
+
const res = await request("GET", `/api/deliverable-groups/${encodeURIComponent(trackId)}/work-orders`);
|
|
34330
|
+
if (res === void 0) throw new Error(`Track ${trackId} not found`);
|
|
34331
|
+
const parsed = WorkOrdersResponseSchema.safeParse(res);
|
|
34332
|
+
if (!parsed.success) throw new Error(`Unexpected work orders list shape from API: ${parsed.error.message}`);
|
|
34333
|
+
return parsed.data.workOrders;
|
|
34334
|
+
},
|
|
34335
|
+
listWorkRequestWorkOrders: async (workRequestId) => {
|
|
34336
|
+
const res = await request("GET", `/api/work-requests/${encodeURIComponent(workRequestId)}/work-orders`);
|
|
34337
|
+
if (res === void 0) throw new Error(`Work request ${workRequestId} not found`);
|
|
34338
|
+
const parsed = WorkOrdersResponseSchema.safeParse(res);
|
|
34339
|
+
if (!parsed.success) throw new Error(`Unexpected work orders list shape from API: ${parsed.error.message}`);
|
|
34340
|
+
return parsed.data.workOrders;
|
|
34341
|
+
},
|
|
34342
|
+
moveWorkOrder: async (workOrderId, newTrackId) => {
|
|
34343
|
+
const res = await request(
|
|
34344
|
+
"POST",
|
|
34345
|
+
`/api/work-orders/${encodeURIComponent(workOrderId)}/move`,
|
|
34346
|
+
{ newTrackId }
|
|
34347
|
+
);
|
|
34348
|
+
if (res === void 0) return void 0;
|
|
34349
|
+
const parsed = WorkOrderApiSchema.safeParse(res.workOrder);
|
|
34350
|
+
if (!parsed.success) throw new Error(`Unexpected WorkOrder shape from API: ${parsed.error.message}`);
|
|
34351
|
+
return parsed.data;
|
|
34352
|
+
},
|
|
34353
|
+
transitionWorkOrderStatus: async (workOrderId, toStatus, reason) => {
|
|
34354
|
+
const res = await request(
|
|
34355
|
+
"POST",
|
|
34356
|
+
`/api/work-orders/${encodeURIComponent(workOrderId)}/status`,
|
|
34357
|
+
{ status: toStatus, ...reason !== void 0 && { reason } }
|
|
34358
|
+
);
|
|
34359
|
+
if (res === void 0) return void 0;
|
|
34360
|
+
const parsed = WorkOrderApiSchema.safeParse(res.workOrder);
|
|
34361
|
+
if (!parsed.success) throw new Error(`Unexpected WorkOrder shape from API: ${parsed.error.message}`);
|
|
34362
|
+
return parsed.data;
|
|
34363
|
+
},
|
|
33769
34364
|
// Accounts (B-242 / org-scoped paying client entity that owns Movements)
|
|
33770
34365
|
createAccount: async (input) => {
|
|
33771
34366
|
const { orgId, name, description, createdBy, parentAccountId, accountType, accountCode } = input;
|
|
@@ -34317,7 +34912,7 @@ function loadConfig() {
|
|
|
34317
34912
|
};
|
|
34318
34913
|
}
|
|
34319
34914
|
async function main() {
|
|
34320
|
-
console.error(`[harmonica-mcp] v${"3.
|
|
34915
|
+
console.error(`[harmonica-mcp] v${"3.9.1"} starting\u2026`);
|
|
34321
34916
|
const config2 = loadConfig();
|
|
34322
34917
|
const client = createHttpClient({
|
|
34323
34918
|
apiBaseUrl: config2.apiBaseUrl,
|