@codazen/harmonica-mcp 4.3.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +666 -645
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21514,44 +21514,22 @@ if (typeof global.crypto === "undefined") {
|
|
|
21514
21514
|
var outgoingEnded = Symbol("outgoingEnded");
|
|
21515
21515
|
|
|
21516
21516
|
// ../../libs/harmonica-services/src/mcp/auth.ts
|
|
21517
|
-
async function
|
|
21518
|
-
const
|
|
21519
|
-
const match = orgs.find((o) => o.slug === slug);
|
|
21520
|
-
if (!match) {
|
|
21521
|
-
throw new Error(
|
|
21522
|
-
`Organization with slug "${slug}" not found. Check HARMONICA_ORG_SLUG in your .mcp.json env config.`
|
|
21523
|
-
);
|
|
21524
|
-
}
|
|
21525
|
-
return match.orgId;
|
|
21526
|
-
}
|
|
21527
|
-
async function validateAuth(client, orgIdentifier, isOrgSlug, userId) {
|
|
21528
|
-
const orgId = isOrgSlug ? await resolveOrgSlug(client, orgIdentifier) : orgIdentifier;
|
|
21529
|
-
const user = await client.getUser(userId);
|
|
21517
|
+
async function validateAuth(client) {
|
|
21518
|
+
const user = await client.getCurrentUser();
|
|
21530
21519
|
if (!user) {
|
|
21531
21520
|
throw new Error(
|
|
21532
|
-
`
|
|
21521
|
+
`Could not resolve an identity for this credential. Check that HARMONICA_API_KEY is a valid Harmonica API key and that the user it authenticates as exists (HARMONICA_USER_ID is your email address).`
|
|
21533
21522
|
);
|
|
21534
21523
|
}
|
|
21535
|
-
|
|
21536
|
-
if (!membership) {
|
|
21537
|
-
throw new Error(
|
|
21538
|
-
`User "${userId}" is not a member of org "${orgId}". Log into Harmonica first to create your membership, or check HARMONICA_ORG_ID / HARMONICA_ORG_SLUG in your .mcp.json.`
|
|
21539
|
-
);
|
|
21540
|
-
}
|
|
21541
|
-
return { user, orgId, membership };
|
|
21524
|
+
return { user };
|
|
21542
21525
|
}
|
|
21543
21526
|
|
|
21544
|
-
// ../../libs/harmonica-services/src/mcp/
|
|
21545
|
-
async function
|
|
21527
|
+
// ../../libs/harmonica-services/src/mcp/entity-guard.ts
|
|
21528
|
+
async function assertSystemExists(client, systemId) {
|
|
21546
21529
|
const system = await client.getSystem(systemId);
|
|
21547
21530
|
if (!system) {
|
|
21548
21531
|
throw new Error(`System not found: "${systemId}"`);
|
|
21549
21532
|
}
|
|
21550
|
-
if (system.orgId !== orgId) {
|
|
21551
|
-
throw new Error(
|
|
21552
|
-
`System "${systemId}" does not belong to the configured organization. Access denied.`
|
|
21553
|
-
);
|
|
21554
|
-
}
|
|
21555
21533
|
}
|
|
21556
21534
|
async function assertRevisionInSystem(client, revisionId, systemId) {
|
|
21557
21535
|
const revision = await client.getRevision(revisionId);
|
|
@@ -21564,96 +21542,60 @@ async function assertRevisionInSystem(client, revisionId, systemId) {
|
|
|
21564
21542
|
);
|
|
21565
21543
|
}
|
|
21566
21544
|
}
|
|
21567
|
-
async function
|
|
21545
|
+
async function assertBeatExists(client, beatId) {
|
|
21568
21546
|
const beat = await client.getBeat(beatId);
|
|
21569
21547
|
if (!beat) {
|
|
21570
21548
|
throw new Error(`Beat not found: "${beatId}"`);
|
|
21571
21549
|
}
|
|
21572
|
-
if (beat.orgId && beat.orgId !== orgId) {
|
|
21573
|
-
throw new Error(
|
|
21574
|
-
`Beat "${beatId}" does not belong to the configured organization. Access denied.`
|
|
21575
|
-
);
|
|
21576
|
-
}
|
|
21577
21550
|
}
|
|
21578
|
-
async function
|
|
21551
|
+
async function fetchSystem(client, systemId) {
|
|
21579
21552
|
const system = await client.getSystem(systemId);
|
|
21580
21553
|
if (!system) {
|
|
21581
21554
|
throw new Error(`System not found: "${systemId}"`);
|
|
21582
21555
|
}
|
|
21583
|
-
if (system.orgId !== orgId) {
|
|
21584
|
-
throw new Error(
|
|
21585
|
-
`System "${systemId}" does not belong to the configured organization. Access denied.`
|
|
21586
|
-
);
|
|
21587
|
-
}
|
|
21588
21556
|
return system;
|
|
21589
21557
|
}
|
|
21590
|
-
async function
|
|
21558
|
+
async function fetchBeat(client, beatId) {
|
|
21591
21559
|
const beat = await client.getBeat(beatId);
|
|
21592
21560
|
if (!beat) {
|
|
21593
21561
|
throw new Error(`Beat not found: "${beatId}"`);
|
|
21594
21562
|
}
|
|
21595
|
-
if (beat.orgId && beat.orgId !== orgId) {
|
|
21596
|
-
throw new Error(
|
|
21597
|
-
`Beat "${beatId}" does not belong to the configured organization. Access denied.`
|
|
21598
|
-
);
|
|
21599
|
-
}
|
|
21600
21563
|
return beat;
|
|
21601
21564
|
}
|
|
21602
|
-
async function
|
|
21565
|
+
async function assertTeamspaceExists(client, teamspaceId) {
|
|
21566
|
+
await fetchTeamspace(client, teamspaceId);
|
|
21567
|
+
}
|
|
21568
|
+
async function fetchTeamspace(client, teamspaceId) {
|
|
21603
21569
|
const teamspace = await client.getTeamspace(teamspaceId);
|
|
21604
21570
|
if (!teamspace) {
|
|
21605
21571
|
throw new Error(`Teamspace not found: "${teamspaceId}"`);
|
|
21606
21572
|
}
|
|
21607
|
-
|
|
21608
|
-
throw new Error(
|
|
21609
|
-
`Teamspace "${teamspaceId}" does not belong to the configured organization. Access denied.`
|
|
21610
|
-
);
|
|
21611
|
-
}
|
|
21573
|
+
return teamspace;
|
|
21612
21574
|
}
|
|
21613
|
-
async function
|
|
21575
|
+
async function assertAccountExists(client, accountId) {
|
|
21614
21576
|
const account = await client.getAccount(accountId);
|
|
21615
21577
|
if (!account) {
|
|
21616
21578
|
throw new Error(`Account not found: "${accountId}"`);
|
|
21617
21579
|
}
|
|
21618
|
-
if (account.orgId !== orgId) {
|
|
21619
|
-
throw new Error(
|
|
21620
|
-
`Account "${accountId}" does not belong to the configured organization. Access denied.`
|
|
21621
|
-
);
|
|
21622
|
-
}
|
|
21623
21580
|
}
|
|
21624
|
-
async function
|
|
21581
|
+
async function assertMovementExists(client, movementId) {
|
|
21625
21582
|
const movement = await client.getMovement(movementId);
|
|
21626
21583
|
if (!movement) {
|
|
21627
21584
|
throw new Error(`Movement not found: "${movementId}"`);
|
|
21628
21585
|
}
|
|
21629
|
-
if (movement.orgId !== orgId) {
|
|
21630
|
-
throw new Error(
|
|
21631
|
-
`Movement "${movementId}" does not belong to the configured organization. Access denied.`
|
|
21632
|
-
);
|
|
21633
|
-
}
|
|
21634
21586
|
}
|
|
21635
|
-
async function
|
|
21587
|
+
async function fetchDeliverableGroup(client, deliverableGroupId) {
|
|
21636
21588
|
const group = await client.getDeliverableGroup(deliverableGroupId);
|
|
21637
21589
|
if (!group) {
|
|
21638
21590
|
throw new Error(`DeliverableGroup not found: "${deliverableGroupId}"`);
|
|
21639
21591
|
}
|
|
21640
|
-
if (group.orgId !== orgId) {
|
|
21641
|
-
throw new Error(
|
|
21642
|
-
`DeliverableGroup "${deliverableGroupId}" does not belong to the configured organization. Access denied.`
|
|
21643
|
-
);
|
|
21644
|
-
}
|
|
21645
21592
|
return group;
|
|
21646
21593
|
}
|
|
21647
|
-
async function
|
|
21594
|
+
async function fetchNotebook(client, notebookId) {
|
|
21648
21595
|
const notebook = await client.getNotebook(notebookId);
|
|
21649
21596
|
if (!notebook) {
|
|
21650
21597
|
throw new Error(`Notebook not found: "${notebookId}"`);
|
|
21651
21598
|
}
|
|
21652
|
-
if (notebook.orgId !== orgId) {
|
|
21653
|
-
throw new Error(
|
|
21654
|
-
`Notebook "${notebookId}" does not belong to the configured organization. Access denied.`
|
|
21655
|
-
);
|
|
21656
|
-
}
|
|
21657
21599
|
return notebook;
|
|
21658
21600
|
}
|
|
21659
21601
|
|
|
@@ -21678,9 +21620,6 @@ function registerAccountTools(server, ctx, client) {
|
|
|
21678
21620
|
orgId: external_exports.string().describe("The organization ID")
|
|
21679
21621
|
},
|
|
21680
21622
|
async ({ orgId }) => {
|
|
21681
|
-
if (orgId !== ctx.orgId) {
|
|
21682
|
-
return { content: [{ type: "text", text: `Org not found: ${orgId}` }] };
|
|
21683
|
-
}
|
|
21684
21623
|
const accounts = await client.listOrgAccounts(orgId);
|
|
21685
21624
|
const text = accounts.length === 0 ? "No accounts found." : accounts.map(
|
|
21686
21625
|
(a) => `[${a.accountId}] ${a.accountCode ?? "\u2014"} ${a.name} (${a.accountType ?? "client"}, ${a.status})`
|
|
@@ -21696,7 +21635,7 @@ function registerAccountTools(server, ctx, client) {
|
|
|
21696
21635
|
},
|
|
21697
21636
|
async ({ accountId }) => {
|
|
21698
21637
|
const a = await client.getAccount(accountId);
|
|
21699
|
-
if (!a
|
|
21638
|
+
if (!a) {
|
|
21700
21639
|
return { content: [{ type: "text", text: `Account not found: ${accountId}` }] };
|
|
21701
21640
|
}
|
|
21702
21641
|
const lines = [
|
|
@@ -21717,8 +21656,9 @@ function registerAccountTools(server, ctx, client) {
|
|
|
21717
21656
|
);
|
|
21718
21657
|
server.tool(
|
|
21719
21658
|
"create_account",
|
|
21720
|
-
`Create an Account in
|
|
21659
|
+
`Create an Account in an Organization. An Account is an org unit that owns Movements \u2014 either a paying client OR the agency itself (accountType). It is NOT a Beat. Pass parentAccountId to nest it under another Account, which may itself be a sub-account \u2014 up to ${MAX_ACCOUNT_DEPTH} levels deep, e.g. Meta \u2192 Reality Labs \u2192 Lifecycle CRM, where the middle level is organisational grouping rather than a separately billed entity. Recognition cues: "new client account", "add a paying client", "create account for Acme Corp", "add a department under this client" \u2192 create_account.`,
|
|
21721
21660
|
{
|
|
21661
|
+
orgId: external_exports.string().describe("The organization ID \u2014 see list_organizations"),
|
|
21722
21662
|
name: external_exports.string().min(1).max(300).describe('Account name (e.g. "Acme Corporation")'),
|
|
21723
21663
|
accountType: external_exports.enum(["internal", "client"]).describe(
|
|
21724
21664
|
"'client' for a paying client, 'internal' for one of the agency's own org units. An org may have several internal Accounts, and an internal Account may be a sub-account of another internal Account \u2014 but never of a client Account."
|
|
@@ -21731,10 +21671,10 @@ function registerAccountTools(server, ctx, client) {
|
|
|
21731
21671
|
`Nests this Account under the given parent, which may itself be a sub-account. Rejected if the result would exceed ${MAX_ACCOUNT_DEPTH} levels.`
|
|
21732
21672
|
)
|
|
21733
21673
|
},
|
|
21734
|
-
async ({ name, accountType, description, accountCode, parentAccountId }) => {
|
|
21674
|
+
async ({ orgId, name, accountType, description, accountCode, parentAccountId }) => {
|
|
21735
21675
|
try {
|
|
21736
21676
|
const a = await client.createAccount({
|
|
21737
|
-
orgId
|
|
21677
|
+
orgId,
|
|
21738
21678
|
name,
|
|
21739
21679
|
accountType,
|
|
21740
21680
|
...description !== void 0 && { description },
|
|
@@ -21773,7 +21713,7 @@ function registerAccountTools(server, ctx, client) {
|
|
|
21773
21713
|
},
|
|
21774
21714
|
async ({ accountId, name, description, accountType, parentAccountId, cadenceConfig }) => {
|
|
21775
21715
|
const existing = await client.getAccount(accountId);
|
|
21776
|
-
if (!existing
|
|
21716
|
+
if (!existing) {
|
|
21777
21717
|
return { content: [{ type: "text", text: `Account not found: ${accountId}` }] };
|
|
21778
21718
|
}
|
|
21779
21719
|
try {
|
|
@@ -21809,7 +21749,7 @@ function registerAccountTools(server, ctx, client) {
|
|
|
21809
21749
|
},
|
|
21810
21750
|
async ({ accountId }) => {
|
|
21811
21751
|
const existing = await client.getAccount(accountId);
|
|
21812
|
-
if (!existing
|
|
21752
|
+
if (!existing) {
|
|
21813
21753
|
return { content: [{ type: "text", text: `Account not found: ${accountId}` }] };
|
|
21814
21754
|
}
|
|
21815
21755
|
const children = await client.listChildAccounts(accountId);
|
|
@@ -21832,7 +21772,7 @@ function registerAccountTools(server, ctx, client) {
|
|
|
21832
21772
|
// published thin client, so gating them costs the tool bundle nothing.
|
|
21833
21773
|
async ({ accountId }) => {
|
|
21834
21774
|
const existing = await client.getAccount(accountId);
|
|
21835
|
-
if (!existing
|
|
21775
|
+
if (!existing) {
|
|
21836
21776
|
return { content: [{ type: "text", text: `Account not found: ${accountId}` }] };
|
|
21837
21777
|
}
|
|
21838
21778
|
const systems = await client.listAccountSystems(accountId);
|
|
@@ -21850,7 +21790,7 @@ function registerAccountTools(server, ctx, client) {
|
|
|
21850
21790
|
// no isEnabled() call here to keep @cdz/feature-flags out of the tool bundle.
|
|
21851
21791
|
async ({ accountId }) => {
|
|
21852
21792
|
const existing = await client.getAccount(accountId);
|
|
21853
|
-
if (!existing
|
|
21793
|
+
if (!existing) {
|
|
21854
21794
|
return { content: [{ type: "text", text: `Account not found: ${accountId}` }] };
|
|
21855
21795
|
}
|
|
21856
21796
|
const documents = await client.listAccountDocuments(accountId);
|
|
@@ -21866,7 +21806,7 @@ function registerAccountTools(server, ctx, client) {
|
|
|
21866
21806
|
},
|
|
21867
21807
|
async ({ accountId }) => {
|
|
21868
21808
|
const existing = await client.getAccount(accountId);
|
|
21869
|
-
if (!existing
|
|
21809
|
+
if (!existing) {
|
|
21870
21810
|
return { content: [{ type: "text", text: `Account not found: ${accountId}` }] };
|
|
21871
21811
|
}
|
|
21872
21812
|
const movements = await client.listAccountMovements(accountId);
|
|
@@ -21930,7 +21870,7 @@ function registerAccountTools(server, ctx, client) {
|
|
|
21930
21870
|
},
|
|
21931
21871
|
async ({ accountId, status }) => {
|
|
21932
21872
|
const existing = await client.getAccount(accountId);
|
|
21933
|
-
if (!existing
|
|
21873
|
+
if (!existing) {
|
|
21934
21874
|
return { content: [{ type: "text", text: `Account not found: ${accountId}` }] };
|
|
21935
21875
|
}
|
|
21936
21876
|
const a = await client.setAccountStatus(accountId, status);
|
|
@@ -21967,7 +21907,7 @@ function registerActivityTools(server, ctx, client) {
|
|
|
21967
21907
|
importance: external_exports.enum(IMPORTANCE_VALUES).optional().describe("Filter by minimum importance level")
|
|
21968
21908
|
},
|
|
21969
21909
|
async ({ systemId, limit, importance }) => {
|
|
21970
|
-
await
|
|
21910
|
+
await assertSystemExists(client, systemId);
|
|
21971
21911
|
const { entries, hasMore } = await client.listSystemActivities(systemId, {
|
|
21972
21912
|
limit: limit ?? 30,
|
|
21973
21913
|
minImportance: importance
|
|
@@ -21999,7 +21939,7 @@ function registerAnalysisTools(server, ctx, client) {
|
|
|
21999
21939
|
minClusterSize: external_exports.coerce.number().int().min(2).optional().default(3).describe("Minimum cluster size. Default 3")
|
|
22000
21940
|
},
|
|
22001
21941
|
async ({ systemId, entityType, threshold, minClusterSize }) => {
|
|
22002
|
-
await
|
|
21942
|
+
await assertSystemExists(client, systemId);
|
|
22003
21943
|
const result = await client.clusterEntities(systemId, entityType, { threshold, minClusterSize });
|
|
22004
21944
|
if (result.clusters.length === 0) {
|
|
22005
21945
|
return {
|
|
@@ -22031,7 +21971,7 @@ Analyzed: ${result.totalAnalyzed} | Clusters: ${result.clusters.length} | Noise:
|
|
|
22031
21971
|
minClusterSize: external_exports.coerce.number().int().min(2).optional().default(2).describe("Minimum cluster size. Default 2")
|
|
22032
21972
|
},
|
|
22033
21973
|
async ({ systemId, analysisType, entityType, threshold, minClusterSize }) => {
|
|
22034
|
-
await
|
|
21974
|
+
await assertSystemExists(client, systemId);
|
|
22035
21975
|
const result = await client.runClusteredAnalysis(systemId, analysisType, {
|
|
22036
21976
|
entityType,
|
|
22037
21977
|
threshold,
|
|
@@ -22066,7 +22006,7 @@ function registerAuditIntegrityTools(server, ctx, client) {
|
|
|
22066
22006
|
},
|
|
22067
22007
|
async ({ systemId, limit }) => {
|
|
22068
22008
|
try {
|
|
22069
|
-
await
|
|
22009
|
+
await assertSystemExists(client, systemId);
|
|
22070
22010
|
const report = await client.verifyAuditIntegrity(systemId, { limit });
|
|
22071
22011
|
const lines = [
|
|
22072
22012
|
`## Audit Integrity Report`,
|
|
@@ -22195,7 +22135,7 @@ function registerBaselineTools(server, ctx, client) {
|
|
|
22195
22135
|
},
|
|
22196
22136
|
async ({ systemId, beatIds, runChecks, localPath }) => {
|
|
22197
22137
|
try {
|
|
22198
|
-
await
|
|
22138
|
+
await assertSystemExists(client, systemId);
|
|
22199
22139
|
const task = await client.baselineSystem(systemId, { beatIds, runChecks, localPath });
|
|
22200
22140
|
return {
|
|
22201
22141
|
content: [{
|
|
@@ -22225,7 +22165,7 @@ function registerBeatComposerTools(server, ctx, client) {
|
|
|
22225
22165
|
},
|
|
22226
22166
|
async ({ survivingBeatId, mergedFromBeatIds, rationale }) => {
|
|
22227
22167
|
try {
|
|
22228
|
-
await
|
|
22168
|
+
await assertBeatExists(client, survivingBeatId);
|
|
22229
22169
|
await client.recordMergeEvent(
|
|
22230
22170
|
survivingBeatId,
|
|
22231
22171
|
mergedFromBeatIds,
|
|
@@ -22253,7 +22193,7 @@ Rationale: ${rationale}`;
|
|
|
22253
22193
|
},
|
|
22254
22194
|
async ({ originalBeatId, splitIntoBeatIds, rationale }) => {
|
|
22255
22195
|
try {
|
|
22256
|
-
await
|
|
22196
|
+
await assertBeatExists(client, originalBeatId);
|
|
22257
22197
|
await client.recordSplitEvent(
|
|
22258
22198
|
originalBeatId,
|
|
22259
22199
|
splitIntoBeatIds,
|
|
@@ -22284,7 +22224,7 @@ Rationale: ${rationale}`;
|
|
|
22284
22224
|
},
|
|
22285
22225
|
async ({ systemId, items, reason }) => {
|
|
22286
22226
|
try {
|
|
22287
|
-
await
|
|
22227
|
+
await assertSystemExists(client, systemId);
|
|
22288
22228
|
const result = await client.bulkReassignRevisions(items, reason, ctx.user.userId);
|
|
22289
22229
|
const lines = [
|
|
22290
22230
|
`# Bulk reassignment complete: ${result.reassigned.length} succeeded, ${result.failed.length} failed`,
|
|
@@ -22323,7 +22263,7 @@ Rationale: ${rationale}`;
|
|
|
22323
22263
|
},
|
|
22324
22264
|
async ({ systemId, revisionId, toBeatId, reason }) => {
|
|
22325
22265
|
try {
|
|
22326
|
-
await
|
|
22266
|
+
await assertSystemExists(client, systemId);
|
|
22327
22267
|
const result = await client.reassignRevision(revisionId, toBeatId, reason, ctx.user.userId);
|
|
22328
22268
|
const text = [
|
|
22329
22269
|
`Revision reassigned successfully.`,
|
|
@@ -22361,7 +22301,7 @@ function registerBeatConvergenceTools(server, ctx, client) {
|
|
|
22361
22301
|
"Read-only convergence-vs-churn report for a Beat \u2014 is it converging on its Coda or churning? Returns net Coda coverage, gross delivered, churn ratio, diminishing-delta trend, confirmed-\u0394f coverage, and an overall signal, computed from the Beat\u2019s confirmed Value Velocity inputs. No writes.",
|
|
22362
22302
|
{ beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
|
|
22363
22303
|
async ({ beatId }) => {
|
|
22364
|
-
const beat = await
|
|
22304
|
+
const beat = await fetchBeat(client, beatId);
|
|
22365
22305
|
const report = await client.getBeatConvergence(beat.systemId, beatId);
|
|
22366
22306
|
if (!report) {
|
|
22367
22307
|
return { content: [{ type: "text", text: `No convergence data for beat "${beatId}".` }], isError: true };
|
|
@@ -22389,7 +22329,7 @@ function registerBeatLifecycleTools(server, ctx, client) {
|
|
|
22389
22329
|
return { content: [{ type: "text", text: `Beat not found: "${beatId}"` }], isError: true };
|
|
22390
22330
|
}
|
|
22391
22331
|
const currentStatus = beat.beatStatus ?? "open";
|
|
22392
|
-
await
|
|
22332
|
+
await assertBeatExists(client, beatId);
|
|
22393
22333
|
const result = await client.updateBeatStatus(beatId, targetStatus, {
|
|
22394
22334
|
actor: { type: "human", id: ctx.user.userId, name: ctx.user.userId },
|
|
22395
22335
|
reason
|
|
@@ -22437,7 +22377,7 @@ function registerBeatPlanningTools(server, ctx, client) {
|
|
|
22437
22377
|
if (!revision) {
|
|
22438
22378
|
return { content: [{ type: "text", text: `Revision not found: ${revisionId}` }], isError: true };
|
|
22439
22379
|
}
|
|
22440
|
-
await
|
|
22380
|
+
await assertBeatExists(client, revision.beatId);
|
|
22441
22381
|
const messages = [
|
|
22442
22382
|
...conversationHistory ?? [],
|
|
22443
22383
|
{ role: "user", content: message }
|
|
@@ -22498,7 +22438,7 @@ async function pollForCheck(client, taskId) {
|
|
|
22498
22438
|
consecutiveErrors = 0;
|
|
22499
22439
|
if (!task) throw new Error(`Task ${taskId} not found`);
|
|
22500
22440
|
if (task.status === "completed") {
|
|
22501
|
-
if (!task.result) throw new Error(`Task ${taskId}
|
|
22441
|
+
if (!task.result) throw new Error(`async:completed:no-result Task ${taskId} succeeded on the async path but the scorecard is not yet readable on the blocking path \u2014 use list_checks or get_job_status to retrieve it.`);
|
|
22502
22442
|
return task.result;
|
|
22503
22443
|
}
|
|
22504
22444
|
if (task.status === "failed") throw new Error(`Quality check failed: ${task.error ?? "unknown error"}`);
|
|
@@ -22530,7 +22470,7 @@ function registerBeatQualityTools(server, ctx, client) {
|
|
|
22530
22470
|
};
|
|
22531
22471
|
const handler = async ({ beatId, systemId, wait }) => {
|
|
22532
22472
|
try {
|
|
22533
|
-
await
|
|
22473
|
+
await assertBeatExists(client, beatId);
|
|
22534
22474
|
const { taskId } = await client.checkBeatQuality(systemId, beatId);
|
|
22535
22475
|
if (!wait) {
|
|
22536
22476
|
return {
|
|
@@ -22580,7 +22520,7 @@ function registerBeatReframeTools(server, ctx, client) {
|
|
|
22580
22520
|
},
|
|
22581
22521
|
async ({ beatId, systemId }) => {
|
|
22582
22522
|
try {
|
|
22583
|
-
await
|
|
22523
|
+
await assertBeatExists(client, beatId);
|
|
22584
22524
|
const result = await client.reframeBeat(systemId, beatId);
|
|
22585
22525
|
const text = formatReframeResult(beatId, result);
|
|
22586
22526
|
return { content: [{ type: "text", text }] };
|
|
@@ -22673,7 +22613,6 @@ var DEFAULT_CONTEXT_NOTE_LIMIT = 200;
|
|
|
22673
22613
|
var MAX_CONTEXT_NOTE_LIMIT = 1e3;
|
|
22674
22614
|
var NOTE_TYPE_LABELS = {
|
|
22675
22615
|
context: "Context",
|
|
22676
|
-
assumption: "Assumptions",
|
|
22677
22616
|
constraint: "Constraints",
|
|
22678
22617
|
guidance: "Guidance",
|
|
22679
22618
|
decision: "Decisions",
|
|
@@ -22808,7 +22747,6 @@ ${revision.description}`);
|
|
|
22808
22747
|
// ../../libs/harmonica-services/src/mcp/formatters/note-formatter.ts
|
|
22809
22748
|
var TYPE_LABELS = {
|
|
22810
22749
|
context: "Context",
|
|
22811
|
-
assumption: "Assumption",
|
|
22812
22750
|
constraint: "Constraint",
|
|
22813
22751
|
guidance: "Guidance",
|
|
22814
22752
|
decision: "Decision",
|
|
@@ -23094,7 +23032,7 @@ function registerBeatTools(server, ctx, client) {
|
|
|
23094
23032
|
includeArchived: external_exports.boolean().optional().default(false).describe("Include archived and deprecated beats (excluded by default)")
|
|
23095
23033
|
},
|
|
23096
23034
|
async ({ systemId, offset = 0, limit = 100, includeArchived }) => {
|
|
23097
|
-
await
|
|
23035
|
+
await assertSystemExists(client, systemId);
|
|
23098
23036
|
let beats = await client.listSystemBeats(systemId);
|
|
23099
23037
|
if (!includeArchived) {
|
|
23100
23038
|
beats = beats.filter((b) => b.beatStatus !== "archived");
|
|
@@ -23116,7 +23054,7 @@ function registerBeatTools(server, ctx, client) {
|
|
|
23116
23054
|
"Get full beat detail including description",
|
|
23117
23055
|
{ beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
|
|
23118
23056
|
async ({ beatId }) => {
|
|
23119
|
-
await
|
|
23057
|
+
await assertBeatExists(client, beatId);
|
|
23120
23058
|
const beat = await client.getBeat(beatId);
|
|
23121
23059
|
if (!beat) {
|
|
23122
23060
|
return { content: [{ type: "text", text: `Beat not found: "${beatId}"` }], isError: true };
|
|
@@ -23133,7 +23071,7 @@ function registerBeatTools(server, ctx, client) {
|
|
|
23133
23071
|
systemId: external_exports.string().optional().describe("System ID (auto-resolved from beat if omitted)")
|
|
23134
23072
|
},
|
|
23135
23073
|
async ({ beatId, systemId }) => {
|
|
23136
|
-
const beat = await
|
|
23074
|
+
const beat = await fetchBeat(client, beatId);
|
|
23137
23075
|
const resolvedProjectId = systemId ?? beat.systemId;
|
|
23138
23076
|
const timeline = await client.getUnifiedBeatTimeline(resolvedProjectId, beatId);
|
|
23139
23077
|
if (timeline.length === 0) {
|
|
@@ -23160,12 +23098,12 @@ function registerBeatTools(server, ctx, client) {
|
|
|
23160
23098
|
},
|
|
23161
23099
|
async ({ systemId, title, description, tags, priority, estimatedEffort, beatId, humanAssignee, agentAssignee, branch }) => {
|
|
23162
23100
|
try {
|
|
23163
|
-
await
|
|
23101
|
+
const system = await fetchSystem(client, systemId);
|
|
23164
23102
|
const resolvedBeatId = beatId ?? await client.getNextBeatId(systemId);
|
|
23165
23103
|
const beat = await client.createBeat({
|
|
23166
23104
|
beatId: resolvedBeatId,
|
|
23167
23105
|
systemId,
|
|
23168
|
-
orgId:
|
|
23106
|
+
orgId: system.orgId,
|
|
23169
23107
|
title,
|
|
23170
23108
|
description,
|
|
23171
23109
|
tags,
|
|
@@ -23234,7 +23172,7 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
|
|
|
23234
23172
|
async ({ beatId, changeSummary, ...changes }) => {
|
|
23235
23173
|
try {
|
|
23236
23174
|
console.error("[MCP:UpdateBeat] beatId=%s actor=%s", beatId, ctx.user.userId);
|
|
23237
|
-
const beat = await
|
|
23175
|
+
const beat = await fetchBeat(client, beatId);
|
|
23238
23176
|
const updates = {};
|
|
23239
23177
|
for (const [k, v] of Object.entries(changes)) {
|
|
23240
23178
|
if (v !== void 0) updates[k] = v;
|
|
@@ -23277,7 +23215,7 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
|
|
|
23277
23215
|
},
|
|
23278
23216
|
async ({ systemId, beatId, status, includeArchived }) => {
|
|
23279
23217
|
try {
|
|
23280
|
-
await
|
|
23218
|
+
await assertSystemExists(client, systemId);
|
|
23281
23219
|
const revisions = beatId ? await client.listBeatRevisions(systemId, beatId, { status, includeArchived }) : await client.listSystemRevisions(systemId, { status, includeArchived });
|
|
23282
23220
|
return { content: [{ type: "text", text: formatRevisionSummaryTable(revisions) }] };
|
|
23283
23221
|
} catch (err) {
|
|
@@ -23298,7 +23236,7 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
|
|
|
23298
23236
|
if (!revision) {
|
|
23299
23237
|
return { content: [{ type: "text", text: `Revision not found: ${revisionId}` }], isError: true };
|
|
23300
23238
|
}
|
|
23301
|
-
await
|
|
23239
|
+
await assertBeatExists(client, revision.beatId);
|
|
23302
23240
|
const resolution = await client.resolveDeliveryPolicy(revisionId);
|
|
23303
23241
|
const policyBlock = `
|
|
23304
23242
|
|
|
@@ -23383,6 +23321,11 @@ var PlanVersionsIdempotencyError = class extends Error {
|
|
|
23383
23321
|
this.existingBeatVersionIds = existingBeatVersionIds;
|
|
23384
23322
|
}
|
|
23385
23323
|
};
|
|
23324
|
+
function isRenderableGateFailure(value) {
|
|
23325
|
+
if (typeof value !== "object" || value === null) return false;
|
|
23326
|
+
const f = value;
|
|
23327
|
+
return typeof f.reason === "string" && typeof f.message === "string";
|
|
23328
|
+
}
|
|
23386
23329
|
|
|
23387
23330
|
// ../../libs/harmonica-services/src/mcp/tools/beat-version-tools.ts
|
|
23388
23331
|
function registerBeatVersionTools(server, ctx, client) {
|
|
@@ -23466,7 +23409,7 @@ function registerBeatVersionTools(server, ctx, client) {
|
|
|
23466
23409
|
if (!beat) {
|
|
23467
23410
|
return { content: [{ type: "text", text: `Beat not found: "${beatId}"` }], isError: true };
|
|
23468
23411
|
}
|
|
23469
|
-
await
|
|
23412
|
+
await assertBeatExists(client, beatId);
|
|
23470
23413
|
const beatVersion = await client.createBeatVersion(beat.systemId, beatId, {
|
|
23471
23414
|
title,
|
|
23472
23415
|
description,
|
|
@@ -23505,6 +23448,22 @@ function registerBeatVersionTools(server, ctx, client) {
|
|
|
23505
23448
|
];
|
|
23506
23449
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
23507
23450
|
} catch (err) {
|
|
23451
|
+
if (err instanceof BeatQualityGateError && isRenderableGateFailure(err.failure)) {
|
|
23452
|
+
const f = err.failure;
|
|
23453
|
+
const isStale = f.reason === "stale_check_hash" || f.reason === "stale_check_timestamp";
|
|
23454
|
+
const lines = [
|
|
23455
|
+
`**Gate:** coda_quality`,
|
|
23456
|
+
`**Status:** ${isStale ? "stale" : "failed"} (${f.reason})`,
|
|
23457
|
+
`**Detail:** ${f.message}`
|
|
23458
|
+
];
|
|
23459
|
+
if (f.currentScore !== void 0) lines.push(`**Current:** ${f.currentScore}`);
|
|
23460
|
+
if (f.requiredScore !== void 0) lines.push(`**Required:** ${f.requiredScore}`);
|
|
23461
|
+
if (f.remedy) lines.push(`**Action:** ${f.remedy}`);
|
|
23462
|
+
return {
|
|
23463
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
23464
|
+
isError: true
|
|
23465
|
+
};
|
|
23466
|
+
}
|
|
23508
23467
|
const message = err instanceof Error ? err.message : String(err);
|
|
23509
23468
|
return { content: [{ type: "text", text: `Failed to create Beat Version: ${message}` }], isError: true };
|
|
23510
23469
|
}
|
|
@@ -23531,7 +23490,7 @@ function registerBeatVersionTools(server, ctx, client) {
|
|
|
23531
23490
|
if (!bv) {
|
|
23532
23491
|
return { content: [{ type: "text", text: `Beat Version not found: "${beatVersionId}"` }], isError: true };
|
|
23533
23492
|
}
|
|
23534
|
-
await
|
|
23493
|
+
await assertBeatExists(client, bv.beatId);
|
|
23535
23494
|
const updates = {};
|
|
23536
23495
|
for (const [k, v] of Object.entries(changes)) {
|
|
23537
23496
|
if (v !== void 0) updates[k] = v;
|
|
@@ -23572,7 +23531,7 @@ function registerBeatVersionTools(server, ctx, client) {
|
|
|
23572
23531
|
},
|
|
23573
23532
|
async ({ beatId, force }) => {
|
|
23574
23533
|
try {
|
|
23575
|
-
await
|
|
23534
|
+
await assertBeatExists(client, beatId);
|
|
23576
23535
|
const actorId = ctx.user.email ?? ctx.user.userId;
|
|
23577
23536
|
const beatVersions = await client.planBeatVersions(beatId, actorId, { force });
|
|
23578
23537
|
const lines = [
|
|
@@ -23635,7 +23594,7 @@ function registerBeatVersionTools(server, ctx, client) {
|
|
|
23635
23594
|
if (bv.status === targetState) {
|
|
23636
23595
|
return { content: [{ type: "text", text: `Beat Version is already at ${targetState}.` }], isError: true };
|
|
23637
23596
|
}
|
|
23638
|
-
await
|
|
23597
|
+
await assertBeatExists(client, bv.beatId);
|
|
23639
23598
|
const result = await client.transitionBeatVersionStatus(beatVersionId, targetState, {
|
|
23640
23599
|
actor: { type: "human", id: ctx.user.userId, name: ctx.user.name, email: ctx.user.email },
|
|
23641
23600
|
trigger: "human_action",
|
|
@@ -23677,7 +23636,7 @@ function registerBeatVersionTools(server, ctx, client) {
|
|
|
23677
23636
|
if (!bv) {
|
|
23678
23637
|
return { content: [{ type: "text", text: `Beat Version not found: ${beatVersionId}` }], isError: true };
|
|
23679
23638
|
}
|
|
23680
|
-
await
|
|
23639
|
+
await assertBeatExists(client, bv.beatId);
|
|
23681
23640
|
if (!confirm) {
|
|
23682
23641
|
const revisions = bv.computedStatus ? `${bv.computedStatus.totalRevisions} child Revision(s)` : "child Revisions (count unknown until execution)";
|
|
23683
23642
|
const dropNote = bv.targetDropId ? ` Beat Version is assigned to Drop ${bv.targetDropId} \u2014 Drop membership is preserved.` : "";
|
|
@@ -23963,7 +23922,7 @@ function registerCheckTools(server, ctx, client) {
|
|
|
23963
23922
|
},
|
|
23964
23923
|
async ({ checkType, targetId, systemId, branch, localPath }) => {
|
|
23965
23924
|
try {
|
|
23966
|
-
await
|
|
23925
|
+
await assertSystemExists(client, systemId);
|
|
23967
23926
|
const targetError = checkType === "build_quality" ? buildQualityTargetError(targetId) : checkType === "beat_version_quality" || checkType === "plan_quality" ? beatVersionQualityTargetError(targetId) : null;
|
|
23968
23927
|
if (targetError) {
|
|
23969
23928
|
return { content: [{ type: "text", text: targetError }], isError: true };
|
|
@@ -24061,7 +24020,7 @@ function registerClaimTools(server, ctx, client) {
|
|
|
24061
24020
|
if (!revision) {
|
|
24062
24021
|
return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
|
|
24063
24022
|
}
|
|
24064
|
-
await
|
|
24023
|
+
await assertBeatExists(client, revision.beatId);
|
|
24065
24024
|
const result = await client.claimRevision(revision.systemId, revisionId);
|
|
24066
24025
|
if (!result.success) {
|
|
24067
24026
|
return { content: [{ type: "text", text: `Failed to claim revision (${result.error.code}): ${result.error.message}` }], isError: true };
|
|
@@ -24095,7 +24054,7 @@ function registerClaimTools(server, ctx, client) {
|
|
|
24095
24054
|
if (!revision) {
|
|
24096
24055
|
return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
|
|
24097
24056
|
}
|
|
24098
|
-
await
|
|
24057
|
+
await assertBeatExists(client, revision.beatId);
|
|
24099
24058
|
const result = await client.releaseRevisionClaim(revision.systemId, revisionId, reason);
|
|
24100
24059
|
if (!result.success) {
|
|
24101
24060
|
return { content: [{ type: "text", text: `Failed to release claim (${result.error.code}): ${result.error.message}` }], isError: true };
|
|
@@ -24141,7 +24100,7 @@ function registerDeliverableGroupTools(server, ctx, client) {
|
|
|
24141
24100
|
movementId: external_exports.string().describe("The Movement ID (e.g., mv-abc123)")
|
|
24142
24101
|
},
|
|
24143
24102
|
async ({ movementId }) => {
|
|
24144
|
-
await
|
|
24103
|
+
await assertMovementExists(client, movementId);
|
|
24145
24104
|
const groups = await client.listMovementDeliverableGroups(movementId);
|
|
24146
24105
|
const text = groups.length === 0 ? "No deliverable groups found for this Movement." : groups.map(
|
|
24147
24106
|
(g) => `[${g.deliverableGroupId}] ${g.groupCode} \u2014 ${g.title} (${g.status})`
|
|
@@ -24162,11 +24121,6 @@ function registerDeliverableGroupTools(server, ctx, client) {
|
|
|
24162
24121
|
content: [{ type: "text", text: `DeliverableGroup not found: ${deliverableGroupId}` }]
|
|
24163
24122
|
};
|
|
24164
24123
|
}
|
|
24165
|
-
if (g.orgId !== ctx.orgId) {
|
|
24166
|
-
throw new Error(
|
|
24167
|
-
`DeliverableGroup "${deliverableGroupId}" does not belong to the configured organization. Access denied.`
|
|
24168
|
-
);
|
|
24169
|
-
}
|
|
24170
24124
|
const lines = [
|
|
24171
24125
|
`ID: ${g.deliverableGroupId}`,
|
|
24172
24126
|
`Code: ${g.groupCode}`,
|
|
@@ -24201,16 +24155,16 @@ ${g.acceptanceCriteria}` : null,
|
|
|
24201
24155
|
parentGroupId: external_exports.string().optional().describe("Optional parent Track ID for nested grouping (Phase 0 scaffolding)")
|
|
24202
24156
|
};
|
|
24203
24157
|
const createTrackHandler = async ({ teamspaceId, title, description, deliverables, acceptanceCriteria, vuRate, systemId, movementId, parentGroupId }) => {
|
|
24204
|
-
await
|
|
24158
|
+
const teamspace = await fetchTeamspace(client, teamspaceId);
|
|
24205
24159
|
if (movementId !== void 0) {
|
|
24206
|
-
await
|
|
24160
|
+
await assertMovementExists(client, movementId);
|
|
24207
24161
|
}
|
|
24208
24162
|
if (parentGroupId !== void 0) {
|
|
24209
|
-
await
|
|
24163
|
+
await fetchDeliverableGroup(client, parentGroupId);
|
|
24210
24164
|
}
|
|
24211
24165
|
const g = await client.createDeliverableGroup({
|
|
24212
24166
|
teamspaceId,
|
|
24213
|
-
orgId:
|
|
24167
|
+
orgId: teamspace.orgId,
|
|
24214
24168
|
title,
|
|
24215
24169
|
...description !== void 0 && { description },
|
|
24216
24170
|
...deliverables !== void 0 && { deliverables },
|
|
@@ -24340,12 +24294,12 @@ ${g.acceptanceCriteria}` : null,
|
|
|
24340
24294
|
dropId: external_exports.string().describe("The Drop ID to link to the group")
|
|
24341
24295
|
},
|
|
24342
24296
|
async ({ deliverableGroupId, dropId }) => {
|
|
24343
|
-
await
|
|
24297
|
+
await fetchDeliverableGroup(client, deliverableGroupId);
|
|
24344
24298
|
const drop = await client.getDrop(dropId);
|
|
24345
24299
|
if (!drop) {
|
|
24346
24300
|
return { content: [{ type: "text", text: `Drop not found: ${dropId}` }] };
|
|
24347
24301
|
}
|
|
24348
|
-
await
|
|
24302
|
+
await assertAccountExists(client, drop.accountId);
|
|
24349
24303
|
const ok = await client.setDropDeliverableGroup(deliverableGroupId, dropId);
|
|
24350
24304
|
if (!ok) {
|
|
24351
24305
|
return { content: [{ type: "text", text: `Drop not found: ${dropId}` }] };
|
|
@@ -24362,7 +24316,7 @@ ${g.acceptanceCriteria}` : null,
|
|
|
24362
24316
|
deliverableGroupId: external_exports.string().describe("The DeliverableGroup ID")
|
|
24363
24317
|
},
|
|
24364
24318
|
async ({ deliverableGroupId }) => {
|
|
24365
|
-
await
|
|
24319
|
+
await fetchDeliverableGroup(client, deliverableGroupId);
|
|
24366
24320
|
const workItems = await client.listTrackWorkItems(deliverableGroupId);
|
|
24367
24321
|
const bvIds = [...new Set(
|
|
24368
24322
|
workItems.map((wi) => wi.beatVersionId).filter((id) => id !== void 0)
|
|
@@ -24438,7 +24392,7 @@ function registerDownbeatTools(server, ctx, client) {
|
|
|
24438
24392
|
"Set a Downbeat on a Beat \u2014 a non-destructive recalibration anchor that freezes the current convergence reading and Coda content-hash. Future progress is measured from here without erasing prior increments. Returns the created anchor. A Beat with zero increments can still be anchored.",
|
|
24439
24393
|
{ beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
|
|
24440
24394
|
async ({ beatId }) => {
|
|
24441
|
-
const beat = await
|
|
24395
|
+
const beat = await fetchBeat(client, beatId);
|
|
24442
24396
|
const downbeat = await client.setDownbeat(beat.systemId, beatId);
|
|
24443
24397
|
if (!downbeat) {
|
|
24444
24398
|
return { content: [{ type: "text", text: `Could not set a Downbeat for beat "${beatId}".` }], isError: true };
|
|
@@ -24451,7 +24405,7 @@ function registerDownbeatTools(server, ctx, client) {
|
|
|
24451
24405
|
"Read-only: the Beat Versions delivered since the latest Downbeat (the non-destructive default view window). When no Downbeat has been set, returns every increment.",
|
|
24452
24406
|
{ beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
|
|
24453
24407
|
async ({ beatId }) => {
|
|
24454
|
-
const beat = await
|
|
24408
|
+
const beat = await fetchBeat(client, beatId);
|
|
24455
24409
|
const result = await client.listIncrementsSinceDownbeat(beat.systemId, beatId);
|
|
24456
24410
|
if (!result) {
|
|
24457
24411
|
return { content: [{ type: "text", text: `No such beat "${beatId}".` }], isError: true };
|
|
@@ -24464,7 +24418,7 @@ function registerDownbeatTools(server, ctx, client) {
|
|
|
24464
24418
|
"Read-only: whether the Beat\u2019s Coda has changed since the latest Downbeat was set (the goal moved). Reports hasDownbeat=false when the Beat has never been recalibrated.",
|
|
24465
24419
|
{ beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
|
|
24466
24420
|
async ({ beatId }) => {
|
|
24467
|
-
const beat = await
|
|
24421
|
+
const beat = await fetchBeat(client, beatId);
|
|
24468
24422
|
const report = await client.getCodaDrift(beat.systemId, beatId);
|
|
24469
24423
|
if (!report) {
|
|
24470
24424
|
return { content: [{ type: "text", text: `No such beat "${beatId}".` }], isError: true };
|
|
@@ -24477,7 +24431,7 @@ function registerDownbeatTools(server, ctx, client) {
|
|
|
24477
24431
|
"Read-only: whether sibling Beats created since the latest Downbeat leave this Beat coherent with what got built around it \u2014 a drift signal edited-goal content-hash comparison (get_coda_drift) cannot catch, since this Beat's own text may be unchanged. Reports hasDownbeat=false when the Beat has never been recalibrated.",
|
|
24478
24432
|
{ beatId: external_exports.string().describe("The beat ID (e.g., TF-B-001)") },
|
|
24479
24433
|
async ({ beatId }) => {
|
|
24480
|
-
const beat = await
|
|
24434
|
+
const beat = await fetchBeat(client, beatId);
|
|
24481
24435
|
const report = await client.getRelationalDrift(beat.systemId, beatId);
|
|
24482
24436
|
if (!report) {
|
|
24483
24437
|
return { content: [{ type: "text", text: `No such beat "${beatId}".` }], isError: true };
|
|
@@ -24552,7 +24506,7 @@ function registerDropQualityTools(server, ctx, client) {
|
|
|
24552
24506
|
},
|
|
24553
24507
|
async ({ dropId, systemId, wait }) => {
|
|
24554
24508
|
try {
|
|
24555
|
-
await
|
|
24509
|
+
await assertSystemExists(client, systemId);
|
|
24556
24510
|
const drop = await client.getDrop(dropId);
|
|
24557
24511
|
if (!drop) {
|
|
24558
24512
|
return { content: [{ type: "text", text: `Drop "${dropId}" not found.` }], isError: true };
|
|
@@ -24946,7 +24900,7 @@ function registerEmbeddingTools(server, ctx, client) {
|
|
|
24946
24900
|
systemId: external_exports.string().describe("The system ID to embed entities for")
|
|
24947
24901
|
},
|
|
24948
24902
|
async ({ systemId }) => {
|
|
24949
|
-
await
|
|
24903
|
+
await assertSystemExists(client, systemId);
|
|
24950
24904
|
const result = await client.embedSystemEntities(systemId);
|
|
24951
24905
|
const text = `## Embedding Results
|
|
24952
24906
|
|
|
@@ -24958,33 +24912,33 @@ function registerEmbeddingTools(server, ctx, client) {
|
|
|
24958
24912
|
);
|
|
24959
24913
|
server.tool(
|
|
24960
24914
|
"search",
|
|
24961
|
-
"Free-text semantic search across
|
|
24915
|
+
"Free-text semantic search across system, teamspace, or org scope. Provide exactly one of systemId / teamspaceId / orgId. Teamspace and org searches fan out across child projects and merge by score. Cross-project results carry the source project name.",
|
|
24962
24916
|
{
|
|
24963
24917
|
systemId: external_exports.string().optional().describe("Search within a single project"),
|
|
24964
24918
|
teamspaceId: external_exports.string().optional().describe("Search across all projects in a teamspace"),
|
|
24965
|
-
|
|
24919
|
+
orgId: external_exports.string().optional().describe("Search across every system in this organization \u2014 see list_organizations"),
|
|
24966
24920
|
query: external_exports.string().describe("Natural language search query"),
|
|
24967
24921
|
limit: external_exports.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results to return. Default 10"),
|
|
24968
24922
|
includeArchived: external_exports.boolean().optional().default(false).describe("Include archived/deprecated entities in results (excluded by default)"),
|
|
24969
24923
|
entityType: external_exports.enum(["note", "beat", "revision"]).optional().describe("Restrict results to a specific entity type"),
|
|
24970
|
-
noteType: external_exports.enum(["context", "
|
|
24924
|
+
noteType: external_exports.enum(["context", "constraint", "guidance", "decision", "document"]).optional().describe("Restrict results to a specific note type (only applies when entityType is note or unset). Requires embeddings generated after this filter was introduced."),
|
|
24971
24925
|
noteStatus: external_exports.enum(["active", "unvalidated", "validated", "invalidated", "inProgress", "resolved", "superseded", "dismissed"]).optional().describe("Restrict note results to a specific status (e.g. unvalidated). DynamoDB post-filter \u2014 works without re-embedding."),
|
|
24972
24926
|
beatId: external_exports.string().optional().describe("Restrict note results to those belonging to a specific beat (DynamoDB post-filter, no re-embedding needed)")
|
|
24973
24927
|
},
|
|
24974
|
-
async ({ systemId, teamspaceId,
|
|
24975
|
-
const provided = [systemId, teamspaceId,
|
|
24928
|
+
async ({ systemId, teamspaceId, orgId, query, limit, includeArchived, entityType, noteType, noteStatus, beatId }) => {
|
|
24929
|
+
const provided = [systemId, teamspaceId, orgId].filter(Boolean).length;
|
|
24976
24930
|
if (provided !== 1) {
|
|
24977
|
-
throw new Error("Specify exactly one of systemId, teamspaceId, or
|
|
24931
|
+
throw new Error("Specify exactly one of systemId, teamspaceId, or orgId.");
|
|
24978
24932
|
}
|
|
24979
24933
|
let scope;
|
|
24980
24934
|
if (systemId) {
|
|
24981
|
-
await
|
|
24935
|
+
await assertSystemExists(client, systemId);
|
|
24982
24936
|
scope = { systemId };
|
|
24983
24937
|
} else if (teamspaceId) {
|
|
24984
|
-
await
|
|
24938
|
+
await assertTeamspaceExists(client, teamspaceId);
|
|
24985
24939
|
scope = { teamspaceId };
|
|
24986
24940
|
} else {
|
|
24987
|
-
scope = { orgId
|
|
24941
|
+
scope = { orgId };
|
|
24988
24942
|
}
|
|
24989
24943
|
const results = await client.searchByText(scope, query, { limit, includeArchived, entityType, noteType, noteStatus, beatId });
|
|
24990
24944
|
const text = formatSimilarityResults(results, query, "Search Results");
|
|
@@ -25004,7 +24958,7 @@ function registerEmbeddingTools(server, ctx, client) {
|
|
|
25004
24958
|
beatId: external_exports.string().optional().describe("Restrict results to notes belonging to a specific beat (DynamoDB post-filter)")
|
|
25005
24959
|
},
|
|
25006
24960
|
async ({ noteId, systemId, threshold, limit, includeArchived = false, noteStatus, beatId }) => {
|
|
25007
|
-
await
|
|
24961
|
+
await assertSystemExists(client, systemId);
|
|
25008
24962
|
const ids = Array.isArray(noteId) ? noteId : [noteId];
|
|
25009
24963
|
if (ids.length === 1) {
|
|
25010
24964
|
const results = await client.findSimilarNotes(ids[0], systemId, { threshold, limit, includeArchived, noteStatus, beatId });
|
|
@@ -25029,7 +24983,7 @@ function registerEmbeddingTools(server, ctx, client) {
|
|
|
25029
24983
|
includeArchived: external_exports.boolean().optional().default(false).describe("Include archived/deprecated beats in results (excluded by default)")
|
|
25030
24984
|
},
|
|
25031
24985
|
async ({ beatId, systemId, threshold, limit, includeArchived = false }) => {
|
|
25032
|
-
await
|
|
24986
|
+
await assertBeatExists(client, beatId);
|
|
25033
24987
|
const results = await client.findSimilarBeats(beatId, systemId, { threshold, limit, includeArchived });
|
|
25034
24988
|
const text = formatSimilarityResults(results, beatId, "Similar Beats");
|
|
25035
24989
|
return { content: [{ type: "text", text }] };
|
|
@@ -25045,7 +24999,7 @@ function registerEmbeddingTools(server, ctx, client) {
|
|
|
25045
24999
|
limit: external_exports.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results to return. Default 10")
|
|
25046
25000
|
},
|
|
25047
25001
|
async ({ noteId, systemId, threshold, limit }) => {
|
|
25048
|
-
await
|
|
25002
|
+
await assertSystemExists(client, systemId);
|
|
25049
25003
|
const results = await client.findRelatedBeats(noteId, systemId, { threshold, limit });
|
|
25050
25004
|
const text = formatSimilarityResults(results, noteId, "Related Beats");
|
|
25051
25005
|
return { content: [{ type: "text", text }] };
|
|
@@ -25061,7 +25015,7 @@ function registerEmbeddingTools(server, ctx, client) {
|
|
|
25061
25015
|
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.")
|
|
25062
25016
|
},
|
|
25063
25017
|
async ({ systemId, threshold, minClusterSize, includeLinked }) => {
|
|
25064
|
-
await
|
|
25018
|
+
await assertSystemExists(client, systemId);
|
|
25065
25019
|
const result = await client.clusterNotes(systemId, { threshold, minClusterSize, excludeAlreadyLinked: !includeLinked });
|
|
25066
25020
|
if (result.clusters.length === 0) {
|
|
25067
25021
|
return { content: [{ type: "text", text: `No clusters found among ${result.totalAnalyzed} analyzed notes. Try lowering the threshold.` }] };
|
|
@@ -25092,7 +25046,7 @@ ${members}`;
|
|
|
25092
25046
|
includeLinked: external_exports.coerce.boolean().optional().default(false).describe("Include notes that already have dependsOnNotes links. Default false.")
|
|
25093
25047
|
},
|
|
25094
25048
|
async ({ systemId, threshold, minClusterSize, includeLinked }) => {
|
|
25095
|
-
await
|
|
25049
|
+
await assertSystemExists(client, systemId);
|
|
25096
25050
|
const result = await client.consolidateNotes(systemId, { threshold, minClusterSize, includeLinked });
|
|
25097
25051
|
if (result.proposals.length === 0) {
|
|
25098
25052
|
return { content: [{ type: "text", text: `No consolidation candidates found among ${result.totalAnalyzed} analyzed notes.` }] };
|
|
@@ -25333,7 +25287,7 @@ function registerLayerTools(server, ctx, client) {
|
|
|
25333
25287
|
},
|
|
25334
25288
|
async ({ systemId }) => {
|
|
25335
25289
|
try {
|
|
25336
|
-
await
|
|
25290
|
+
await assertSystemExists(client, systemId);
|
|
25337
25291
|
} catch (err) {
|
|
25338
25292
|
const message = err instanceof Error ? err.message : String(err);
|
|
25339
25293
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -25358,7 +25312,7 @@ function registerLayerTools(server, ctx, client) {
|
|
|
25358
25312
|
},
|
|
25359
25313
|
async ({ systemId, layerId }) => {
|
|
25360
25314
|
try {
|
|
25361
|
-
await
|
|
25315
|
+
await assertSystemExists(client, systemId);
|
|
25362
25316
|
} catch (err) {
|
|
25363
25317
|
const message = err instanceof Error ? err.message : String(err);
|
|
25364
25318
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -25393,7 +25347,7 @@ function registerLayerTools(server, ctx, client) {
|
|
|
25393
25347
|
},
|
|
25394
25348
|
async ({ systemId, layerKey, name, description, parentLayerId, repoPatterns }) => {
|
|
25395
25349
|
try {
|
|
25396
|
-
await
|
|
25350
|
+
await assertSystemExists(client, systemId);
|
|
25397
25351
|
} catch (err) {
|
|
25398
25352
|
const message = err instanceof Error ? err.message : String(err);
|
|
25399
25353
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -25432,7 +25386,7 @@ ${formatLayerDetail(layer)}` }] };
|
|
|
25432
25386
|
},
|
|
25433
25387
|
async ({ systemId, layerId, name, description, repoPatterns }) => {
|
|
25434
25388
|
try {
|
|
25435
|
-
await
|
|
25389
|
+
await assertSystemExists(client, systemId);
|
|
25436
25390
|
} catch (err) {
|
|
25437
25391
|
const message = err instanceof Error ? err.message : String(err);
|
|
25438
25392
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -25476,7 +25430,7 @@ ${formatLayerDetail(layer)}` }] };
|
|
|
25476
25430
|
},
|
|
25477
25431
|
async ({ systemId, layerId, status, reason }) => {
|
|
25478
25432
|
try {
|
|
25479
|
-
await
|
|
25433
|
+
await assertSystemExists(client, systemId);
|
|
25480
25434
|
} catch (err) {
|
|
25481
25435
|
const message = err instanceof Error ? err.message : String(err);
|
|
25482
25436
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -25508,14 +25462,17 @@ ${formatLayerDetail(layer)}` }] };
|
|
|
25508
25462
|
}
|
|
25509
25463
|
|
|
25510
25464
|
// ../../libs/harmonica-services/src/mcp/tools/membership-tools.ts
|
|
25465
|
+
var orgIdArg = external_exports.string().describe("The organization ID \u2014 see list_organizations");
|
|
25511
25466
|
function registerMembershipTools(server, ctx, client) {
|
|
25512
25467
|
server.tool(
|
|
25513
25468
|
"list_org_members",
|
|
25514
|
-
"List all members of
|
|
25515
|
-
{
|
|
25516
|
-
|
|
25469
|
+
"List all members of an organization",
|
|
25470
|
+
{
|
|
25471
|
+
orgId: orgIdArg
|
|
25472
|
+
},
|
|
25473
|
+
async ({ orgId }) => {
|
|
25517
25474
|
try {
|
|
25518
|
-
const members = await client.listOrgMembers(
|
|
25475
|
+
const members = await client.listOrgMembers(orgId);
|
|
25519
25476
|
if (members.length === 0) {
|
|
25520
25477
|
return { content: [{ type: "text", text: "No members found." }] };
|
|
25521
25478
|
}
|
|
@@ -25533,14 +25490,15 @@ function registerMembershipTools(server, ctx, client) {
|
|
|
25533
25490
|
);
|
|
25534
25491
|
server.tool(
|
|
25535
25492
|
"add_member",
|
|
25536
|
-
"Grant a user access to
|
|
25493
|
+
"Grant a user access to an organization. Their name and avatar are filled in when they first sign in.",
|
|
25537
25494
|
{
|
|
25495
|
+
orgId: orgIdArg,
|
|
25538
25496
|
email: external_exports.string().email().describe("Email address (used as the user ID)"),
|
|
25539
25497
|
role: external_exports.enum(["member", "admin", "client"]).optional().describe('Organization role (defaults to "member")')
|
|
25540
25498
|
},
|
|
25541
|
-
async ({ email: email2, role }) => {
|
|
25499
|
+
async ({ orgId, email: email2, role }) => {
|
|
25542
25500
|
try {
|
|
25543
|
-
const member = await client.addOrgMember(
|
|
25501
|
+
const member = await client.addOrgMember(orgId, { email: email2, role });
|
|
25544
25502
|
const lines = [
|
|
25545
25503
|
"Member added successfully.",
|
|
25546
25504
|
"",
|
|
@@ -25559,14 +25517,15 @@ function registerMembershipTools(server, ctx, client) {
|
|
|
25559
25517
|
);
|
|
25560
25518
|
server.tool(
|
|
25561
25519
|
"update_member_role",
|
|
25562
|
-
"Change a member's role within
|
|
25520
|
+
"Change a member's role within an organization.",
|
|
25563
25521
|
{
|
|
25522
|
+
orgId: orgIdArg,
|
|
25564
25523
|
userId: external_exports.string().describe("User ID (email address) of the member to update"),
|
|
25565
25524
|
role: external_exports.enum(["member", "admin", "client"]).describe("New organization role")
|
|
25566
25525
|
},
|
|
25567
|
-
async ({ userId, role }) => {
|
|
25526
|
+
async ({ orgId, userId, role }) => {
|
|
25568
25527
|
try {
|
|
25569
|
-
const member = await client.updateOrgMember(
|
|
25528
|
+
const member = await client.updateOrgMember(orgId, userId, { role });
|
|
25570
25529
|
if (!member) {
|
|
25571
25530
|
return { content: [{ type: "text", text: `Member not found: "${userId}"` }], isError: true };
|
|
25572
25531
|
}
|
|
@@ -25589,13 +25548,14 @@ function registerMembershipTools(server, ctx, client) {
|
|
|
25589
25548
|
);
|
|
25590
25549
|
server.tool(
|
|
25591
25550
|
"remove_member",
|
|
25592
|
-
"Remove a user from
|
|
25551
|
+
"Remove a user from an organization.",
|
|
25593
25552
|
{
|
|
25553
|
+
orgId: orgIdArg,
|
|
25594
25554
|
userId: external_exports.string().describe("User ID (email address) of the member to remove")
|
|
25595
25555
|
},
|
|
25596
|
-
async ({ userId }) => {
|
|
25556
|
+
async ({ orgId, userId }) => {
|
|
25597
25557
|
try {
|
|
25598
|
-
await client.removeOrgMember(
|
|
25558
|
+
await client.removeOrgMember(orgId, userId);
|
|
25599
25559
|
return { content: [{ type: "text", text: `Member "${userId}" has been removed from the organization.` }] };
|
|
25600
25560
|
} catch (err) {
|
|
25601
25561
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -25814,7 +25774,7 @@ function registerNextActionsTools(server, ctx, client) {
|
|
|
25814
25774
|
},
|
|
25815
25775
|
async ({ systemId, limit }) => {
|
|
25816
25776
|
try {
|
|
25817
|
-
await
|
|
25777
|
+
await assertSystemExists(client, systemId);
|
|
25818
25778
|
const actions = await client.getNextActions(systemId, { limit });
|
|
25819
25779
|
return { content: [{ type: "text", text: formatNextActions(actions) }] };
|
|
25820
25780
|
} catch (err) {
|
|
@@ -25831,7 +25791,7 @@ function registerNextActionsTools(server, ctx, client) {
|
|
|
25831
25791
|
},
|
|
25832
25792
|
async ({ systemId }) => {
|
|
25833
25793
|
try {
|
|
25834
|
-
await
|
|
25794
|
+
await assertSystemExists(client, systemId);
|
|
25835
25795
|
const actions = await client.listTriageActions(systemId);
|
|
25836
25796
|
return { content: [{ type: "text", text: formatTriageActions(actions) }] };
|
|
25837
25797
|
} catch (err) {
|
|
@@ -25849,7 +25809,7 @@ function registerNextActionsTools(server, ctx, client) {
|
|
|
25849
25809
|
},
|
|
25850
25810
|
async ({ systemId, actionKey }) => {
|
|
25851
25811
|
try {
|
|
25852
|
-
await
|
|
25812
|
+
await assertSystemExists(client, systemId);
|
|
25853
25813
|
await client.dismissTriageAction(systemId, actionKey, ctx.user?.userId ?? "mcp");
|
|
25854
25814
|
return { content: [{ type: "text", text: `Action ${actionKey} dismissed.` }] };
|
|
25855
25815
|
} catch (err) {
|
|
@@ -26302,14 +26262,10 @@ var BeatVersionStatusSchema = external_exports.preprocess(
|
|
|
26302
26262
|
(value) => typeof value === "string" && ACTIVE_BEAT_VERSION_STATUSES.has(value) ? "active" : "archived",
|
|
26303
26263
|
external_exports.enum(BINARY_ENTITY_STATUS_VALUES)
|
|
26304
26264
|
);
|
|
26305
|
-
var NoteTypeSchema = external_exports.
|
|
26306
|
-
"context",
|
|
26307
|
-
"
|
|
26308
|
-
|
|
26309
|
-
"guidance",
|
|
26310
|
-
"decision",
|
|
26311
|
-
"document"
|
|
26312
|
-
]);
|
|
26265
|
+
var NoteTypeSchema = external_exports.preprocess(
|
|
26266
|
+
(v) => v === "assumption" ? "context" : v,
|
|
26267
|
+
external_exports.enum(["context", "constraint", "guidance", "decision", "document"])
|
|
26268
|
+
);
|
|
26313
26269
|
var NoteStatusSchema = external_exports.enum([
|
|
26314
26270
|
"active",
|
|
26315
26271
|
"unvalidated",
|
|
@@ -26322,62 +26278,25 @@ var NoteStatusSchema = external_exports.enum([
|
|
|
26322
26278
|
]);
|
|
26323
26279
|
var SystemStatusSchema = external_exports.enum(["active", "archived", "draft"]);
|
|
26324
26280
|
var ChangeImportanceSchema = external_exports.enum(["minor", "moderate", "major"]);
|
|
26325
|
-
var
|
|
26326
|
-
|
|
26327
|
-
|
|
26328
|
-
|
|
26329
|
-
|
|
26330
|
-
|
|
26331
|
-
|
|
26332
|
-
|
|
26333
|
-
|
|
26334
|
-
|
|
26335
|
-
|
|
26336
|
-
|
|
26337
|
-
|
|
26338
|
-
|
|
26339
|
-
|
|
26340
|
-
|
|
26341
|
-
|
|
26342
|
-
|
|
26343
|
-
|
|
26344
|
-
const id = b["systemId"] || b["projectId"];
|
|
26345
|
-
const title = b["systemTitle"] || b["projectTitle"];
|
|
26346
|
-
const result = { ...b };
|
|
26347
|
-
if (id) result["systemId"] = id;
|
|
26348
|
-
if (title) result["systemTitle"] = title;
|
|
26349
|
-
return result;
|
|
26350
|
-
};
|
|
26351
|
-
var BeatApiSchema = external_exports.preprocess(
|
|
26352
|
-
(raw) => {
|
|
26353
|
-
if (typeof raw !== "object" || raw === null) return raw;
|
|
26354
|
-
const b = raw;
|
|
26355
|
-
const id = b["systemId"] || b["projectId"];
|
|
26356
|
-
const result = { ...b };
|
|
26357
|
-
if (id) result["systemId"] = id;
|
|
26358
|
-
if ((b["systemName"] == null || b["systemName"] === "") && b["projectName"]) result["systemName"] = b["projectName"];
|
|
26359
|
-
return result;
|
|
26360
|
-
},
|
|
26361
|
-
external_exports.object({
|
|
26362
|
-
beatId: external_exports.string(),
|
|
26363
|
-
systemId: external_exports.string(),
|
|
26364
|
-
/** Source system's title; attached by the flat /api/beats endpoint for
|
|
26365
|
-
* multi-system (teamspace/org) responses. */
|
|
26366
|
-
systemName: external_exports.string().nullish(),
|
|
26367
|
-
title: external_exports.string(),
|
|
26368
|
-
version: external_exports.number().nullish(),
|
|
26369
|
-
description: external_exports.string().nullish(),
|
|
26370
|
-
beatStatus: BeatStatusSchema.nullish(),
|
|
26371
|
-
tags: external_exports.array(external_exports.string()).nullish(),
|
|
26372
|
-
priority: external_exports.number().nullish(),
|
|
26373
|
-
estimatedEffort: external_exports.string().nullish(),
|
|
26374
|
-
humanAssignee: HumanAssigneeSchema.nullish(),
|
|
26375
|
-
agentAssignee: AgentAssigneeSchema.nullish(),
|
|
26376
|
-
revisionCount: external_exports.number().nullish(),
|
|
26377
|
-
createdAt: external_exports.string(),
|
|
26378
|
-
updatedAt: external_exports.string()
|
|
26379
|
-
}).passthrough()
|
|
26380
|
-
);
|
|
26281
|
+
var BeatApiSchema = external_exports.object({
|
|
26282
|
+
beatId: external_exports.string(),
|
|
26283
|
+
systemId: external_exports.string(),
|
|
26284
|
+
/** Source system's title; attached by the flat /api/beats endpoint for
|
|
26285
|
+
* multi-system (teamspace/org) responses. */
|
|
26286
|
+
systemName: external_exports.string().nullish(),
|
|
26287
|
+
title: external_exports.string(),
|
|
26288
|
+
version: external_exports.number().nullish(),
|
|
26289
|
+
description: external_exports.string().nullish(),
|
|
26290
|
+
beatStatus: BeatStatusSchema.nullish(),
|
|
26291
|
+
tags: external_exports.array(external_exports.string()).nullish(),
|
|
26292
|
+
priority: external_exports.number().nullish(),
|
|
26293
|
+
estimatedEffort: external_exports.string().nullish(),
|
|
26294
|
+
humanAssignee: HumanAssigneeSchema.nullish(),
|
|
26295
|
+
agentAssignee: AgentAssigneeSchema.nullish(),
|
|
26296
|
+
revisionCount: external_exports.number().nullish(),
|
|
26297
|
+
createdAt: external_exports.string(),
|
|
26298
|
+
updatedAt: external_exports.string()
|
|
26299
|
+
}).passthrough();
|
|
26381
26300
|
var RevisionScmSchema = external_exports.object({
|
|
26382
26301
|
provider: external_exports.literal("github"),
|
|
26383
26302
|
number: external_exports.string(),
|
|
@@ -26386,33 +26305,30 @@ var RevisionScmSchema = external_exports.object({
|
|
|
26386
26305
|
state: external_exports.enum(["draft", "open", "merged", "closed"]),
|
|
26387
26306
|
mergeSha: external_exports.string().optional()
|
|
26388
26307
|
});
|
|
26389
|
-
var RevisionApiSchema = external_exports.
|
|
26390
|
-
|
|
26391
|
-
external_exports.
|
|
26392
|
-
|
|
26393
|
-
|
|
26394
|
-
|
|
26395
|
-
|
|
26396
|
-
|
|
26397
|
-
|
|
26398
|
-
|
|
26399
|
-
|
|
26400
|
-
|
|
26401
|
-
|
|
26402
|
-
|
|
26403
|
-
|
|
26404
|
-
|
|
26405
|
-
|
|
26406
|
-
|
|
26407
|
-
|
|
26408
|
-
|
|
26409
|
-
|
|
26410
|
-
|
|
26411
|
-
|
|
26412
|
-
|
|
26413
|
-
}).passthrough()
|
|
26414
|
-
);
|
|
26415
|
-
var NoteApiSchema = external_exports.preprocess(normalizeSystemId, external_exports.object({
|
|
26308
|
+
var RevisionApiSchema = external_exports.object({
|
|
26309
|
+
revisionId: external_exports.string(),
|
|
26310
|
+
beatId: external_exports.string(),
|
|
26311
|
+
systemId: external_exports.string(),
|
|
26312
|
+
status: RevisionStatusSchema,
|
|
26313
|
+
title: external_exports.string().default(""),
|
|
26314
|
+
versionNumber: external_exports.number(),
|
|
26315
|
+
changeSummary: external_exports.string().nullish(),
|
|
26316
|
+
changeImportance: ChangeImportanceSchema.nullish(),
|
|
26317
|
+
primaryUser: external_exports.string(),
|
|
26318
|
+
description: external_exports.string().nullish(),
|
|
26319
|
+
tags: external_exports.array(external_exports.string()).nullish(),
|
|
26320
|
+
priority: external_exports.number().nullish(),
|
|
26321
|
+
estimatedEffort: external_exports.string().nullish(),
|
|
26322
|
+
humanAssignee: HumanAssigneeSchema.nullish(),
|
|
26323
|
+
agentAssignee: AgentAssigneeSchema.nullish(),
|
|
26324
|
+
scm: RevisionScmSchema.optional(),
|
|
26325
|
+
// The Layer this Revision was created under (immutable). Absent on Revisions
|
|
26326
|
+
// predating Layers — never backfilled — so consumers group those as "Unassigned".
|
|
26327
|
+
layerId: external_exports.string().nullish(),
|
|
26328
|
+
createdAt: external_exports.string(),
|
|
26329
|
+
updatedAt: external_exports.string()
|
|
26330
|
+
}).passthrough();
|
|
26331
|
+
var NoteApiSchema = external_exports.object({
|
|
26416
26332
|
noteId: external_exports.string(),
|
|
26417
26333
|
noteType: NoteTypeSchema,
|
|
26418
26334
|
status: NoteStatusSchema,
|
|
@@ -26428,21 +26344,18 @@ var NoteApiSchema = external_exports.preprocess(normalizeSystemId, external_expo
|
|
|
26428
26344
|
agentAssignee: AgentAssigneeSchema.nullish(),
|
|
26429
26345
|
createdAt: external_exports.string(),
|
|
26430
26346
|
updatedAt: external_exports.string()
|
|
26431
|
-
}).passthrough()
|
|
26432
|
-
var SystemApiSchema = external_exports.
|
|
26433
|
-
|
|
26434
|
-
external_exports.
|
|
26435
|
-
|
|
26436
|
-
|
|
26437
|
-
|
|
26438
|
-
|
|
26439
|
-
|
|
26440
|
-
|
|
26441
|
-
|
|
26442
|
-
|
|
26443
|
-
updatedAt: external_exports.string()
|
|
26444
|
-
}).passthrough()
|
|
26445
|
-
);
|
|
26347
|
+
}).passthrough();
|
|
26348
|
+
var SystemApiSchema = external_exports.object({
|
|
26349
|
+
systemId: external_exports.string(),
|
|
26350
|
+
title: external_exports.string(),
|
|
26351
|
+
status: SystemStatusSchema,
|
|
26352
|
+
description: external_exports.string().optional(),
|
|
26353
|
+
strategy: external_exports.string().optional(),
|
|
26354
|
+
orgId: external_exports.string().optional(),
|
|
26355
|
+
ownerUserId: external_exports.string().optional(),
|
|
26356
|
+
createdAt: external_exports.string(),
|
|
26357
|
+
updatedAt: external_exports.string()
|
|
26358
|
+
}).passthrough();
|
|
26446
26359
|
var BeatsResponseSchema = external_exports.object({
|
|
26447
26360
|
beats: external_exports.array(BeatApiSchema),
|
|
26448
26361
|
total: external_exports.number().optional(),
|
|
@@ -26628,43 +26541,37 @@ var CHECK_TARGET_TYPES = [
|
|
|
26628
26541
|
"organization"
|
|
26629
26542
|
];
|
|
26630
26543
|
var CheckTargetTypeSchema = external_exports.enum(CHECK_TARGET_TYPES);
|
|
26631
|
-
var CheckApiSchema = external_exports.
|
|
26632
|
-
|
|
26633
|
-
|
|
26634
|
-
|
|
26635
|
-
|
|
26636
|
-
|
|
26637
|
-
|
|
26638
|
-
|
|
26639
|
-
|
|
26640
|
-
|
|
26641
|
-
|
|
26642
|
-
|
|
26643
|
-
|
|
26644
|
-
|
|
26645
|
-
|
|
26646
|
-
|
|
26647
|
-
|
|
26648
|
-
|
|
26649
|
-
|
|
26650
|
-
|
|
26651
|
-
|
|
26652
|
-
external_exports.
|
|
26653
|
-
|
|
26654
|
-
|
|
26655
|
-
|
|
26656
|
-
|
|
26657
|
-
|
|
26658
|
-
|
|
26659
|
-
|
|
26660
|
-
|
|
26661
|
-
|
|
26662
|
-
updatedAt: external_exports.string(),
|
|
26663
|
-
/** Populated only when the API was called with ?includeDimensions=true.
|
|
26664
|
-
* Read-time enrichment from the underlying Check entity. */
|
|
26665
|
-
dimensions: external_exports.array(CheckDimensionScoreSchema).optional()
|
|
26666
|
-
}).passthrough()
|
|
26667
|
-
);
|
|
26544
|
+
var CheckApiSchema = external_exports.object({
|
|
26545
|
+
checkId: external_exports.string(),
|
|
26546
|
+
checkType: CheckTypeSchema,
|
|
26547
|
+
targetType: CheckTargetTypeSchema,
|
|
26548
|
+
targetId: external_exports.string(),
|
|
26549
|
+
systemId: external_exports.string(),
|
|
26550
|
+
overallScore: external_exports.number(),
|
|
26551
|
+
dimensions: external_exports.array(CheckDimensionScoreSchema),
|
|
26552
|
+
summary: external_exports.string(),
|
|
26553
|
+
topSuggestion: external_exports.string().optional(),
|
|
26554
|
+
gaps: external_exports.array(CheckGapSchema).optional(),
|
|
26555
|
+
taskId: external_exports.string().optional(),
|
|
26556
|
+
triggeredBy: external_exports.string().optional(),
|
|
26557
|
+
createdAt: external_exports.string(),
|
|
26558
|
+
updatedAt: external_exports.string()
|
|
26559
|
+
}).passthrough();
|
|
26560
|
+
var SystemCheckApiSchema = external_exports.object({
|
|
26561
|
+
checkId: external_exports.string(),
|
|
26562
|
+
checkType: CheckTypeSchema,
|
|
26563
|
+
targetType: CheckTargetTypeSchema,
|
|
26564
|
+
targetId: external_exports.string(),
|
|
26565
|
+
systemId: external_exports.string(),
|
|
26566
|
+
overallScore: external_exports.number(),
|
|
26567
|
+
summary: external_exports.string(),
|
|
26568
|
+
triggeredBy: external_exports.string().optional(),
|
|
26569
|
+
createdAt: external_exports.string(),
|
|
26570
|
+
updatedAt: external_exports.string(),
|
|
26571
|
+
/** Populated only when the API was called with ?includeDimensions=true.
|
|
26572
|
+
* Read-time enrichment from the underlying Check entity. */
|
|
26573
|
+
dimensions: external_exports.array(CheckDimensionScoreSchema).optional()
|
|
26574
|
+
}).passthrough();
|
|
26668
26575
|
var CheckResponseSchema = external_exports.object({ check: CheckApiSchema }).passthrough();
|
|
26669
26576
|
var ChecksResponseSchema = external_exports.object({ checks: external_exports.array(SystemCheckApiSchema), nextCursor: external_exports.string().optional() }).passthrough();
|
|
26670
26577
|
var TaskStatusSchema2 = external_exports.enum(["pending", "running", "completed", "failed"]);
|
|
@@ -26709,30 +26616,27 @@ var OrgMemberSchema = external_exports.object({
|
|
|
26709
26616
|
var OrganizationsResponseSchema = external_exports.array(OrganizationSummarySchema);
|
|
26710
26617
|
var OrgMembersResponseSchema = external_exports.array(OrgMemberSchema);
|
|
26711
26618
|
var WrappedOrgMembersResponseSchema = external_exports.object({ members: OrgMembersResponseSchema });
|
|
26712
|
-
var ActivitySchema = external_exports.
|
|
26713
|
-
|
|
26714
|
-
external_exports.
|
|
26715
|
-
|
|
26716
|
-
|
|
26717
|
-
|
|
26718
|
-
|
|
26719
|
-
|
|
26720
|
-
|
|
26721
|
-
|
|
26722
|
-
|
|
26723
|
-
|
|
26724
|
-
|
|
26725
|
-
|
|
26726
|
-
|
|
26727
|
-
createdAt: external_exports.string()
|
|
26728
|
-
}).passthrough()
|
|
26729
|
-
);
|
|
26619
|
+
var ActivitySchema = external_exports.object({
|
|
26620
|
+
activityId: external_exports.string(),
|
|
26621
|
+
systemId: external_exports.string(),
|
|
26622
|
+
action: external_exports.string(),
|
|
26623
|
+
importance: external_exports.enum(["minor", "moderate", "major"]),
|
|
26624
|
+
reason: external_exports.string(),
|
|
26625
|
+
// actorType is typed as string (not a strict enum) because the API accepts values
|
|
26626
|
+
// beyond 'user'/'system' (e.g. 'human', 'agent') depending on the call site.
|
|
26627
|
+
actorType: external_exports.string().optional(),
|
|
26628
|
+
actorId: external_exports.string().optional(),
|
|
26629
|
+
affectedBeatIds: external_exports.array(external_exports.string()).optional(),
|
|
26630
|
+
affectedBeatTitles: external_exports.array(external_exports.string()).optional(),
|
|
26631
|
+
metadata: external_exports.record(external_exports.unknown()).optional(),
|
|
26632
|
+
createdAt: external_exports.string()
|
|
26633
|
+
}).passthrough();
|
|
26730
26634
|
var ActivitiesResponseSchema = external_exports.object({
|
|
26731
26635
|
activities: external_exports.array(ActivitySchema),
|
|
26732
26636
|
hasMore: external_exports.boolean(),
|
|
26733
26637
|
nextCursor: external_exports.string().optional()
|
|
26734
26638
|
}).passthrough();
|
|
26735
|
-
var NotificationSchema2 = external_exports.
|
|
26639
|
+
var NotificationSchema2 = external_exports.object({
|
|
26736
26640
|
notificationId: external_exports.string(),
|
|
26737
26641
|
action: external_exports.string(),
|
|
26738
26642
|
title: external_exports.string(),
|
|
@@ -26747,7 +26651,7 @@ var NotificationSchema2 = external_exports.preprocess(normalizeSystemId, externa
|
|
|
26747
26651
|
actorType: external_exports.enum(["user", "system"]),
|
|
26748
26652
|
actorId: external_exports.string().optional(),
|
|
26749
26653
|
actorName: external_exports.string().optional()
|
|
26750
|
-
}).passthrough()
|
|
26654
|
+
}).passthrough();
|
|
26751
26655
|
var NotificationsResponseSchema = external_exports.object({
|
|
26752
26656
|
notifications: external_exports.array(NotificationSchema2),
|
|
26753
26657
|
hasMore: external_exports.boolean().optional(),
|
|
@@ -26791,29 +26695,26 @@ var InboxItemsResponseSchema = external_exports.object({
|
|
|
26791
26695
|
hasMore: external_exports.boolean(),
|
|
26792
26696
|
nextCursor: external_exports.string().optional()
|
|
26793
26697
|
}).passthrough();
|
|
26794
|
-
var TriageActionSchema = external_exports.
|
|
26795
|
-
|
|
26796
|
-
external_exports.
|
|
26797
|
-
|
|
26798
|
-
|
|
26799
|
-
|
|
26800
|
-
|
|
26801
|
-
|
|
26802
|
-
|
|
26803
|
-
|
|
26804
|
-
|
|
26805
|
-
|
|
26806
|
-
|
|
26807
|
-
|
|
26808
|
-
|
|
26809
|
-
|
|
26810
|
-
|
|
26811
|
-
|
|
26812
|
-
|
|
26813
|
-
|
|
26814
|
-
dismissedAt: external_exports.string().optional()
|
|
26815
|
-
}).passthrough()
|
|
26816
|
-
);
|
|
26698
|
+
var TriageActionSchema = external_exports.object({
|
|
26699
|
+
actionKey: external_exports.string(),
|
|
26700
|
+
systemId: external_exports.string(),
|
|
26701
|
+
entityId: external_exports.string().optional(),
|
|
26702
|
+
entityType: external_exports.enum(["beat", "revision", "note"]).optional(),
|
|
26703
|
+
beatId: external_exports.string().optional(),
|
|
26704
|
+
category: external_exports.string(),
|
|
26705
|
+
priority: external_exports.string(),
|
|
26706
|
+
beatStatus: external_exports.string().optional(),
|
|
26707
|
+
title: external_exports.string(),
|
|
26708
|
+
description: external_exports.string(),
|
|
26709
|
+
status: external_exports.enum(["open", "acted", "dismissed", "resolved"]),
|
|
26710
|
+
decision: external_exports.enum(["auto-execute", "escalate", "skip"]).optional(),
|
|
26711
|
+
taskId: external_exports.string().optional(),
|
|
26712
|
+
taskStatus: external_exports.string().optional(),
|
|
26713
|
+
firstSeenAt: external_exports.string(),
|
|
26714
|
+
lastSeenAt: external_exports.string(),
|
|
26715
|
+
dismissedBy: external_exports.string().optional(),
|
|
26716
|
+
dismissedAt: external_exports.string().optional()
|
|
26717
|
+
}).passthrough();
|
|
26817
26718
|
var TriageActionsResponseSchema = external_exports.object({
|
|
26818
26719
|
actions: external_exports.array(TriageActionSchema)
|
|
26819
26720
|
}).passthrough();
|
|
@@ -26830,7 +26731,7 @@ var NextActionSchema = external_exports.object({
|
|
|
26830
26731
|
var NextActionsResponseSchema = external_exports.object({
|
|
26831
26732
|
actions: external_exports.array(NextActionSchema)
|
|
26832
26733
|
}).passthrough();
|
|
26833
|
-
var SearchResultSchema = external_exports.
|
|
26734
|
+
var SearchResultSchema = external_exports.object({
|
|
26834
26735
|
entityId: external_exports.string(),
|
|
26835
26736
|
entityType: external_exports.enum(["beat", "note", "revision", "beatVersion", "drop", "project", "system"]),
|
|
26836
26737
|
score: external_exports.number(),
|
|
@@ -26838,7 +26739,7 @@ var SearchResultSchema = external_exports.preprocess(normalizeSystemIdAndName, e
|
|
|
26838
26739
|
systemId: external_exports.string().optional(),
|
|
26839
26740
|
systemName: external_exports.string().optional(),
|
|
26840
26741
|
teamspaceId: external_exports.string().optional()
|
|
26841
|
-
}).passthrough()
|
|
26742
|
+
}).passthrough();
|
|
26842
26743
|
var SearchScopeSchema = external_exports.object({
|
|
26843
26744
|
kind: external_exports.enum(["project", "teamspace", "org"]),
|
|
26844
26745
|
id: external_exports.string()
|
|
@@ -26882,7 +26783,7 @@ var LandscapeResponseSchema = external_exports.object({
|
|
|
26882
26783
|
(raw) => {
|
|
26883
26784
|
if (typeof raw !== "object" || raw === null) return raw;
|
|
26884
26785
|
const o = raw;
|
|
26885
|
-
const id = o["systemId"]
|
|
26786
|
+
const id = o["systemId"];
|
|
26886
26787
|
if (!id) return raw;
|
|
26887
26788
|
return { ...o, systemId: id };
|
|
26888
26789
|
},
|
|
@@ -26900,14 +26801,16 @@ var ClusterDescriptionSchema = external_exports.object({
|
|
|
26900
26801
|
title: external_exports.string(),
|
|
26901
26802
|
description: external_exports.string()
|
|
26902
26803
|
}).passthrough();
|
|
26903
|
-
var BeatVersionItemSchema = external_exports.
|
|
26804
|
+
var BeatVersionItemSchema = external_exports.object({
|
|
26904
26805
|
beatVersionId: external_exports.string(),
|
|
26905
26806
|
beatId: external_exports.string(),
|
|
26906
26807
|
systemId: external_exports.string().optional(),
|
|
26907
26808
|
status: external_exports.string()
|
|
26908
|
-
}).passthrough()
|
|
26809
|
+
}).passthrough();
|
|
26909
26810
|
var BeatVersionsResponseSchema = external_exports.object({
|
|
26910
|
-
beatVersions: external_exports.array(BeatVersionItemSchema)
|
|
26811
|
+
beatVersions: external_exports.array(BeatVersionItemSchema),
|
|
26812
|
+
cursor: external_exports.string().optional(),
|
|
26813
|
+
hasMore: external_exports.boolean().optional()
|
|
26911
26814
|
}).passthrough();
|
|
26912
26815
|
var ConversationSummaryBaseSchema = external_exports.object({
|
|
26913
26816
|
conversationId: external_exports.string(),
|
|
@@ -26924,7 +26827,7 @@ var ConversationSummaryBaseSchema = external_exports.object({
|
|
|
26924
26827
|
createdAt: external_exports.string(),
|
|
26925
26828
|
updatedAt: external_exports.string()
|
|
26926
26829
|
});
|
|
26927
|
-
var ConversationSummarySchema =
|
|
26830
|
+
var ConversationSummarySchema = ConversationSummaryBaseSchema.passthrough();
|
|
26928
26831
|
var StoredMessageSchema = external_exports.object({
|
|
26929
26832
|
messageId: external_exports.string(),
|
|
26930
26833
|
conversationId: external_exports.string(),
|
|
@@ -27169,30 +27072,21 @@ var AccountSystemApiSchema = external_exports.object({
|
|
|
27169
27072
|
var AccountSystemsResponseSchema = external_exports.object({ systems: external_exports.array(AccountSystemApiSchema) }).passthrough();
|
|
27170
27073
|
var LAYER_STATUSES = ["active", "archived"];
|
|
27171
27074
|
var LayerStatusApiSchema = external_exports.enum(LAYER_STATUSES);
|
|
27172
|
-
var LayerApiSchema = external_exports.
|
|
27173
|
-
(
|
|
27174
|
-
|
|
27175
|
-
|
|
27176
|
-
|
|
27177
|
-
|
|
27178
|
-
|
|
27179
|
-
|
|
27180
|
-
|
|
27181
|
-
|
|
27182
|
-
|
|
27183
|
-
|
|
27184
|
-
|
|
27185
|
-
|
|
27186
|
-
|
|
27187
|
-
repoPatterns: external_exports.array(external_exports.string()).nullish(),
|
|
27188
|
-
status: LayerStatusApiSchema,
|
|
27189
|
-
archivedAt: external_exports.string().nullish(),
|
|
27190
|
-
createdBy: external_exports.string().nullish(),
|
|
27191
|
-
version: external_exports.number().nullish(),
|
|
27192
|
-
createdAt: external_exports.string(),
|
|
27193
|
-
updatedAt: external_exports.string()
|
|
27194
|
-
}).passthrough()
|
|
27195
|
-
);
|
|
27075
|
+
var LayerApiSchema = external_exports.object({
|
|
27076
|
+
layerId: external_exports.string(),
|
|
27077
|
+
systemId: external_exports.string(),
|
|
27078
|
+
layerKey: external_exports.string(),
|
|
27079
|
+
name: external_exports.string(),
|
|
27080
|
+
description: external_exports.string().nullish(),
|
|
27081
|
+
parentLayerId: external_exports.string().nullish(),
|
|
27082
|
+
repoPatterns: external_exports.array(external_exports.string()).nullish(),
|
|
27083
|
+
status: LayerStatusApiSchema,
|
|
27084
|
+
archivedAt: external_exports.string().nullish(),
|
|
27085
|
+
createdBy: external_exports.string().nullish(),
|
|
27086
|
+
version: external_exports.number().nullish(),
|
|
27087
|
+
createdAt: external_exports.string(),
|
|
27088
|
+
updatedAt: external_exports.string()
|
|
27089
|
+
}).passthrough();
|
|
27196
27090
|
var LayersResponseSchema = external_exports.object({ layers: external_exports.array(LayerApiSchema) }).passthrough();
|
|
27197
27091
|
var LAYER_RUBRIC_SCOPES_LIST = ["account", "teamspace", "movement"];
|
|
27198
27092
|
var LayerRubricScopeApiSchema = external_exports.enum(LAYER_RUBRIC_SCOPES_LIST);
|
|
@@ -27231,7 +27125,7 @@ var DELIVERABLE_GROUP_STATUSES = [
|
|
|
27231
27125
|
"cancelled"
|
|
27232
27126
|
];
|
|
27233
27127
|
var DeliverableGroupStatusSchema = external_exports.enum(DELIVERABLE_GROUP_STATUSES);
|
|
27234
|
-
var DeliverableGroupApiSchema = external_exports.
|
|
27128
|
+
var DeliverableGroupApiSchema = external_exports.object({
|
|
27235
27129
|
deliverableGroupId: external_exports.string(),
|
|
27236
27130
|
groupCode: external_exports.string(),
|
|
27237
27131
|
teamspaceId: external_exports.string(),
|
|
@@ -27252,7 +27146,7 @@ var DeliverableGroupApiSchema = external_exports.preprocess(normalizeSystemId, e
|
|
|
27252
27146
|
version: external_exports.number().nullish(),
|
|
27253
27147
|
createdAt: external_exports.string(),
|
|
27254
27148
|
updatedAt: external_exports.string()
|
|
27255
|
-
}).passthrough()
|
|
27149
|
+
}).passthrough();
|
|
27256
27150
|
var DeliverableGroupsResponseSchema = external_exports.object({
|
|
27257
27151
|
deliverableGroups: external_exports.array(DeliverableGroupApiSchema)
|
|
27258
27152
|
}).passthrough();
|
|
@@ -27269,33 +27163,27 @@ var MovementDropSummaryApiSchema = external_exports.object({
|
|
|
27269
27163
|
var MovementDropsResponseSchema = external_exports.object({
|
|
27270
27164
|
drops: external_exports.array(MovementDropSummaryApiSchema)
|
|
27271
27165
|
}).passthrough();
|
|
27272
|
-
var WorkPlanBeatVersionApiSchema = external_exports.
|
|
27273
|
-
|
|
27274
|
-
external_exports.
|
|
27275
|
-
|
|
27276
|
-
|
|
27277
|
-
|
|
27278
|
-
|
|
27279
|
-
|
|
27280
|
-
|
|
27281
|
-
|
|
27282
|
-
|
|
27283
|
-
|
|
27284
|
-
|
|
27285
|
-
|
|
27286
|
-
|
|
27287
|
-
updatedAt: external_exports.string()
|
|
27288
|
-
}).passthrough()
|
|
27289
|
-
);
|
|
27166
|
+
var WorkPlanBeatVersionApiSchema = external_exports.object({
|
|
27167
|
+
beatVersionId: external_exports.string(),
|
|
27168
|
+
beatId: external_exports.string(),
|
|
27169
|
+
systemId: external_exports.string(),
|
|
27170
|
+
versionNumber: external_exports.number(),
|
|
27171
|
+
title: external_exports.string(),
|
|
27172
|
+
status: BeatVersionStatusSchema,
|
|
27173
|
+
// Derived Value Units (VU = Δf × W), read-only. 0 when the BV has no confirmed
|
|
27174
|
+
// value-velocity inputs. Computed server-side from the parent Beat's weight.
|
|
27175
|
+
// `.default(0)` tolerates a rolling deploy where an older API omits the field —
|
|
27176
|
+
// validatedApiFetch would otherwise throw and drop the whole hook into error.
|
|
27177
|
+
valueUnits: external_exports.number().default(0),
|
|
27178
|
+
createdAt: external_exports.string(),
|
|
27179
|
+
updatedAt: external_exports.string()
|
|
27180
|
+
}).passthrough();
|
|
27290
27181
|
var DeliverableGroupBeatVersionsResponseSchema = external_exports.object({
|
|
27291
27182
|
beatVersions: external_exports.array(WorkPlanBeatVersionApiSchema)
|
|
27292
27183
|
}).passthrough();
|
|
27293
27184
|
var RevisionClaimActorSchema = external_exports.union([
|
|
27294
27185
|
external_exports.object({ kind: external_exports.literal("user"), userId: external_exports.string() }),
|
|
27295
|
-
external_exports.
|
|
27296
|
-
normalizeSystemId,
|
|
27297
|
-
external_exports.object({ kind: external_exports.literal("agent"), agentId: external_exports.string(), systemId: external_exports.string() })
|
|
27298
|
-
)
|
|
27186
|
+
external_exports.object({ kind: external_exports.literal("agent"), agentId: external_exports.string(), systemId: external_exports.string() })
|
|
27299
27187
|
]);
|
|
27300
27188
|
var RevisionClaimSchema = external_exports.object({
|
|
27301
27189
|
claimId: external_exports.string(),
|
|
@@ -27306,39 +27194,33 @@ var RevisionClaimSchema = external_exports.object({
|
|
|
27306
27194
|
var RevisionClaimsResponseSchema = external_exports.object({
|
|
27307
27195
|
claims: external_exports.array(RevisionClaimSchema)
|
|
27308
27196
|
}).passthrough();
|
|
27309
|
-
var ProposedRevisionSchema = external_exports.
|
|
27310
|
-
|
|
27311
|
-
external_exports.
|
|
27312
|
-
|
|
27313
|
-
|
|
27314
|
-
|
|
27315
|
-
|
|
27316
|
-
|
|
27317
|
-
|
|
27318
|
-
systemTitle: external_exports.string().optional()
|
|
27319
|
-
}).passthrough()
|
|
27320
|
-
);
|
|
27197
|
+
var ProposedRevisionSchema = external_exports.object({
|
|
27198
|
+
revisionId: external_exports.string(),
|
|
27199
|
+
title: external_exports.string(),
|
|
27200
|
+
state: external_exports.string(),
|
|
27201
|
+
beatId: external_exports.string(),
|
|
27202
|
+
beatTitle: external_exports.string().optional(),
|
|
27203
|
+
systemId: external_exports.string(),
|
|
27204
|
+
systemTitle: external_exports.string().optional()
|
|
27205
|
+
}).passthrough();
|
|
27321
27206
|
var ReleaseIntervalSchema = external_exports.object({
|
|
27322
27207
|
averageIntervalDays: external_exports.number(),
|
|
27323
27208
|
sampleSize: external_exports.number(),
|
|
27324
27209
|
isFallback: external_exports.boolean()
|
|
27325
27210
|
}).passthrough();
|
|
27326
|
-
var EligibleBeatVersionForDropSchema = external_exports.
|
|
27327
|
-
|
|
27328
|
-
external_exports.
|
|
27329
|
-
|
|
27330
|
-
|
|
27331
|
-
|
|
27332
|
-
|
|
27333
|
-
|
|
27334
|
-
|
|
27335
|
-
|
|
27336
|
-
|
|
27337
|
-
|
|
27338
|
-
|
|
27339
|
-
currentDropAssignment: external_exports.object({ dropId: external_exports.string(), dropCode: external_exports.string().optional(), state: external_exports.string() }).optional()
|
|
27340
|
-
}).passthrough()
|
|
27341
|
-
);
|
|
27211
|
+
var EligibleBeatVersionForDropSchema = external_exports.object({
|
|
27212
|
+
beatVersionId: external_exports.string(),
|
|
27213
|
+
title: external_exports.string(),
|
|
27214
|
+
state: external_exports.string(),
|
|
27215
|
+
beatId: external_exports.string(),
|
|
27216
|
+
beatTitle: external_exports.string().optional(),
|
|
27217
|
+
systemId: external_exports.string(),
|
|
27218
|
+
systemTitle: external_exports.string().optional(),
|
|
27219
|
+
createdAt: external_exports.string(),
|
|
27220
|
+
updatedAt: external_exports.string(),
|
|
27221
|
+
humanAssignee: external_exports.object({ email: external_exports.string(), name: external_exports.string() }).optional(),
|
|
27222
|
+
currentDropAssignment: external_exports.object({ dropId: external_exports.string(), dropCode: external_exports.string().optional(), state: external_exports.string() }).optional()
|
|
27223
|
+
}).passthrough();
|
|
27342
27224
|
var ScoredDropCandidateSchema = external_exports.object({
|
|
27343
27225
|
bv: EligibleBeatVersionForDropSchema,
|
|
27344
27226
|
// Score fields are nullable — the ranking engine returns null when there is
|
|
@@ -27558,7 +27440,7 @@ function sortNotesByCreatedAt(notes, orderBy) {
|
|
|
27558
27440
|
|
|
27559
27441
|
// ../../libs/harmonica-services/src/mcp/tools/note-tools.ts
|
|
27560
27442
|
var DISMISSED_NOTE_STATUS = "dismissed";
|
|
27561
|
-
var NOTE_TYPE_VALUES = ["context", "
|
|
27443
|
+
var NOTE_TYPE_VALUES = ["context", "constraint", "guidance", "decision", "document"];
|
|
27562
27444
|
var NOTE_STATUS_VALUES = ["active", "unvalidated", "validated", "invalidated", "inProgress", "resolved", "superseded", "dismissed"];
|
|
27563
27445
|
var DECISION_SIGNIFICANCE_VALUES2 = ["strategic", "structural", "implementation"];
|
|
27564
27446
|
async function deliveryPolicyBlock(client, note) {
|
|
@@ -27605,7 +27487,7 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27605
27487
|
return { content: [{ type: "text", text: "orderBy requires beatId \u2014 it is only supported for beat-scoped queries." }], isError: true };
|
|
27606
27488
|
}
|
|
27607
27489
|
try {
|
|
27608
|
-
await
|
|
27490
|
+
await assertSystemExists(client, systemId);
|
|
27609
27491
|
} catch (err) {
|
|
27610
27492
|
const message = err instanceof Error ? err.message : String(err);
|
|
27611
27493
|
return { content: [{ type: "text", text: `Access denied: ${message}` }], isError: true };
|
|
@@ -27688,13 +27570,14 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27688
27570
|
"list_org_notes",
|
|
27689
27571
|
"List Notes at the organization level (cross-cutting constraints, org-wide decisions, and context that applies to all systems). Filterable by type and status. Use offset/limit to paginate.",
|
|
27690
27572
|
{
|
|
27573
|
+
orgId: external_exports.string().describe("The organization ID \u2014 see list_organizations"),
|
|
27691
27574
|
noteType: external_exports.enum(NOTE_TYPE_VALUES).optional().describe("Filter by note type"),
|
|
27692
27575
|
status: external_exports.enum(NOTE_STATUS_VALUES).optional().describe("Filter by status"),
|
|
27693
27576
|
offset: external_exports.coerce.number().int().min(0).default(0).describe("Number of notes to skip (for pagination)"),
|
|
27694
27577
|
limit: external_exports.coerce.number().int().min(1).max(500).default(100).describe("Max notes to return (default 100, max 500)")
|
|
27695
27578
|
},
|
|
27696
|
-
async ({ noteType, status, offset = 0, limit = 100 }) => {
|
|
27697
|
-
const notes = await client.listOrgNotes(
|
|
27579
|
+
async ({ orgId, noteType, status, offset = 0, limit = 100 }) => {
|
|
27580
|
+
const notes = await client.listOrgNotes(orgId, {
|
|
27698
27581
|
noteType,
|
|
27699
27582
|
status
|
|
27700
27583
|
});
|
|
@@ -27725,9 +27608,6 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27725
27608
|
if (!movement) {
|
|
27726
27609
|
return { content: [{ type: "text", text: `Movement not found: "${movementId}"` }], isError: true };
|
|
27727
27610
|
}
|
|
27728
|
-
if (movement.orgId !== ctx.orgId) {
|
|
27729
|
-
return { content: [{ type: "text", text: `Movement "${movementId}" is not in the configured organization.` }], isError: true };
|
|
27730
|
-
}
|
|
27731
27611
|
const notes = await client.listMovementNotes(movementId, {
|
|
27732
27612
|
noteType,
|
|
27733
27613
|
status
|
|
@@ -27751,7 +27631,7 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27751
27631
|
systemId: external_exports.string().describe("The system ID")
|
|
27752
27632
|
},
|
|
27753
27633
|
async ({ systemId }) => {
|
|
27754
|
-
await
|
|
27634
|
+
await assertSystemExists(client, systemId);
|
|
27755
27635
|
const DOCUMENT_FETCH_LIMIT = 5e3;
|
|
27756
27636
|
const { notes: docs, hasMore: truncated } = await client.listAllSystemNotes(systemId, { noteType: "document" }, DOCUMENT_FETCH_LIMIT);
|
|
27757
27637
|
const visibleDocs = docs.filter((d) => d.status !== DISMISSED_NOTE_STATUS);
|
|
@@ -27777,16 +27657,13 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27777
27657
|
}
|
|
27778
27658
|
try {
|
|
27779
27659
|
if (orgId) {
|
|
27780
|
-
if (orgId !== ctx.orgId) {
|
|
27781
|
-
return { content: [{ type: "text", text: "Access denied: orgId does not match the configured organization." }], isError: true };
|
|
27782
|
-
}
|
|
27783
27660
|
const note2 = await client.getOrgNote(orgId, noteId);
|
|
27784
27661
|
if (!note2 || note2.organizationId !== orgId) {
|
|
27785
27662
|
return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
|
|
27786
27663
|
}
|
|
27787
27664
|
return { content: [{ type: "text", text: formatNoteDetail(note2) }] };
|
|
27788
27665
|
}
|
|
27789
|
-
await
|
|
27666
|
+
await assertSystemExists(client, systemId);
|
|
27790
27667
|
const note = await client.getNote(noteId);
|
|
27791
27668
|
if (!note) {
|
|
27792
27669
|
return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
|
|
@@ -27803,6 +27680,7 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27803
27680
|
"create_note",
|
|
27804
27681
|
"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.",
|
|
27805
27682
|
{
|
|
27683
|
+
orgId: external_exports.string().optional().describe("The organization ID \u2014 required for an org-level Note (when systemId, teamspaceId and movementId are all omitted). See list_organizations."),
|
|
27806
27684
|
systemId: external_exports.string().optional().describe("The system ID (omit for teamspace-, movement-, or org-level note)"),
|
|
27807
27685
|
teamspaceId: external_exports.string().optional().describe("The teamspace ID (omit for system-, movement-, or org-level note)"),
|
|
27808
27686
|
movementId: external_exports.string().min(1).optional().describe("The Movement ID (omit for system-, teamspace-, or org-level note)"),
|
|
@@ -27812,20 +27690,18 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27812
27690
|
revisionId: external_exports.string().optional().describe("Revision ID if this note is revision-scoped (requires systemId)"),
|
|
27813
27691
|
beatVersionId: external_exports.string().optional().describe("Beat Version ID if this note governs a specific planning increment (requires beatId)"),
|
|
27814
27692
|
rationale: external_exports.string().optional().describe("Why this note exists"),
|
|
27815
|
-
confidence: external_exports.coerce.number().min(0).max(1).optional().describe("Confidence level for assumptions (0-1)"),
|
|
27816
27693
|
affectsBeats: external_exports.array(external_exports.string()).optional().describe("Beat IDs this note impacts"),
|
|
27817
27694
|
dependsOnNotes: external_exports.array(external_exports.string()).optional().describe("Note IDs this note depends on (child \u2192 parent links for consolidation)"),
|
|
27818
27695
|
significance: external_exports.enum(DECISION_SIGNIFICANCE_VALUES2).optional().describe("Significance tier for decision Notes (strategic | structural | implementation). Defaults to implementation when unset. Only meaningful on noteType=decision.")
|
|
27819
27696
|
},
|
|
27820
|
-
async ({ systemId, teamspaceId, movementId, noteType, content, beatId, revisionId, beatVersionId, rationale,
|
|
27697
|
+
async ({ orgId, systemId, teamspaceId, movementId, noteType, content, beatId, revisionId, beatVersionId, rationale, affectsBeats, dependsOnNotes, significance }) => {
|
|
27821
27698
|
try {
|
|
27822
27699
|
if ([systemId, teamspaceId, movementId].filter(Boolean).length > 1) {
|
|
27823
27700
|
return { content: [{ type: "text", text: "Specify at most one of systemId, teamspaceId, or movementId." }], isError: true };
|
|
27824
27701
|
}
|
|
27825
|
-
const assumptionMeta = noteType === "assumption" && confidence !== void 0 ? { confidence } : void 0;
|
|
27826
27702
|
let note;
|
|
27827
27703
|
if (systemId) {
|
|
27828
|
-
await
|
|
27704
|
+
await assertSystemExists(client, systemId);
|
|
27829
27705
|
const projectCode = systemId.substring(0, 4).toUpperCase();
|
|
27830
27706
|
const noteId = await client.getNextNoteId(projectCode, systemId);
|
|
27831
27707
|
note = await client.createNote({
|
|
@@ -27841,7 +27717,6 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27841
27717
|
aiGenerated: false,
|
|
27842
27718
|
affectsBeats,
|
|
27843
27719
|
dependsOnNotes,
|
|
27844
|
-
assumptionMeta,
|
|
27845
27720
|
...noteType === "decision" && significance !== void 0 && { significance }
|
|
27846
27721
|
});
|
|
27847
27722
|
} else if (teamspaceId) {
|
|
@@ -27849,8 +27724,11 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27849
27724
|
if (!teamspace) {
|
|
27850
27725
|
return { content: [{ type: "text", text: `Teamspace not found: "${teamspaceId}"` }], isError: true };
|
|
27851
27726
|
}
|
|
27852
|
-
if (teamspace.orgId !==
|
|
27853
|
-
return {
|
|
27727
|
+
if (orgId && teamspace.orgId !== orgId) {
|
|
27728
|
+
return {
|
|
27729
|
+
content: [{ type: "text", text: "Teamspace belongs to a different organization" }],
|
|
27730
|
+
isError: true
|
|
27731
|
+
};
|
|
27854
27732
|
}
|
|
27855
27733
|
note = await client.createTeamspaceNote(teamspaceId, teamspace.slug, {
|
|
27856
27734
|
noteType,
|
|
@@ -27858,7 +27736,6 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27858
27736
|
rationale,
|
|
27859
27737
|
createdBy: ctx.user.userId,
|
|
27860
27738
|
aiGenerated: false,
|
|
27861
|
-
assumptionMeta,
|
|
27862
27739
|
...noteType === "decision" && significance !== void 0 && { significance }
|
|
27863
27740
|
});
|
|
27864
27741
|
} else if (movementId) {
|
|
@@ -27866,26 +27743,24 @@ function registerNoteTools(server, ctx, client) {
|
|
|
27866
27743
|
if (!movement) {
|
|
27867
27744
|
return { content: [{ type: "text", text: `Movement not found: "${movementId}"` }], isError: true };
|
|
27868
27745
|
}
|
|
27869
|
-
if (movement.orgId !== ctx.orgId) {
|
|
27870
|
-
return { content: [{ type: "text", text: `Movement "${movementId}" is not in the configured organization.` }], isError: true };
|
|
27871
|
-
}
|
|
27872
27746
|
note = await client.createMovementNote(movementId, movement.movementCode, {
|
|
27873
27747
|
noteType,
|
|
27874
27748
|
content,
|
|
27875
27749
|
rationale,
|
|
27876
27750
|
createdBy: ctx.user.userId,
|
|
27877
27751
|
aiGenerated: false,
|
|
27878
|
-
assumptionMeta,
|
|
27879
27752
|
...noteType === "decision" && significance !== void 0 && { significance }
|
|
27880
27753
|
});
|
|
27881
27754
|
} else {
|
|
27882
|
-
|
|
27755
|
+
if (!orgId) {
|
|
27756
|
+
return { content: [{ type: "text", text: "Provide orgId for an org-level Note, or scope the Note with systemId, teamspaceId, or movementId." }], isError: true };
|
|
27757
|
+
}
|
|
27758
|
+
note = await client.createOrgNote(orgId, {
|
|
27883
27759
|
noteType,
|
|
27884
27760
|
content,
|
|
27885
27761
|
rationale,
|
|
27886
27762
|
createdBy: ctx.user.userId,
|
|
27887
|
-
aiGenerated: false
|
|
27888
|
-
assumptionMeta
|
|
27763
|
+
aiGenerated: false
|
|
27889
27764
|
});
|
|
27890
27765
|
}
|
|
27891
27766
|
const text = formatNoteDetail(note);
|
|
@@ -27926,9 +27801,6 @@ ${text}` }] };
|
|
|
27926
27801
|
return { content: [{ type: "text", text: "Provide systemId or orgId (for access control)." }], isError: true };
|
|
27927
27802
|
}
|
|
27928
27803
|
if (orgId) {
|
|
27929
|
-
if (orgId !== ctx.orgId) {
|
|
27930
|
-
return { content: [{ type: "text", text: "Access denied: orgId does not match the configured organization." }], isError: true };
|
|
27931
|
-
}
|
|
27932
27804
|
if (beatId !== void 0) {
|
|
27933
27805
|
return { content: [{ type: "text", text: "beatId is not supported for org-level notes." }], isError: true };
|
|
27934
27806
|
}
|
|
@@ -27953,7 +27825,7 @@ ${text}` }] };
|
|
|
27953
27825
|
|
|
27954
27826
|
${formatNoteDetail(updated2)}` }] };
|
|
27955
27827
|
}
|
|
27956
|
-
await
|
|
27828
|
+
await assertSystemExists(client, systemId);
|
|
27957
27829
|
const updated = await client.updateNote(noteId, {
|
|
27958
27830
|
...noteType !== void 0 && { noteType },
|
|
27959
27831
|
status,
|
|
@@ -27995,7 +27867,7 @@ ${text}` }] };
|
|
|
27995
27867
|
},
|
|
27996
27868
|
async ({ systemId, noteIds, noteType, status, content, response, humanAssignee, agentAssignee }) => {
|
|
27997
27869
|
try {
|
|
27998
|
-
await
|
|
27870
|
+
await assertSystemExists(client, systemId);
|
|
27999
27871
|
const result = await client.bulkUpdateNotes(noteIds, {
|
|
28000
27872
|
...noteType !== void 0 && { noteType },
|
|
28001
27873
|
status,
|
|
@@ -28034,7 +27906,7 @@ ${text}` }] };
|
|
|
28034
27906
|
},
|
|
28035
27907
|
async ({ systemId, noteId, targetBeatId, targetRevisionId, reason }) => {
|
|
28036
27908
|
try {
|
|
28037
|
-
await
|
|
27909
|
+
await assertSystemExists(client, systemId);
|
|
28038
27910
|
const updated = await client.reassignNote(noteId, targetBeatId, reason, ctx.user.userId, targetRevisionId);
|
|
28039
27911
|
if (!updated) {
|
|
28040
27912
|
return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
|
|
@@ -28056,7 +27928,7 @@ ${text}` }] };
|
|
|
28056
27928
|
},
|
|
28057
27929
|
async ({ systemId, noteId, reason }) => {
|
|
28058
27930
|
try {
|
|
28059
|
-
await
|
|
27931
|
+
await assertSystemExists(client, systemId);
|
|
28060
27932
|
const note = await client.getNote(noteId);
|
|
28061
27933
|
if (!note || (note.systemId ?? note.projectId) !== systemId) {
|
|
28062
27934
|
return { content: [{ type: "text", text: `Note not found: "${noteId}"` }], isError: true };
|
|
@@ -28083,7 +27955,7 @@ ${text}` }] };
|
|
|
28083
27955
|
},
|
|
28084
27956
|
async ({ systemId, noteId, reason }) => {
|
|
28085
27957
|
try {
|
|
28086
|
-
await
|
|
27958
|
+
await assertSystemExists(client, systemId);
|
|
28087
27959
|
const note = await client.getNote(noteId);
|
|
28088
27960
|
if (!note || (note.systemId ?? note.projectId) !== systemId) {
|
|
28089
27961
|
return { content: [{ type: "text", text: `Document not found: "${noteId}"` }], isError: true };
|
|
@@ -28106,29 +27978,6 @@ ${text}` }] };
|
|
|
28106
27978
|
}
|
|
28107
27979
|
}
|
|
28108
27980
|
);
|
|
28109
|
-
server.tool(
|
|
28110
|
-
"list_key_assumptions",
|
|
28111
|
-
"Get key assumptions sorted by criticality (highest risk first)",
|
|
28112
|
-
{
|
|
28113
|
-
systemId: external_exports.string().describe("The system ID")
|
|
28114
|
-
},
|
|
28115
|
-
async ({ systemId }) => {
|
|
28116
|
-
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
28117
|
-
const assumptions = await client.getKeyAssumptions(systemId);
|
|
28118
|
-
if (assumptions.length === 0) {
|
|
28119
|
-
return { content: [{ type: "text", text: "No unvalidated assumptions found." }] };
|
|
28120
|
-
}
|
|
28121
|
-
const lines = assumptions.map((a, i) => {
|
|
28122
|
-
const conf = a.assumptionMeta?.confidence?.toFixed(2) ?? "?";
|
|
28123
|
-
const crit = a.criticality?.toFixed(2) ?? "?";
|
|
28124
|
-
return `${i + 1}. [confidence: ${conf}, criticality: ${crit}] ${a.content}`;
|
|
28125
|
-
});
|
|
28126
|
-
const text = `**${assumptions.length} Key Assumptions** (sorted by criticality)
|
|
28127
|
-
|
|
28128
|
-
${lines.join("\n")}`;
|
|
28129
|
-
return { content: [{ type: "text", text }] };
|
|
28130
|
-
}
|
|
28131
|
-
);
|
|
28132
27981
|
}
|
|
28133
27982
|
|
|
28134
27983
|
// ../../libs/harmonica-services/src/mcp/tools/notebook-tools.ts
|
|
@@ -28193,26 +28042,27 @@ function formatTree(node, depth = 0) {
|
|
|
28193
28042
|
function registerNotebookTools(server, ctx, client) {
|
|
28194
28043
|
server.tool(
|
|
28195
28044
|
"create_notebook",
|
|
28196
|
-
"Create a new Notebook in
|
|
28045
|
+
"Create a new Notebook in an organization. Optionally bind it to one or more scopes (org, teamspace, or system) at creation.",
|
|
28197
28046
|
{
|
|
28047
|
+
orgId: external_exports.string().describe("The organization ID \u2014 see list_organizations"),
|
|
28198
28048
|
title: external_exports.string().min(1).max(300).describe("Notebook title (1\u2013300 characters)"),
|
|
28199
28049
|
description: external_exports.string().max(2e4).optional().describe("Optional description (max 20 000 characters)"),
|
|
28200
28050
|
scopes: external_exports.array(scopeRefSchema).optional().describe("Optional initial scope bindings"),
|
|
28201
28051
|
createdBy: external_exports.string().optional().describe("Actor ID \u2014 defaults to the session user")
|
|
28202
28052
|
},
|
|
28203
|
-
async ({ title, description, scopes, createdBy }) => {
|
|
28053
|
+
async ({ orgId, title, description, scopes, createdBy }) => {
|
|
28204
28054
|
const actorId = createdBy ?? ctx.user.userId ?? "agent";
|
|
28205
28055
|
try {
|
|
28206
28056
|
const notebook = await client.createNotebook({
|
|
28207
28057
|
notebookId: `nb-${(0, import_crypto4.randomUUID)()}`,
|
|
28208
|
-
orgId
|
|
28058
|
+
orgId,
|
|
28209
28059
|
title,
|
|
28210
28060
|
description,
|
|
28211
28061
|
createdBy: actorId,
|
|
28212
28062
|
scopes
|
|
28213
28063
|
});
|
|
28214
28064
|
if (!notebook) {
|
|
28215
|
-
return { content: [{ type: "text", text: `Organization not found: "${
|
|
28065
|
+
return { content: [{ type: "text", text: `Organization not found: "${orgId}"` }], isError: true };
|
|
28216
28066
|
}
|
|
28217
28067
|
return { content: [{ type: "text", text: `Notebook created.
|
|
28218
28068
|
|
|
@@ -28232,7 +28082,7 @@ ${formatNotebook(notebook)}` }] };
|
|
|
28232
28082
|
async ({ notebookId }) => {
|
|
28233
28083
|
let notebook;
|
|
28234
28084
|
try {
|
|
28235
|
-
notebook = await
|
|
28085
|
+
notebook = await fetchNotebook(client, notebookId);
|
|
28236
28086
|
} catch (err) {
|
|
28237
28087
|
const message = err instanceof Error ? err.message : String(err);
|
|
28238
28088
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -28256,7 +28106,7 @@ ${formatNotebook(notebook)}` }] };
|
|
|
28256
28106
|
},
|
|
28257
28107
|
async ({ notebookId }) => {
|
|
28258
28108
|
try {
|
|
28259
|
-
await
|
|
28109
|
+
await fetchNotebook(client, notebookId);
|
|
28260
28110
|
} catch (err) {
|
|
28261
28111
|
const message = err instanceof Error ? err.message : String(err);
|
|
28262
28112
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -28291,7 +28141,7 @@ ${formatTree(tree.root)}` }] };
|
|
|
28291
28141
|
async ({ notebookId, title, content, parentNoteId, style }) => {
|
|
28292
28142
|
let notebook;
|
|
28293
28143
|
try {
|
|
28294
|
-
notebook = await
|
|
28144
|
+
notebook = await fetchNotebook(client, notebookId);
|
|
28295
28145
|
} catch (err) {
|
|
28296
28146
|
const message = err instanceof Error ? err.message : String(err);
|
|
28297
28147
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -28328,7 +28178,7 @@ ${formatTree(tree.root)}` }] };
|
|
|
28328
28178
|
const pageContent = buildPageNoteContent(title, content);
|
|
28329
28179
|
let note;
|
|
28330
28180
|
try {
|
|
28331
|
-
note = await client.createOrgNote(
|
|
28181
|
+
note = await client.createOrgNote(notebook.orgId, {
|
|
28332
28182
|
noteType: "context",
|
|
28333
28183
|
content: pageContent,
|
|
28334
28184
|
createdBy: actorId,
|
|
@@ -28347,7 +28197,7 @@ ${formatTree(tree.root)}` }] };
|
|
|
28347
28197
|
variant: style ?? "card",
|
|
28348
28198
|
position,
|
|
28349
28199
|
depth: placement.childDepth,
|
|
28350
|
-
orgId:
|
|
28200
|
+
orgId: notebook.orgId,
|
|
28351
28201
|
writeUsedByEdge: true
|
|
28352
28202
|
});
|
|
28353
28203
|
if (!addResult.success) {
|
|
@@ -28384,7 +28234,7 @@ ${formatTree(tree.root)}` }] };
|
|
|
28384
28234
|
async ({ notebookId, heading, parentNoteId, noteType, systemId, limit, order, significance }) => {
|
|
28385
28235
|
let notebook;
|
|
28386
28236
|
try {
|
|
28387
|
-
notebook = await
|
|
28237
|
+
notebook = await fetchNotebook(client, notebookId);
|
|
28388
28238
|
} catch (err) {
|
|
28389
28239
|
return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
|
|
28390
28240
|
}
|
|
@@ -28442,7 +28292,7 @@ ${formatTree(tree.root)}` }] };
|
|
|
28442
28292
|
heading,
|
|
28443
28293
|
position,
|
|
28444
28294
|
depth: placement.childDepth,
|
|
28445
|
-
orgId:
|
|
28295
|
+
orgId: notebook.orgId
|
|
28446
28296
|
});
|
|
28447
28297
|
if (!result.success) {
|
|
28448
28298
|
return { content: [{ type: "text", text: `Failed to add collection: ${result.error?.message ?? "unknown error"}` }], isError: true };
|
|
@@ -28472,7 +28322,7 @@ ${formatTree(tree.root)}` }] };
|
|
|
28472
28322
|
async ({ notebookId, elementId, parentNoteId, heading, noteType, systemId, limit, order, significance }) => {
|
|
28473
28323
|
let notebook;
|
|
28474
28324
|
try {
|
|
28475
|
-
notebook = await
|
|
28325
|
+
notebook = await fetchNotebook(client, notebookId);
|
|
28476
28326
|
} catch (err) {
|
|
28477
28327
|
return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
|
|
28478
28328
|
}
|
|
@@ -28543,7 +28393,7 @@ ${formatTree(tree.root)}` }] };
|
|
|
28543
28393
|
},
|
|
28544
28394
|
async ({ notebookId, title, description }) => {
|
|
28545
28395
|
try {
|
|
28546
|
-
await
|
|
28396
|
+
await fetchNotebook(client, notebookId);
|
|
28547
28397
|
} catch (err) {
|
|
28548
28398
|
const message = err instanceof Error ? err.message : String(err);
|
|
28549
28399
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -28571,7 +28421,7 @@ ${formatNotebook(updated)}` }] };
|
|
|
28571
28421
|
},
|
|
28572
28422
|
async ({ notebookId, scopes }) => {
|
|
28573
28423
|
try {
|
|
28574
|
-
await
|
|
28424
|
+
await fetchNotebook(client, notebookId);
|
|
28575
28425
|
} catch (err) {
|
|
28576
28426
|
const message = err instanceof Error ? err.message : String(err);
|
|
28577
28427
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -28628,7 +28478,7 @@ ${lines.join("\n")}` }] };
|
|
|
28628
28478
|
},
|
|
28629
28479
|
async ({ notebookId, status, reason }) => {
|
|
28630
28480
|
try {
|
|
28631
|
-
await
|
|
28481
|
+
await fetchNotebook(client, notebookId);
|
|
28632
28482
|
} catch (err) {
|
|
28633
28483
|
const message = err instanceof Error ? err.message : String(err);
|
|
28634
28484
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -28665,7 +28515,7 @@ ${formatNotebook(updated)}` }] };
|
|
|
28665
28515
|
},
|
|
28666
28516
|
async ({ notebookId, scopeType, scopeId, createdBy }) => {
|
|
28667
28517
|
try {
|
|
28668
|
-
await
|
|
28518
|
+
await fetchNotebook(client, notebookId);
|
|
28669
28519
|
} catch (err) {
|
|
28670
28520
|
const message = err instanceof Error ? err.message : String(err);
|
|
28671
28521
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -28693,7 +28543,7 @@ ${formatNotebook(updated)}` }] };
|
|
|
28693
28543
|
},
|
|
28694
28544
|
async ({ notebookId, scopeType, scopeId }) => {
|
|
28695
28545
|
try {
|
|
28696
|
-
await
|
|
28546
|
+
await fetchNotebook(client, notebookId);
|
|
28697
28547
|
} catch (err) {
|
|
28698
28548
|
const message = err instanceof Error ? err.message : String(err);
|
|
28699
28549
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -28738,7 +28588,7 @@ ${lines.join("\n")}` }] };
|
|
|
28738
28588
|
// ../../libs/harmonica-services/src/mcp/tools/onboarding-tools.ts
|
|
28739
28589
|
var import_node_crypto = require("node:crypto");
|
|
28740
28590
|
var OnboardingNoteInputSchema = external_exports.object({
|
|
28741
|
-
noteType: external_exports.enum(["context", "
|
|
28591
|
+
noteType: external_exports.enum(["context", "constraint", "guidance", "decision"]),
|
|
28742
28592
|
content: external_exports.string().min(1),
|
|
28743
28593
|
rationale: external_exports.string().optional(),
|
|
28744
28594
|
confidence: external_exports.number().min(0).max(1).optional()
|
|
@@ -28760,12 +28610,13 @@ function registerOnboardingTools(server, ctx, client) {
|
|
|
28760
28610
|
"Returns the full analysis and gap classification for pre-flight review \u2014 nothing is persisted yet."
|
|
28761
28611
|
].join(" "),
|
|
28762
28612
|
{
|
|
28613
|
+
orgId: external_exports.string().describe("The organization ID \u2014 see list_organizations"),
|
|
28763
28614
|
clientName: external_exports.string().min(1).max(200).describe("The client company name \u2014 used to check for duplicate Teamspaces"),
|
|
28764
28615
|
pmContext: external_exports.string().min(1).describe('What the PM said to initiate the session (e.g. "I just had an intro call with...")'),
|
|
28765
28616
|
documentContent: external_exports.string().min(1).describe("Full text content of all attached documents, concatenated")
|
|
28766
28617
|
},
|
|
28767
|
-
async ({ clientName, pmContext, documentContent }) => {
|
|
28768
|
-
const result = await client.initiateOnboarding(
|
|
28618
|
+
async ({ orgId, clientName, pmContext, documentContent }) => {
|
|
28619
|
+
const result = await client.initiateOnboarding(orgId, { clientName, pmContext, documentContent });
|
|
28769
28620
|
if (result.isDuplicate) {
|
|
28770
28621
|
return {
|
|
28771
28622
|
content: [{
|
|
@@ -28816,7 +28667,7 @@ function registerOnboardingTools(server, ctx, client) {
|
|
|
28816
28667
|
analysisJson: external_exports.string().optional().describe("JSON-serialized DiscoveryAnalysis from initiate_teamspace_onboarding. Pass this to produce Beats grounded in the actual discovery data.")
|
|
28817
28668
|
},
|
|
28818
28669
|
async ({ systemId, systemTitle, systemDescription, analysisJson }) => {
|
|
28819
|
-
await
|
|
28670
|
+
await assertSystemExists(client, systemId);
|
|
28820
28671
|
const result = await client.draftOnboardingBeats(systemId, { systemTitle, systemDescription, analysisJson });
|
|
28821
28672
|
if (!result.suggestions || result.suggestions.length === 0) {
|
|
28822
28673
|
return {
|
|
@@ -28842,6 +28693,10 @@ function registerOnboardingTools(server, ctx, client) {
|
|
|
28842
28693
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
28843
28694
|
}
|
|
28844
28695
|
);
|
|
28696
|
+
const OnboardingTrackInputSchema = external_exports.object({
|
|
28697
|
+
title: external_exports.string().min(1).max(300),
|
|
28698
|
+
description: external_exports.string().max(2e3).optional()
|
|
28699
|
+
});
|
|
28845
28700
|
server.tool(
|
|
28846
28701
|
"create_teamspace_onboarding_batch",
|
|
28847
28702
|
[
|
|
@@ -28852,22 +28707,40 @@ function registerOnboardingTools(server, ctx, client) {
|
|
|
28852
28707
|
"If any step fails, the receipt reports what succeeded and where creation stopped."
|
|
28853
28708
|
].join(" "),
|
|
28854
28709
|
{
|
|
28710
|
+
orgId: external_exports.string().describe("The organization ID \u2014 see list_organizations"),
|
|
28855
28711
|
teamspaceName: external_exports.string().min(1).max(200).describe("Teamspace display name"),
|
|
28856
|
-
|
|
28857
|
-
teamspaceNotes: external_exports.array(OnboardingNoteInputSchema).describe("Constraints, assumptions, and context notes at the Teamspace/system level")
|
|
28712
|
+
systems: external_exports.array(OnboardingSystemInputSchema).min(1).describe("Phase 1 systems to create, each linked to the Teamspace"),
|
|
28713
|
+
teamspaceNotes: external_exports.array(OnboardingNoteInputSchema).describe("Constraints, assumptions, and context notes at the Teamspace/system level"),
|
|
28714
|
+
account: external_exports.object({ name: external_exports.string().min(1).max(300), description: external_exports.string().max(2e3).optional() }).optional().describe("New client Account to create for this engagement. Omit to skip commercial scaffolding."),
|
|
28715
|
+
movement: external_exports.object({ title: external_exports.string().min(1).max(300), description: external_exports.string().max(2e3).optional() }).optional().describe("SOW/engagement Movement to create under the Account. Only used when account is provided."),
|
|
28716
|
+
tracks: external_exports.array(OnboardingTrackInputSchema).max(10).optional().describe("Tracks (phases/workstreams) to create under the Movement, capped at 10. Defaults to one Track named after the first System when omitted or empty."),
|
|
28717
|
+
drop: external_exports.object({ name: external_exports.string().min(1).max(200), description: external_exports.string().max(2e3).optional() }).optional().describe('Optional initial Drop (e.g. "MVP") to create under the Account. Only created when explicitly provided.')
|
|
28858
28718
|
},
|
|
28859
28719
|
async ({
|
|
28720
|
+
orgId,
|
|
28860
28721
|
teamspaceName,
|
|
28861
|
-
|
|
28862
|
-
teamspaceNotes
|
|
28722
|
+
systems,
|
|
28723
|
+
teamspaceNotes,
|
|
28724
|
+
account,
|
|
28725
|
+
movement,
|
|
28726
|
+
tracks,
|
|
28727
|
+
drop
|
|
28863
28728
|
}) => {
|
|
28729
|
+
if ((movement || tracks && tracks.length > 0 || drop) && !account) {
|
|
28730
|
+
return {
|
|
28731
|
+
content: [{
|
|
28732
|
+
type: "text",
|
|
28733
|
+
text: "\u26A0\uFE0F movement, tracks, and drop are only created when account is provided. Add an account or omit the commercial fields."
|
|
28734
|
+
}]
|
|
28735
|
+
};
|
|
28736
|
+
}
|
|
28864
28737
|
const lines = [];
|
|
28865
28738
|
let teamspace;
|
|
28866
28739
|
try {
|
|
28867
28740
|
teamspace = await client.createTeamspace({
|
|
28868
28741
|
teamspaceId: (0, import_node_crypto.randomUUID)(),
|
|
28869
28742
|
slug: teamspaceName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""),
|
|
28870
|
-
orgId
|
|
28743
|
+
orgId,
|
|
28871
28744
|
name: teamspaceName,
|
|
28872
28745
|
createdBy: ctx.user?.userId
|
|
28873
28746
|
});
|
|
@@ -28879,31 +28752,31 @@ Teamspace: [${teamspace.teamspaceId}] ${teamspace.name}`);
|
|
|
28879
28752
|
Error: ${msg}`);
|
|
28880
28753
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
28881
28754
|
}
|
|
28882
|
-
const
|
|
28883
|
-
for (const p of
|
|
28755
|
+
const createdSystems = [];
|
|
28756
|
+
for (const p of systems) {
|
|
28884
28757
|
try {
|
|
28885
28758
|
const project = await client.createSystem({
|
|
28886
28759
|
systemId: (0, import_node_crypto.randomUUID)(),
|
|
28887
|
-
orgId
|
|
28760
|
+
orgId,
|
|
28888
28761
|
teamspaceId: teamspace.teamspaceId,
|
|
28889
28762
|
ownerUserId: ctx.user?.userId ?? "system",
|
|
28890
28763
|
title: p.title,
|
|
28891
28764
|
description: p.description
|
|
28892
28765
|
});
|
|
28893
|
-
|
|
28766
|
+
createdSystems.push({ systemId: project.systemId, title: project.title });
|
|
28894
28767
|
} catch (err) {
|
|
28895
28768
|
const msg = err instanceof Error ? err.message : String(err);
|
|
28896
28769
|
lines.push(`
|
|
28897
|
-
Systems created: ${
|
|
28770
|
+
Systems created: ${createdSystems.length}`);
|
|
28898
28771
|
lines.unshift(`\u26A0\uFE0F Onboarding batch partially created \u2014 stopped at: system "${p.title}"
|
|
28899
28772
|
Error: ${msg}`);
|
|
28900
28773
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
28901
28774
|
}
|
|
28902
28775
|
}
|
|
28903
|
-
if (
|
|
28776
|
+
if (createdSystems.length > 0) {
|
|
28904
28777
|
lines.push(`
|
|
28905
|
-
Systems (${
|
|
28906
|
-
for (const p of
|
|
28778
|
+
Systems (${createdSystems.length}):`);
|
|
28779
|
+
for (const p of createdSystems) lines.push(` [${p.systemId}] ${p.title}`);
|
|
28907
28780
|
}
|
|
28908
28781
|
const createdNotes = [];
|
|
28909
28782
|
const teamspaceSlug = teamspace.slug ?? teamspaceName.toLowerCase().replace(/\s+/g, "-");
|
|
@@ -28922,9 +28795,9 @@ Systems (${createdProjects.length}):`);
|
|
|
28922
28795
|
} catch {
|
|
28923
28796
|
}
|
|
28924
28797
|
}
|
|
28925
|
-
for (let i = 0; i <
|
|
28926
|
-
const project =
|
|
28927
|
-
for (const note of
|
|
28798
|
+
for (let i = 0; i < createdSystems.length; i++) {
|
|
28799
|
+
const project = createdSystems[i];
|
|
28800
|
+
for (const note of systems[i]?.notes ?? []) {
|
|
28928
28801
|
try {
|
|
28929
28802
|
const n = await client.createNote({
|
|
28930
28803
|
noteId: `N-TEMP-${(0, import_node_crypto.randomUUID)()}`,
|
|
@@ -28944,6 +28817,93 @@ Systems (${createdProjects.length}):`);
|
|
|
28944
28817
|
}
|
|
28945
28818
|
lines.push(`
|
|
28946
28819
|
Notes filed: ${createdNotes.length}`);
|
|
28820
|
+
if (account) {
|
|
28821
|
+
let createdAccountId;
|
|
28822
|
+
try {
|
|
28823
|
+
const createdAccount = await client.createAccount({
|
|
28824
|
+
orgId,
|
|
28825
|
+
name: account.name,
|
|
28826
|
+
description: account.description,
|
|
28827
|
+
accountType: "client",
|
|
28828
|
+
createdBy: ctx.user?.userId
|
|
28829
|
+
});
|
|
28830
|
+
createdAccountId = createdAccount.accountId;
|
|
28831
|
+
lines.push(`
|
|
28832
|
+
Account: [${createdAccount.accountId}] ${createdAccount.name}`);
|
|
28833
|
+
} catch (err) {
|
|
28834
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
28835
|
+
lines.unshift(`\u26A0\uFE0F Onboarding batch partially created \u2014 stopped at: account
|
|
28836
|
+
Error: ${msg}`);
|
|
28837
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
28838
|
+
}
|
|
28839
|
+
if (movement && createdAccountId) {
|
|
28840
|
+
let createdMovementId;
|
|
28841
|
+
try {
|
|
28842
|
+
const createdMovement = await client.createMovement({
|
|
28843
|
+
teamspaceId: teamspace.teamspaceId,
|
|
28844
|
+
title: movement.title,
|
|
28845
|
+
description: movement.description,
|
|
28846
|
+
accountId: createdAccountId,
|
|
28847
|
+
createdBy: ctx.user?.userId
|
|
28848
|
+
});
|
|
28849
|
+
createdMovementId = createdMovement.movementId;
|
|
28850
|
+
lines.push(`
|
|
28851
|
+
Movement: [${createdMovement.movementId}] ${createdMovement.title}`);
|
|
28852
|
+
} catch (err) {
|
|
28853
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
28854
|
+
lines.unshift(`\u26A0\uFE0F Onboarding batch partially created \u2014 stopped at: movement
|
|
28855
|
+
Error: ${msg}`);
|
|
28856
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
28857
|
+
}
|
|
28858
|
+
const MAX_TRACKS = 10;
|
|
28859
|
+
const trackInputs = (tracks ?? []).slice(0, MAX_TRACKS);
|
|
28860
|
+
const fallbackTitle = createdSystems[0]?.title ?? "Phase 1";
|
|
28861
|
+
const tracksToCreate = trackInputs.length > 0 ? trackInputs : [{ title: fallbackTitle }];
|
|
28862
|
+
const createdTracks = [];
|
|
28863
|
+
for (const trackInput of tracksToCreate) {
|
|
28864
|
+
try {
|
|
28865
|
+
const track = await client.createDeliverableGroup({
|
|
28866
|
+
orgId,
|
|
28867
|
+
teamspaceId: teamspace.teamspaceId,
|
|
28868
|
+
movementId: createdMovementId,
|
|
28869
|
+
title: trackInput.title,
|
|
28870
|
+
description: trackInput.description,
|
|
28871
|
+
vuRate: 0,
|
|
28872
|
+
createdBy: ctx.user?.userId
|
|
28873
|
+
});
|
|
28874
|
+
createdTracks.push({ id: track.deliverableGroupId, title: track.title });
|
|
28875
|
+
} catch (err) {
|
|
28876
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
28877
|
+
lines.unshift(`\u26A0\uFE0F Onboarding batch partially created \u2014 stopped at: track:${trackInput.title}
|
|
28878
|
+
Error: ${msg}`);
|
|
28879
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
28880
|
+
}
|
|
28881
|
+
}
|
|
28882
|
+
if (createdTracks.length > 0) {
|
|
28883
|
+
lines.push(`
|
|
28884
|
+
Tracks (${createdTracks.length}):`);
|
|
28885
|
+
for (const t of createdTracks) lines.push(` [${t.id}] ${t.title}`);
|
|
28886
|
+
}
|
|
28887
|
+
}
|
|
28888
|
+
if (drop && createdAccountId) {
|
|
28889
|
+
try {
|
|
28890
|
+
const createdDrop = await client.createDrop({
|
|
28891
|
+
accountId: createdAccountId,
|
|
28892
|
+
teamspaceId: teamspace.teamspaceId,
|
|
28893
|
+
name: drop.name,
|
|
28894
|
+
description: drop.description,
|
|
28895
|
+
createdBy: ctx.user?.userId
|
|
28896
|
+
});
|
|
28897
|
+
lines.push(`
|
|
28898
|
+
Drop: [${createdDrop.dropId}] ${createdDrop.name}`);
|
|
28899
|
+
} catch (err) {
|
|
28900
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
28901
|
+
lines.unshift(`\u26A0\uFE0F Onboarding batch partially created \u2014 stopped at: drop
|
|
28902
|
+
Error: ${msg}`);
|
|
28903
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
28904
|
+
}
|
|
28905
|
+
}
|
|
28906
|
+
}
|
|
28947
28907
|
lines.unshift(`\u2705 Onboarding batch created successfully.`);
|
|
28948
28908
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
28949
28909
|
}
|
|
@@ -29114,10 +29074,7 @@ function registerOrganizationTools(server, ctx, client) {
|
|
|
29114
29074
|
if (!system) {
|
|
29115
29075
|
return { content: [{ type: "text", text: `System not found: ${systemId}` }], isError: true };
|
|
29116
29076
|
}
|
|
29117
|
-
|
|
29118
|
-
return { content: [{ type: "text", text: `System "${systemId}" does not belong to the configured organization.` }], isError: true };
|
|
29119
|
-
}
|
|
29120
|
-
const result = await client.transferSystem(ctx.orgId, systemId, targetOrgId);
|
|
29077
|
+
const result = await client.transferSystem(system.orgId, systemId, targetOrgId);
|
|
29121
29078
|
if (result.error) {
|
|
29122
29079
|
return { content: [{ type: "text", text: result.error }], isError: true };
|
|
29123
29080
|
}
|
|
@@ -29224,7 +29181,7 @@ async function pollForCheck3(client, taskId) {
|
|
|
29224
29181
|
}
|
|
29225
29182
|
consecutiveErrors = 0;
|
|
29226
29183
|
if (task.status === "completed") {
|
|
29227
|
-
if (!task.result) throw new Error(`Task ${taskId}
|
|
29184
|
+
if (!task.result) throw new Error(`async:completed:no-result Task ${taskId} succeeded on the async path but the scorecard is not yet readable on the blocking path \u2014 use list_checks or get_job_status to retrieve it.`);
|
|
29228
29185
|
return task.result;
|
|
29229
29186
|
}
|
|
29230
29187
|
if (task.status === "failed") throw new Error(`Beat version quality check failed: ${task.error ?? "unknown error"}`);
|
|
@@ -29268,7 +29225,7 @@ function registerPlanQualityTools(server, ctx, client) {
|
|
|
29268
29225
|
isError: true
|
|
29269
29226
|
};
|
|
29270
29227
|
}
|
|
29271
|
-
await
|
|
29228
|
+
await assertSystemExists(client, systemId);
|
|
29272
29229
|
const opts = branch || localPath ? { branch, localPath } : void 0;
|
|
29273
29230
|
const task = await client.runCheck(systemId, "beat_version_quality", beatVersionId, opts);
|
|
29274
29231
|
if (!wait) {
|
|
@@ -29415,7 +29372,7 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
|
|
|
29415
29372
|
},
|
|
29416
29373
|
async ({ systemId, wait }) => {
|
|
29417
29374
|
try {
|
|
29418
|
-
await
|
|
29375
|
+
await assertSystemExists(client, systemId);
|
|
29419
29376
|
const { taskId } = await client.runCheck(systemId, "portfolio_coherence", systemId);
|
|
29420
29377
|
if (!wait) {
|
|
29421
29378
|
return {
|
|
@@ -29452,7 +29409,7 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
|
|
|
29452
29409
|
const archivedBeatIds = [];
|
|
29453
29410
|
let totalNotesReassigned = 0;
|
|
29454
29411
|
try {
|
|
29455
|
-
await
|
|
29412
|
+
await assertSystemExists(client, systemId);
|
|
29456
29413
|
const uniqueBeatIds = [...new Set(beatIds)];
|
|
29457
29414
|
const resolvedPrimaryId = primaryBeatId ?? uniqueBeatIds[0];
|
|
29458
29415
|
if (!uniqueBeatIds.includes(resolvedPrimaryId)) {
|
|
@@ -29656,7 +29613,7 @@ function registerRevisionLifecycleTools(server, ctx, client) {
|
|
|
29656
29613
|
},
|
|
29657
29614
|
async ({ beatId, title, description, changeSummary, beatVersionId, baseRevisionId, tags, priority, estimatedEffort, humanAssignee, agentAssignee }) => {
|
|
29658
29615
|
try {
|
|
29659
|
-
await
|
|
29616
|
+
await assertBeatExists(client, beatId);
|
|
29660
29617
|
const beat = await client.getBeat(beatId);
|
|
29661
29618
|
if (!beat) {
|
|
29662
29619
|
return { content: [{ type: "text", text: `Beat not found: "${beatId}"` }], isError: true };
|
|
@@ -29711,8 +29668,8 @@ function registerRevisionLifecycleTools(server, ctx, client) {
|
|
|
29711
29668
|
}
|
|
29712
29669
|
if (parentBv && !parentBv.targetDropId) {
|
|
29713
29670
|
try {
|
|
29714
|
-
const
|
|
29715
|
-
const accountId =
|
|
29671
|
+
const system = await client.getSystem(parentBv.systemId);
|
|
29672
|
+
const accountId = system?.accountId;
|
|
29716
29673
|
if (accountId) {
|
|
29717
29674
|
const draftDrops = await client.listAccountDrops(accountId, { state: "draft" });
|
|
29718
29675
|
lines.push("");
|
|
@@ -29735,6 +29692,21 @@ function registerRevisionLifecycleTools(server, ctx, client) {
|
|
|
29735
29692
|
}
|
|
29736
29693
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
29737
29694
|
} catch (err) {
|
|
29695
|
+
if (err instanceof Error && err.name === "BeatVersionPlanQualityError" && "failure" in err && isRenderableGateFailure(err.failure)) {
|
|
29696
|
+
const f = err.failure;
|
|
29697
|
+
const isStale = f.reason === "stale_check_hash" || f.reason === "stale_check_timestamp";
|
|
29698
|
+
const lines = [
|
|
29699
|
+
`**Gate:** beat_version_quality`,
|
|
29700
|
+
`**Status:** ${isStale ? "stale" : "failed"} (${f.reason})`,
|
|
29701
|
+
`**Detail:** ${f.message}`
|
|
29702
|
+
];
|
|
29703
|
+
if (f.entityId) lines.push(`**Beat Version:** ${f.entityId}`);
|
|
29704
|
+
if (f.remedy) lines.push(`**Action:** ${f.remedy}`);
|
|
29705
|
+
return {
|
|
29706
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
29707
|
+
isError: true
|
|
29708
|
+
};
|
|
29709
|
+
}
|
|
29738
29710
|
const message = err instanceof Error ? err.message : String(err);
|
|
29739
29711
|
return { content: [{ type: "text", text: `Failed to create revision: ${message}` }], isError: true };
|
|
29740
29712
|
}
|
|
@@ -29761,7 +29733,7 @@ function registerRevisionLifecycleTools(server, ctx, client) {
|
|
|
29761
29733
|
if (!revision) {
|
|
29762
29734
|
return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
|
|
29763
29735
|
}
|
|
29764
|
-
await
|
|
29736
|
+
await assertBeatExists(client, revision.beatId);
|
|
29765
29737
|
if (typeof beatVersionId === "string") {
|
|
29766
29738
|
const bv = await client.getBeatVersion(beatVersionId);
|
|
29767
29739
|
if (!bv) {
|
|
@@ -29830,7 +29802,7 @@ function registerRevisionLifecycleTools(server, ctx, client) {
|
|
|
29830
29802
|
if (revision.status === targetState) {
|
|
29831
29803
|
return { content: [{ type: "text", text: `Revision is already at ${targetState}.` }], isError: true };
|
|
29832
29804
|
}
|
|
29833
|
-
await
|
|
29805
|
+
await assertBeatExists(client, revision.beatId);
|
|
29834
29806
|
const transitionOptions = {
|
|
29835
29807
|
actor: { type: "human", id: ctx.user.userId, name: ctx.user.name, email: ctx.user.email },
|
|
29836
29808
|
trigger: "human_action",
|
|
@@ -29880,7 +29852,7 @@ Accepts the full PR metadata (number, url, branch, state, optional mergeSha). Fi
|
|
|
29880
29852
|
if (!revision) {
|
|
29881
29853
|
return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
|
|
29882
29854
|
}
|
|
29883
|
-
await
|
|
29855
|
+
await assertBeatExists(client, revision.beatId);
|
|
29884
29856
|
if (!revision.scm && (!url || !branch)) {
|
|
29885
29857
|
return {
|
|
29886
29858
|
content: [{ type: "text", text: "This Revision has no existing SCM record. Provide url and branch to retro-attach a PR from scratch." }],
|
|
@@ -29928,7 +29900,7 @@ Accepts the full PR metadata (number, url, branch, state, optional mergeSha). Fi
|
|
|
29928
29900
|
if (!revision) {
|
|
29929
29901
|
return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
|
|
29930
29902
|
}
|
|
29931
|
-
await
|
|
29903
|
+
await assertBeatExists(client, revision.beatId);
|
|
29932
29904
|
const latitude = await checkDeliveryStep(client, revisionId, "openPullRequest");
|
|
29933
29905
|
if ("refusal" in latitude) {
|
|
29934
29906
|
return { content: [{ type: "text", text: latitude.refusal }], isError: true };
|
|
@@ -29944,13 +29916,13 @@ Accepts the full PR metadata (number, url, branch, state, optional mergeSha). Fi
|
|
|
29944
29916
|
if (revision.scm) {
|
|
29945
29917
|
lines2.push("", `\u26A0 This revision already has a linked PR (#${revision.scm.number} \u2014 ${revision.scm.url}). create_pr_for_revision would be rejected.`);
|
|
29946
29918
|
} else {
|
|
29947
|
-
const
|
|
29948
|
-
if (!
|
|
29949
|
-
lines2.push("", "\u26A0 The
|
|
29919
|
+
const system = await client.getSystem(revision.systemId);
|
|
29920
|
+
if (!system?.repoOwner || !system?.repoName || !system?.repoDefaultBranch) {
|
|
29921
|
+
lines2.push("", "\u26A0 The system has no repo configured (repoOwner/repoName/repoDefaultBranch). create_pr_for_revision would be rejected.");
|
|
29950
29922
|
} else {
|
|
29951
29923
|
lines2.push(
|
|
29952
29924
|
"",
|
|
29953
|
-
`Would open a draft PR against \`${
|
|
29925
|
+
`Would open a draft PR against \`${system.repoOwner}/${system.repoName}:${system.repoDefaultBranch}\` on a new \`agent/${shortId}-\u2026\` branch, with body starting \`Harmonica: ${revisionId}\`.`
|
|
29954
29926
|
);
|
|
29955
29927
|
}
|
|
29956
29928
|
}
|
|
@@ -29995,7 +29967,7 @@ Accepts the full PR metadata (number, url, branch, state, optional mergeSha). Fi
|
|
|
29995
29967
|
if (!revision) {
|
|
29996
29968
|
return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
|
|
29997
29969
|
}
|
|
29998
|
-
await
|
|
29970
|
+
await assertBeatExists(client, revision.beatId);
|
|
29999
29971
|
const latitude = await checkDeliveryStep(client, revisionId, "requestAgentReview");
|
|
30000
29972
|
if ("refusal" in latitude) {
|
|
30001
29973
|
return { content: [{ type: "text", text: latitude.refusal }], isError: true };
|
|
@@ -30025,7 +29997,7 @@ ${latitude.notice}` }] };
|
|
|
30025
29997
|
if (!revision) {
|
|
30026
29998
|
return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
|
|
30027
29999
|
}
|
|
30028
|
-
await
|
|
30000
|
+
await assertBeatExists(client, revision.beatId);
|
|
30029
30001
|
const result = await client.getPrDiff(revisionId);
|
|
30030
30002
|
if (!result.success) {
|
|
30031
30003
|
return { content: [{ type: "text", text: `Failed to get diff (${result.error.code}): ${result.error.message}` }], isError: true };
|
|
@@ -30059,7 +30031,7 @@ Line-level comments are optional \u2014 include them to anchor feedback to speci
|
|
|
30059
30031
|
if (!revision) {
|
|
30060
30032
|
return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
|
|
30061
30033
|
}
|
|
30062
|
-
await
|
|
30034
|
+
await assertBeatExists(client, revision.beatId);
|
|
30063
30035
|
const result = await client.postPrReview(revisionId, { prNumber: 0, event, body, comments });
|
|
30064
30036
|
if (!result.success) {
|
|
30065
30037
|
return { content: [{ type: "text", text: `Failed to post review (${result.error.code}): ${result.error.message}` }], isError: true };
|
|
@@ -30095,7 +30067,7 @@ Error codes: PR_NOT_OPEN (draft or already merged/closed), MERGE_FAILED (GitHub
|
|
|
30095
30067
|
if (!revision) {
|
|
30096
30068
|
return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
|
|
30097
30069
|
}
|
|
30098
|
-
await
|
|
30070
|
+
await assertBeatExists(client, revision.beatId);
|
|
30099
30071
|
const options = mergeMethod !== void 0 || commitTitle !== void 0 ? { mergeMethod, commitTitle } : void 0;
|
|
30100
30072
|
const result = await client.mergePr(revisionId, options);
|
|
30101
30073
|
if (!result.success) {
|
|
@@ -30129,7 +30101,7 @@ Error code: PR_NOT_DRAFT (the PR is already ready for review or was never a draf
|
|
|
30129
30101
|
if (!revision) {
|
|
30130
30102
|
return { content: [{ type: "text", text: `Revision not found: "${revisionId}"` }], isError: true };
|
|
30131
30103
|
}
|
|
30132
|
-
await
|
|
30104
|
+
await assertBeatExists(client, revision.beatId);
|
|
30133
30105
|
const result = await client.markPrReadyForReview(revisionId);
|
|
30134
30106
|
if (!result.success) {
|
|
30135
30107
|
return { content: [{ type: "text", text: `Failed to mark PR ready (${result.error.code}): ${result.error.message}` }], isError: true };
|
|
@@ -30220,7 +30192,7 @@ function registerRevisionQualityTools(server, ctx, client) {
|
|
|
30220
30192
|
isError: true
|
|
30221
30193
|
};
|
|
30222
30194
|
}
|
|
30223
|
-
await
|
|
30195
|
+
await assertSystemExists(client, systemId);
|
|
30224
30196
|
await assertRevisionInSystem(client, revisionId, systemId);
|
|
30225
30197
|
const task = await client.runCheck(systemId, "revision_quality", revisionId);
|
|
30226
30198
|
if (!wait) {
|
|
@@ -30526,7 +30498,7 @@ function registerSnapshotTools(server, ctx, client) {
|
|
|
30526
30498
|
"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.",
|
|
30527
30499
|
{ systemId: external_exports.string().describe("The system ID to export") },
|
|
30528
30500
|
async ({ systemId }) => {
|
|
30529
|
-
await
|
|
30501
|
+
await assertSystemExists(client, systemId);
|
|
30530
30502
|
const result = await client.exportSystemSnapshot(systemId);
|
|
30531
30503
|
if (typeof result === "string") {
|
|
30532
30504
|
if (!result.startsWith("https://")) {
|
|
@@ -30586,8 +30558,8 @@ function registerSnapshotTools(server, ctx, client) {
|
|
|
30586
30558
|
targetTeamspaceId: external_exports.string().min(1).optional().describe(
|
|
30587
30559
|
"Remap all teamspace references (Drops, Deliverables, Project record) to this teamspace ID in the target environment. Required when the source and target environments use different teamspace UUIDs. When omitted and the snapshot contains Drops or Deliverables, a warning is included in the import summary."
|
|
30588
30560
|
),
|
|
30589
|
-
targetOrgId: external_exports.string().min(1).
|
|
30590
|
-
"Import all records under this org ID
|
|
30561
|
+
targetOrgId: external_exports.string().min(1).describe(
|
|
30562
|
+
"Import all records under this org ID. Named explicitly rather than defaulted: a snapshot carries the org UUID of wherever it was taken, which differs between environments, and there is no session org to fall back to. See list_organizations."
|
|
30591
30563
|
)
|
|
30592
30564
|
},
|
|
30593
30565
|
async ({ snapshot: snapshotInput, targetTeamspaceId, targetOrgId }) => {
|
|
@@ -30603,7 +30575,7 @@ function registerSnapshotTools(server, ctx, client) {
|
|
|
30603
30575
|
};
|
|
30604
30576
|
}
|
|
30605
30577
|
const trimmed = snapshotInput.trim();
|
|
30606
|
-
const importOptions = { targetOrgId
|
|
30578
|
+
const importOptions = { targetOrgId, targetUserId: ctx.user.userId, targetTeamspaceId };
|
|
30607
30579
|
if (trimmed.startsWith("https://")) {
|
|
30608
30580
|
if (isPrivateHost(trimmed)) {
|
|
30609
30581
|
throw new Error("Snapshot URL must point to a public host \u2014 private, link-local, and loopback addresses are not permitted.");
|
|
@@ -30778,7 +30750,7 @@ function registerSubscriptionTools(server, ctx, client) {
|
|
|
30778
30750
|
},
|
|
30779
30751
|
async ({ entityType, entityId, systemId }) => {
|
|
30780
30752
|
try {
|
|
30781
|
-
await
|
|
30753
|
+
await assertSystemExists(client, systemId);
|
|
30782
30754
|
await client.subscribe(ctx.user.userId, entityType, entityId, systemId, "manual");
|
|
30783
30755
|
return {
|
|
30784
30756
|
content: [{
|
|
@@ -30869,9 +30841,6 @@ function registerSystemLifecycleTools(server, ctx, client) {
|
|
|
30869
30841
|
if (!system) {
|
|
30870
30842
|
return { content: [{ type: "text", text: `System not found: "${systemId}"` }], isError: true };
|
|
30871
30843
|
}
|
|
30872
|
-
if (system.orgId !== ctx.orgId) {
|
|
30873
|
-
return { content: [{ type: "text", text: `System "${systemId}" is not in this organization` }], isError: true };
|
|
30874
|
-
}
|
|
30875
30844
|
const result = await client.transitionSystemLifecycleState(systemId, targetState, {
|
|
30876
30845
|
actor: { type: "human", id: ctx.user.userId, name: ctx.user.name },
|
|
30877
30846
|
reason,
|
|
@@ -30916,9 +30885,9 @@ function accountLine(_request, resolved) {
|
|
|
30916
30885
|
}
|
|
30917
30886
|
var PROJECT_EMBEDDING_FIELDS = ["title", "description", "strategy"];
|
|
30918
30887
|
function registerSystemTools(server, ctx, client) {
|
|
30919
|
-
const listSystemsHandler = async ({ teamspaceId }) => {
|
|
30888
|
+
const listSystemsHandler = async ({ orgId, teamspaceId }) => {
|
|
30920
30889
|
try {
|
|
30921
|
-
const projects = await client.listOrgSystems(
|
|
30890
|
+
const projects = await client.listOrgSystems(orgId);
|
|
30922
30891
|
const trimmed = teamspaceId?.trim();
|
|
30923
30892
|
const filtered = trimmed ? projects.filter((p) => p.teamspaceId === trimmed) : projects;
|
|
30924
30893
|
const text = formatSystemSummaryTable(filtered);
|
|
@@ -30930,8 +30899,9 @@ function registerSystemTools(server, ctx, client) {
|
|
|
30930
30899
|
};
|
|
30931
30900
|
server.tool(
|
|
30932
30901
|
"list_systems",
|
|
30933
|
-
"List all systems in
|
|
30902
|
+
"List all systems in an organization",
|
|
30934
30903
|
{
|
|
30904
|
+
orgId: external_exports.string().describe("The organization ID \u2014 see list_organizations"),
|
|
30935
30905
|
teamspaceId: external_exports.string().optional().describe("Filter to systems belonging to a specific teamspace. Empty or whitespace-only treated as no filter.")
|
|
30936
30906
|
},
|
|
30937
30907
|
listSystemsHandler
|
|
@@ -30947,10 +30917,10 @@ function registerSystemTools(server, ctx, client) {
|
|
|
30947
30917
|
noteLimit
|
|
30948
30918
|
}) => {
|
|
30949
30919
|
try {
|
|
30950
|
-
const [project
|
|
30951
|
-
|
|
30952
|
-
client.getOrg(ctx.orgId)
|
|
30920
|
+
const [project] = await Promise.all([
|
|
30921
|
+
fetchSystem(client, systemId)
|
|
30953
30922
|
]);
|
|
30923
|
+
const org = await client.getOrg(project.orgId);
|
|
30954
30924
|
const notes = await client.listSystemNotes(systemId, {
|
|
30955
30925
|
limit: noteLimit ?? DEFAULT_CONTEXT_NOTE_LIMIT
|
|
30956
30926
|
});
|
|
@@ -30992,7 +30962,7 @@ function registerSystemTools(server, ctx, client) {
|
|
|
30992
30962
|
if (Object.keys(nonEmpty).length === 0) {
|
|
30993
30963
|
return { content: [{ type: "text", text: "No updates provided." }], isError: true };
|
|
30994
30964
|
}
|
|
30995
|
-
const currentSystem = await
|
|
30965
|
+
const currentSystem = await fetchSystem(client, systemId);
|
|
30996
30966
|
if (nonEmpty["accountId"] === "") delete nonEmpty["accountId"];
|
|
30997
30967
|
if (nonEmpty["agentDeliveryPolicy"] && typeof nonEmpty["agentDeliveryPolicy"] === "object") {
|
|
30998
30968
|
nonEmpty["agentDeliveryPolicy"] = mergeAgentDeliveryPolicy(
|
|
@@ -31005,8 +30975,11 @@ function registerSystemTools(server, ctx, client) {
|
|
|
31005
30975
|
if (!teamspace) {
|
|
31006
30976
|
return { content: [{ type: "text", text: "Teamspace not found" }], isError: true };
|
|
31007
30977
|
}
|
|
31008
|
-
if (teamspace.orgId !==
|
|
31009
|
-
return {
|
|
30978
|
+
if (teamspace.orgId !== currentSystem.orgId) {
|
|
30979
|
+
return {
|
|
30980
|
+
content: [{ type: "text", text: "Teamspace belongs to a different organization than the System" }],
|
|
30981
|
+
isError: true
|
|
30982
|
+
};
|
|
31010
30983
|
}
|
|
31011
30984
|
}
|
|
31012
30985
|
const updated = await client.updateSystem(systemId, nonEmpty);
|
|
@@ -31044,9 +31017,6 @@ function registerSystemTools(server, ctx, client) {
|
|
|
31044
31017
|
if (!system) {
|
|
31045
31018
|
return { content: [{ type: "text", text: `System not found: "${systemId}"` }], isError: true };
|
|
31046
31019
|
}
|
|
31047
|
-
if (system.orgId !== ctx.orgId) {
|
|
31048
|
-
return { content: [{ type: "text", text: `System "${systemId}" is not in this organization` }], isError: true };
|
|
31049
|
-
}
|
|
31050
31020
|
try {
|
|
31051
31021
|
const updated = await client.archiveSystem(systemId);
|
|
31052
31022
|
if (!updated) {
|
|
@@ -31071,6 +31041,7 @@ function registerSystemTools(server, ctx, client) {
|
|
|
31071
31041
|
}
|
|
31072
31042
|
);
|
|
31073
31043
|
const createSystemSchema = {
|
|
31044
|
+
orgId: external_exports.string().describe("The organization ID \u2014 see list_organizations"),
|
|
31074
31045
|
title: external_exports.string().describe("System title"),
|
|
31075
31046
|
description: external_exports.string().optional().describe("System description"),
|
|
31076
31047
|
strategy: external_exports.string().optional().describe('System strategy markdown \u2014 Org Strategy + System Strategy ("System Coda")'),
|
|
@@ -31080,21 +31051,24 @@ function registerSystemTools(server, ctx, client) {
|
|
|
31080
31051
|
repoName: external_exports.string().optional().describe("GitHub repository name"),
|
|
31081
31052
|
repoDefaultBranch: external_exports.string().optional().describe('Default branch for code check (defaults to "main")')
|
|
31082
31053
|
};
|
|
31083
|
-
const createSystemHandler = async ({ title, description, strategy, teamspaceId, accountId, repoOwner, repoName, repoDefaultBranch }) => {
|
|
31054
|
+
const createSystemHandler = async ({ orgId, title, description, strategy, teamspaceId, accountId, repoOwner, repoName, repoDefaultBranch }) => {
|
|
31084
31055
|
if (teamspaceId) {
|
|
31085
31056
|
const teamspace = await client.getTeamspace(teamspaceId);
|
|
31086
31057
|
if (!teamspace) {
|
|
31087
31058
|
return { content: [{ type: "text", text: "Teamspace not found" }], isError: true };
|
|
31088
31059
|
}
|
|
31089
|
-
if (teamspace.orgId !==
|
|
31090
|
-
return {
|
|
31060
|
+
if (teamspace.orgId !== orgId) {
|
|
31061
|
+
return {
|
|
31062
|
+
content: [{ type: "text", text: "Teamspace belongs to a different organization" }],
|
|
31063
|
+
isError: true
|
|
31064
|
+
};
|
|
31091
31065
|
}
|
|
31092
31066
|
}
|
|
31093
31067
|
const account = normalizeCreateAccountId(accountId);
|
|
31094
31068
|
try {
|
|
31095
31069
|
const project = await client.createSystem({
|
|
31096
31070
|
systemId: (0, import_crypto5.randomUUID)(),
|
|
31097
|
-
orgId
|
|
31071
|
+
orgId,
|
|
31098
31072
|
ownerUserId: ctx.user.userId,
|
|
31099
31073
|
title,
|
|
31100
31074
|
description,
|
|
@@ -31123,7 +31097,7 @@ function registerSystemTools(server, ctx, client) {
|
|
|
31123
31097
|
return { content: [{ type: "text", text: `Failed to create system: ${message}` }], isError: true };
|
|
31124
31098
|
}
|
|
31125
31099
|
};
|
|
31126
|
-
server.tool("create_system", "Create a new system in
|
|
31100
|
+
server.tool("create_system", "Create a new system in an organization. accountId is required \u2014 it is the Account that owns the System, and the only parent access follows.", createSystemSchema, createSystemHandler);
|
|
31127
31101
|
}
|
|
31128
31102
|
|
|
31129
31103
|
// ../../libs/harmonica-services/src/mcp/tools/teamspace-tools.ts
|
|
@@ -31144,10 +31118,12 @@ function formatEnsembleMember(member) {
|
|
|
31144
31118
|
function registerTeamspaceTools(server, ctx, client) {
|
|
31145
31119
|
server.tool(
|
|
31146
31120
|
"list_teamspaces",
|
|
31147
|
-
"List all Teamspaces in
|
|
31148
|
-
{
|
|
31149
|
-
|
|
31150
|
-
|
|
31121
|
+
"List all Teamspaces in an organization.",
|
|
31122
|
+
{
|
|
31123
|
+
orgId: external_exports.string().describe("The organization ID \u2014 see list_organizations")
|
|
31124
|
+
},
|
|
31125
|
+
async ({ orgId }) => {
|
|
31126
|
+
const teamspaces = await client.listOrgTeamspaces(orgId);
|
|
31151
31127
|
const text = teamspaces.length === 0 ? "No teamspaces found." : teamspaces.map(
|
|
31152
31128
|
(ts) => `[${ts.teamspaceId}] ${ts.name} \u2014 ${ts.status}`
|
|
31153
31129
|
).join("\n");
|
|
@@ -31158,15 +31134,16 @@ function registerTeamspaceTools(server, ctx, client) {
|
|
|
31158
31134
|
"get_teamspace",
|
|
31159
31135
|
"Get a Teamspace by ID.",
|
|
31160
31136
|
{
|
|
31161
|
-
teamspaceId: external_exports.string().describe("The teamspace ID")
|
|
31137
|
+
teamspaceId: external_exports.string().describe("The teamspace ID"),
|
|
31138
|
+
orgId: external_exports.string().describe("The organization ID \u2014 see list_organizations")
|
|
31162
31139
|
},
|
|
31163
|
-
async ({ teamspaceId }) => {
|
|
31140
|
+
async ({ teamspaceId, orgId }) => {
|
|
31164
31141
|
try {
|
|
31165
31142
|
const ts = await client.getTeamspace(teamspaceId);
|
|
31166
|
-
if (!ts
|
|
31143
|
+
if (!ts) {
|
|
31167
31144
|
return { content: [{ type: "text", text: `Teamspace not found: ${teamspaceId}` }], isError: true };
|
|
31168
31145
|
}
|
|
31169
|
-
const allProjects = await client.listOrgSystems(
|
|
31146
|
+
const allProjects = await client.listOrgSystems(orgId);
|
|
31170
31147
|
const tsProjects = allProjects.filter((p) => p.teamspaceId === teamspaceId);
|
|
31171
31148
|
const lines = [
|
|
31172
31149
|
`ID: ${ts.teamspaceId}`,
|
|
@@ -31198,16 +31175,17 @@ function registerTeamspaceTools(server, ctx, client) {
|
|
|
31198
31175
|
);
|
|
31199
31176
|
server.tool(
|
|
31200
31177
|
"create_teamspace",
|
|
31201
|
-
"Create a new Teamspace in
|
|
31178
|
+
"Create a new Teamspace in an organization.",
|
|
31202
31179
|
{
|
|
31180
|
+
orgId: external_exports.string().describe("The organization ID \u2014 see list_organizations"),
|
|
31203
31181
|
name: external_exports.string().min(1).max(200).describe("Teamspace name")
|
|
31204
31182
|
},
|
|
31205
|
-
async ({ name }) => {
|
|
31183
|
+
async ({ orgId, name }) => {
|
|
31206
31184
|
const teamspaceId = `ts-${(0, import_node_crypto2.randomUUID)()}`;
|
|
31207
31185
|
const slug = generateTeamspaceSlug(name);
|
|
31208
31186
|
const ts = await client.createTeamspace({
|
|
31209
31187
|
teamspaceId,
|
|
31210
|
-
orgId
|
|
31188
|
+
orgId,
|
|
31211
31189
|
name,
|
|
31212
31190
|
slug,
|
|
31213
31191
|
createdBy: ctx.user.userId
|
|
@@ -31274,7 +31252,7 @@ function registerValueVelocityTools(server, ctx, client) {
|
|
|
31274
31252
|
if (!bv) {
|
|
31275
31253
|
return { content: [{ type: "text", text: `Beat Version not found: "${beatVersionId}"` }], isError: true };
|
|
31276
31254
|
}
|
|
31277
|
-
await
|
|
31255
|
+
await assertBeatExists(client, bv.beatId);
|
|
31278
31256
|
const actorId = ctx.user.email ?? ctx.user.userId;
|
|
31279
31257
|
const updated = await client.confirmValueVelocityInputs(beatVersionId, {
|
|
31280
31258
|
nextDeltaV,
|
|
@@ -31339,7 +31317,7 @@ function registerValueVelocityTools(server, ctx, client) {
|
|
|
31339
31317
|
},
|
|
31340
31318
|
async ({ systemId, lens, withLeverage }) => {
|
|
31341
31319
|
try {
|
|
31342
|
-
await
|
|
31320
|
+
await assertSystemExists(client, systemId);
|
|
31343
31321
|
const [rankedResult, staleWResult] = await Promise.allSettled([
|
|
31344
31322
|
withLeverage ? client.rankSystemBeatVersionsWithLeverage(systemId, lens) : client.rankSystemBeatVersions(systemId, lens),
|
|
31345
31323
|
client.getStaleWBeatIds(systemId)
|
|
@@ -31381,7 +31359,7 @@ function registerValueVelocityTools(server, ctx, client) {
|
|
|
31381
31359
|
},
|
|
31382
31360
|
async ({ beatId }) => {
|
|
31383
31361
|
try {
|
|
31384
|
-
await
|
|
31362
|
+
await assertBeatExists(client, beatId);
|
|
31385
31363
|
const actorId = resolveActorId(ctx);
|
|
31386
31364
|
if (!actorId) {
|
|
31387
31365
|
return { content: [{ type: "text", text: "Actor identity unavailable: user has no email or userId." }], isError: true };
|
|
@@ -31417,7 +31395,7 @@ function registerValueVelocityTools(server, ctx, client) {
|
|
|
31417
31395
|
},
|
|
31418
31396
|
async ({ beatId, w }) => {
|
|
31419
31397
|
try {
|
|
31420
|
-
await
|
|
31398
|
+
await assertBeatExists(client, beatId);
|
|
31421
31399
|
const actorId = resolveActorId(ctx);
|
|
31422
31400
|
if (!actorId) {
|
|
31423
31401
|
return { content: [{ type: "text", text: "Actor identity unavailable: user has no email or userId." }], isError: true };
|
|
@@ -31446,12 +31424,12 @@ function registerValueVelocityTools(server, ctx, client) {
|
|
|
31446
31424
|
},
|
|
31447
31425
|
async ({ systemId, beatId }) => {
|
|
31448
31426
|
try {
|
|
31449
|
-
await
|
|
31427
|
+
await assertSystemExists(client, systemId);
|
|
31450
31428
|
const actorId = resolveActorId(ctx);
|
|
31451
31429
|
if (!actorId) {
|
|
31452
31430
|
return { content: [{ type: "text", text: "Actor identity unavailable: user has no email or userId." }], isError: true };
|
|
31453
31431
|
}
|
|
31454
|
-
if (beatId !== void 0) await
|
|
31432
|
+
if (beatId !== void 0) await assertBeatExists(client, beatId);
|
|
31455
31433
|
const proposed = await client.proposeEnablementEdges(systemId, actorId, beatId);
|
|
31456
31434
|
if (proposed.length === 0) {
|
|
31457
31435
|
return { content: [{ type: "text", text: "No enablement edges proposed (fewer than 2 Beats or no relationships found)." }] };
|
|
@@ -31482,8 +31460,8 @@ function registerValueVelocityTools(server, ctx, client) {
|
|
|
31482
31460
|
},
|
|
31483
31461
|
async ({ beatId, targetBeatId }) => {
|
|
31484
31462
|
try {
|
|
31485
|
-
await
|
|
31486
|
-
await
|
|
31463
|
+
await assertBeatExists(client, beatId);
|
|
31464
|
+
await assertBeatExists(client, targetBeatId);
|
|
31487
31465
|
const actorId = resolveActorId(ctx);
|
|
31488
31466
|
if (!actorId) {
|
|
31489
31467
|
return { content: [{ type: "text", text: "Actor identity unavailable: user has no email or userId." }], isError: true };
|
|
@@ -31516,8 +31494,8 @@ function registerValueVelocityTools(server, ctx, client) {
|
|
|
31516
31494
|
},
|
|
31517
31495
|
async ({ beatId, targetBeatId, rationale }) => {
|
|
31518
31496
|
try {
|
|
31519
|
-
await
|
|
31520
|
-
await
|
|
31497
|
+
await assertBeatExists(client, beatId);
|
|
31498
|
+
await assertBeatExists(client, targetBeatId);
|
|
31521
31499
|
const actorId = resolveActorId(ctx);
|
|
31522
31500
|
if (!actorId) {
|
|
31523
31501
|
return { content: [{ type: "text", text: "Actor identity unavailable: user has no email or userId." }], isError: true };
|
|
@@ -31553,8 +31531,8 @@ function registerValueVelocityTools(server, ctx, client) {
|
|
|
31553
31531
|
},
|
|
31554
31532
|
async ({ beatId, targetBeatId }) => {
|
|
31555
31533
|
try {
|
|
31556
|
-
await
|
|
31557
|
-
await
|
|
31534
|
+
await assertBeatExists(client, beatId);
|
|
31535
|
+
await assertBeatExists(client, targetBeatId);
|
|
31558
31536
|
const actorId = resolveActorId(ctx);
|
|
31559
31537
|
if (!actorId) {
|
|
31560
31538
|
return { content: [{ type: "text", text: "Actor identity unavailable: user has no email or userId." }], isError: true };
|
|
@@ -32172,7 +32150,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
32172
32150
|
limit: external_exports.coerce.number().optional().default(20).describe("Max number of jobs to return")
|
|
32173
32151
|
},
|
|
32174
32152
|
async ({ systemId, status, limit }) => {
|
|
32175
|
-
await
|
|
32153
|
+
await assertSystemExists(client, systemId);
|
|
32176
32154
|
const tasks = await client.listSystemTasks(systemId, {
|
|
32177
32155
|
status,
|
|
32178
32156
|
limit
|
|
@@ -32230,7 +32208,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
32230
32208
|
limit: external_exports.coerce.number().optional().default(20).describe("Max number of jobs to return")
|
|
32231
32209
|
},
|
|
32232
32210
|
async ({ systemId, status, limit }) => {
|
|
32233
|
-
await
|
|
32211
|
+
await assertSystemExists(client, systemId);
|
|
32234
32212
|
const tasks = await client.listSystemTasks(systemId, {
|
|
32235
32213
|
status,
|
|
32236
32214
|
limit
|
|
@@ -32254,7 +32232,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
32254
32232
|
},
|
|
32255
32233
|
async ({ systemId, beatId }) => {
|
|
32256
32234
|
try {
|
|
32257
|
-
await
|
|
32235
|
+
await assertSystemExists(client, systemId);
|
|
32258
32236
|
const task = await client.draftCoda(systemId, beatId);
|
|
32259
32237
|
return {
|
|
32260
32238
|
content: [{
|
|
@@ -32287,7 +32265,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
32287
32265
|
},
|
|
32288
32266
|
async ({ systemId, noteIds }) => {
|
|
32289
32267
|
try {
|
|
32290
|
-
await
|
|
32268
|
+
await assertSystemExists(client, systemId);
|
|
32291
32269
|
const task = await client.validateAssumptions(systemId, noteIds);
|
|
32292
32270
|
return {
|
|
32293
32271
|
content: [{
|
|
@@ -32323,7 +32301,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
32323
32301
|
},
|
|
32324
32302
|
async ({ systemId, message, revisionId, parentSessionId }) => {
|
|
32325
32303
|
try {
|
|
32326
|
-
await
|
|
32304
|
+
await assertSystemExists(client, systemId);
|
|
32327
32305
|
const task = await client.startSession(
|
|
32328
32306
|
systemId,
|
|
32329
32307
|
message,
|
|
@@ -32363,7 +32341,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
32363
32341
|
},
|
|
32364
32342
|
async ({ systemId, revisionIds }) => {
|
|
32365
32343
|
try {
|
|
32366
|
-
await
|
|
32344
|
+
await assertSystemExists(client, systemId);
|
|
32367
32345
|
const task = await client.planRevisionBatch(systemId, revisionIds);
|
|
32368
32346
|
return {
|
|
32369
32347
|
content: [{
|
|
@@ -32395,7 +32373,7 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
32395
32373
|
},
|
|
32396
32374
|
async ({ systemId }) => {
|
|
32397
32375
|
try {
|
|
32398
|
-
await
|
|
32376
|
+
await assertSystemExists(client, systemId);
|
|
32399
32377
|
const task = await client.runTriage(systemId);
|
|
32400
32378
|
return {
|
|
32401
32379
|
content: [{
|
|
@@ -32440,8 +32418,30 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
32440
32418
|
encoding: external_exports.enum(["base64"]).optional().describe('Set to "base64" when submitting binary files (DOCX, XLSX, PPTX, PDF). Omit for plain text.')
|
|
32441
32419
|
},
|
|
32442
32420
|
async ({ systemId, content, filename, encoding }) => {
|
|
32421
|
+
const DOCUMENT_MAX_BYTES = 10 * 1024 * 1024;
|
|
32422
|
+
const contentBytes = encoding === "base64" ? Buffer.byteLength(content, "base64") : Buffer.byteLength(content, "utf8");
|
|
32423
|
+
if (contentBytes > DOCUMENT_MAX_BYTES) {
|
|
32424
|
+
const actualMb = (contentBytes / (1024 * 1024)).toFixed(2);
|
|
32425
|
+
return {
|
|
32426
|
+
content: [{
|
|
32427
|
+
type: "text",
|
|
32428
|
+
text: [
|
|
32429
|
+
`**Field:** content`,
|
|
32430
|
+
`**Limit:** 10MB`,
|
|
32431
|
+
`**Actual:** ${actualMb}MB`,
|
|
32432
|
+
`**Action:** Split the document into smaller chunks (each under 10MB) and submit them separately.`
|
|
32433
|
+
].join("\n")
|
|
32434
|
+
}],
|
|
32435
|
+
isError: true
|
|
32436
|
+
};
|
|
32437
|
+
}
|
|
32438
|
+
try {
|
|
32439
|
+
await assertSystemExists(client, systemId);
|
|
32440
|
+
} catch (err) {
|
|
32441
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
32442
|
+
return { content: [{ type: "text", text: `Failed to analyze document: ${message}` }], isError: true };
|
|
32443
|
+
}
|
|
32443
32444
|
try {
|
|
32444
|
-
await assertSystemInOrg(client, systemId, ctx.orgId);
|
|
32445
32445
|
const result = encoding === "base64" ? await client.analyzeBinaryDocument(systemId, content, filename) : await client.analyzeDocument(systemId, content, filename);
|
|
32446
32446
|
return {
|
|
32447
32447
|
content: [{
|
|
@@ -32460,6 +32460,24 @@ ${JSON.stringify(task.result, null, 2)}
|
|
|
32460
32460
|
};
|
|
32461
32461
|
} catch (err) {
|
|
32462
32462
|
const message = err instanceof Error ? err.message : String(err);
|
|
32463
|
+
const status = err?.status;
|
|
32464
|
+
const lower = message.toLowerCase();
|
|
32465
|
+
const isSizeError = status === 413 || lower.includes("size limit") || lower.includes("too large");
|
|
32466
|
+
if (isSizeError) {
|
|
32467
|
+
return {
|
|
32468
|
+
content: [{
|
|
32469
|
+
type: "text",
|
|
32470
|
+
text: [
|
|
32471
|
+
`**Field:** content`,
|
|
32472
|
+
`**Limit:** 10MB`,
|
|
32473
|
+
`**Actual:** ${(contentBytes / (1024 * 1024)).toFixed(2)}MB`,
|
|
32474
|
+
`**Detail:** ${message}`,
|
|
32475
|
+
`**Action:** Split the document into smaller chunks (each under 10MB) and submit them separately.`
|
|
32476
|
+
].join("\n")
|
|
32477
|
+
}],
|
|
32478
|
+
isError: true
|
|
32479
|
+
};
|
|
32480
|
+
}
|
|
32463
32481
|
return { content: [{ type: "text", text: `Failed to analyze document: ${message}` }], isError: true };
|
|
32464
32482
|
}
|
|
32465
32483
|
}
|
|
@@ -32613,7 +32631,7 @@ function registerBeatResources(server, ctx, client) {
|
|
|
32613
32631
|
new ResourceTemplate("harmonica://system/{systemId}/beats", { list: void 0 }),
|
|
32614
32632
|
async (uri, { systemId }) => {
|
|
32615
32633
|
const sid = String(systemId);
|
|
32616
|
-
await
|
|
32634
|
+
await fetchSystem(client, sid);
|
|
32617
32635
|
const beats = await client.listSystemBeats(sid);
|
|
32618
32636
|
const text = formatBeatSummaryTable(beats);
|
|
32619
32637
|
return { contents: [{ uri: uri.href, text }] };
|
|
@@ -32624,7 +32642,7 @@ function registerBeatResources(server, ctx, client) {
|
|
|
32624
32642
|
new ResourceTemplate("harmonica://beat/{beatId}", { list: void 0 }),
|
|
32625
32643
|
async (uri, { beatId }) => {
|
|
32626
32644
|
const bid = String(beatId);
|
|
32627
|
-
await
|
|
32645
|
+
await assertBeatExists(client, bid);
|
|
32628
32646
|
const beat = await client.getBeat(bid);
|
|
32629
32647
|
if (!beat) {
|
|
32630
32648
|
return { contents: [{ uri: uri.href, text: `Beat not found: "${bid}"` }] };
|
|
@@ -32655,7 +32673,7 @@ function registerSystemResources(server, ctx, client) {
|
|
|
32655
32673
|
new ResourceTemplate("harmonica://system/{systemId}/context", { list: void 0 }),
|
|
32656
32674
|
async (uri, { systemId }) => {
|
|
32657
32675
|
const sid = String(systemId);
|
|
32658
|
-
const project = await
|
|
32676
|
+
const project = await fetchSystem(client, sid);
|
|
32659
32677
|
const notes = await client.listSystemNotes(sid, { limit: DEFAULT_CONTEXT_NOTE_LIMIT });
|
|
32660
32678
|
const text = formatSystemContext(project, notes);
|
|
32661
32679
|
return { contents: [{ uri: uri.href, text }] };
|
|
@@ -32843,8 +32861,17 @@ function createHttpClient(config2) {
|
|
|
32843
32861
|
if (!raw) return void 0;
|
|
32844
32862
|
return { ...raw, orgId: raw.id };
|
|
32845
32863
|
},
|
|
32846
|
-
|
|
32847
|
-
return request(
|
|
32864
|
+
getCurrentUser: async () => {
|
|
32865
|
+
return request(
|
|
32866
|
+
"GET",
|
|
32867
|
+
"/api/users/me"
|
|
32868
|
+
);
|
|
32869
|
+
},
|
|
32870
|
+
getUser: async (userId) => {
|
|
32871
|
+
return request(
|
|
32872
|
+
"GET",
|
|
32873
|
+
`/api/users/lookup?email=${encodeURIComponent(userId)}`
|
|
32874
|
+
);
|
|
32848
32875
|
},
|
|
32849
32876
|
getMembership: async (userId, orgId) => {
|
|
32850
32877
|
return { userId, orgId, role: "member" };
|
|
@@ -32914,7 +32941,7 @@ function createHttpClient(config2) {
|
|
|
32914
32941
|
);
|
|
32915
32942
|
const system = result?.system ? {
|
|
32916
32943
|
...result.system,
|
|
32917
|
-
systemId: result.system.id ?? result.system.systemId
|
|
32944
|
+
systemId: result.system.id ?? result.system.systemId,
|
|
32918
32945
|
title: result.system.name ?? result.system.title,
|
|
32919
32946
|
orgId: result.system.organizationId ?? result.system.orgId
|
|
32920
32947
|
} : void 0;
|
|
@@ -32927,14 +32954,14 @@ function createHttpClient(config2) {
|
|
|
32927
32954
|
// Systems (canonical)
|
|
32928
32955
|
listOrgSystems: async (orgId) => {
|
|
32929
32956
|
const raw = await request("GET", `/api/organizations/${encodeURIComponent(orgId)}/systems/summaries`);
|
|
32930
|
-
return (raw ?? []).map((p) => ({ ...p, systemId: p.id,
|
|
32957
|
+
return (raw ?? []).map((p) => ({ ...p, systemId: p.id, title: p.name, orgId: p.organizationId }));
|
|
32931
32958
|
},
|
|
32932
32959
|
getSystem: async (systemId) => {
|
|
32933
32960
|
const raw = await request("GET", `/api/systems/${encodeURIComponent(systemId)}`);
|
|
32934
32961
|
if (!raw) return void 0;
|
|
32935
32962
|
return {
|
|
32936
32963
|
...raw,
|
|
32937
|
-
systemId: raw.id ?? raw.systemId
|
|
32964
|
+
systemId: raw.id ?? raw.systemId,
|
|
32938
32965
|
title: raw.name ?? raw.title,
|
|
32939
32966
|
orgId: raw.organizationId ?? raw.orgId
|
|
32940
32967
|
};
|
|
@@ -32950,26 +32977,25 @@ function createHttpClient(config2) {
|
|
|
32950
32977
|
// is created unlinked — invisible in the Account's Systems list.
|
|
32951
32978
|
...input.accountId !== void 0 && { accountId: input.accountId }
|
|
32952
32979
|
});
|
|
32953
|
-
return { ...raw, systemId: raw?.id ?? raw?.systemId
|
|
32980
|
+
return { ...raw, systemId: raw?.id ?? raw?.systemId, title: raw?.name ?? raw?.title, orgId: raw?.organizationId ?? raw?.orgId };
|
|
32954
32981
|
},
|
|
32955
32982
|
updateSystem: async (systemId, updates) => {
|
|
32956
32983
|
const raw = await request("PATCH", `/api/systems/${encodeURIComponent(systemId)}`, updates);
|
|
32957
32984
|
if (!raw) return void 0;
|
|
32958
|
-
return { ...raw, systemId: raw.id ?? raw.systemId
|
|
32985
|
+
return { ...raw, systemId: raw.id ?? raw.systemId, title: raw.name ?? raw.title, orgId: raw.organizationId ?? raw.orgId };
|
|
32959
32986
|
},
|
|
32960
32987
|
archiveSystem: async (systemId) => {
|
|
32961
32988
|
const raw = await request("PATCH", `/api/systems/${encodeURIComponent(systemId)}/archive`);
|
|
32962
32989
|
if (!raw) return void 0;
|
|
32963
|
-
return { ...raw, systemId: raw.id ?? raw.systemId
|
|
32990
|
+
return { ...raw, systemId: raw.id ?? raw.systemId, title: raw.name ?? raw.title, orgId: raw.organizationId ?? raw.orgId };
|
|
32964
32991
|
},
|
|
32965
32992
|
getSystemFact: async (systemId) => {
|
|
32966
32993
|
const result = await request("GET", `/api/systems/${encodeURIComponent(systemId)}/facts`);
|
|
32967
32994
|
return result?.data;
|
|
32968
32995
|
},
|
|
32969
32996
|
saveSystemFact: (input) => {
|
|
32970
|
-
|
|
32971
|
-
|
|
32972
|
-
return request("PUT", `/api/systems/${encodeURIComponent(String(effectiveId))}/facts`, input);
|
|
32997
|
+
if (!input.systemId) throw new Error("saveSystemFact: systemId is required");
|
|
32998
|
+
return request("PUT", `/api/systems/${encodeURIComponent(String(input.systemId))}/facts`, input);
|
|
32973
32999
|
},
|
|
32974
33000
|
transitionSystemLifecycleState: async (systemId, targetState, options) => {
|
|
32975
33001
|
return request("POST", `/api/systems/${encodeURIComponent(systemId)}/lifecycle/transitions`, {
|
|
@@ -32995,7 +33021,7 @@ function createHttpClient(config2) {
|
|
|
32995
33021
|
getBeat: async (beatId) => {
|
|
32996
33022
|
const raw = await request("GET", `/api/beats/${encodeURIComponent(beatId)}`);
|
|
32997
33023
|
if (!raw) return void 0;
|
|
32998
|
-
return { ...raw, beatId: raw.beatId ?? raw.id,
|
|
33024
|
+
return { ...raw, beatId: raw.beatId ?? raw.id, status: raw.status ?? "open", beatStatus: coerceBeatStatusLocal(raw.beatStatus) };
|
|
32999
33025
|
},
|
|
33000
33026
|
getBeatConvergence: async (systemId, beatId) => {
|
|
33001
33027
|
const raw = await request(
|
|
@@ -33042,7 +33068,7 @@ function createHttpClient(config2) {
|
|
|
33042
33068
|
}
|
|
33043
33069
|
const beat = await request("GET", `/api/beats/${encodeURIComponent(beatId)}`);
|
|
33044
33070
|
if (!beat) return void 0;
|
|
33045
|
-
const systemId = beat.systemId
|
|
33071
|
+
const systemId = beat.systemId;
|
|
33046
33072
|
if (!systemId) return void 0;
|
|
33047
33073
|
const result = await request("PUT", `/api/systems/${encodeURIComponent(systemId)}/beats/${encodeURIComponent(beatId)}`, updates);
|
|
33048
33074
|
const raw = result?.beat ?? result;
|
|
@@ -33178,7 +33204,7 @@ function createHttpClient(config2) {
|
|
|
33178
33204
|
listBeatNotes: async (beatId, filters) => {
|
|
33179
33205
|
const beat = await request("GET", `/api/beats/${encodeURIComponent(beatId)}`);
|
|
33180
33206
|
if (!beat) return [];
|
|
33181
|
-
const systemId = beat.systemId
|
|
33207
|
+
const systemId = beat.systemId;
|
|
33182
33208
|
if (!systemId) return [];
|
|
33183
33209
|
const params = new URLSearchParams();
|
|
33184
33210
|
if (filters?.noteType) {
|
|
@@ -33242,10 +33268,6 @@ function createHttpClient(config2) {
|
|
|
33242
33268
|
);
|
|
33243
33269
|
return result;
|
|
33244
33270
|
},
|
|
33245
|
-
getKeyAssumptions: async (systemId) => {
|
|
33246
|
-
const result = await request("GET", `/api/systems/${encodeURIComponent(systemId)}/notes/key-assumptions`);
|
|
33247
|
-
return result?.assumptions ?? [];
|
|
33248
|
-
},
|
|
33249
33271
|
getNextNoteId: async (projectCode) => {
|
|
33250
33272
|
return `N-${projectCode}-${Date.now()}`;
|
|
33251
33273
|
},
|
|
@@ -34221,8 +34243,7 @@ function createHttpClient(config2) {
|
|
|
34221
34243
|
...parentGroupId !== void 0 && { parentGroupId },
|
|
34222
34244
|
...createdBy !== void 0 && { createdBy }
|
|
34223
34245
|
};
|
|
34224
|
-
const
|
|
34225
|
-
const effectiveSystemId = inputRec.systemId ?? input.projectId;
|
|
34246
|
+
const effectiveSystemId = input.systemId;
|
|
34226
34247
|
const url2 = effectiveSystemId ? `/api/systems/${encodeURIComponent(effectiveSystemId)}/deliverable-groups` : `/api/teamspaces/${encodeURIComponent(input.teamspaceId)}/deliverable-groups`;
|
|
34227
34248
|
const result = await request("POST", url2, body);
|
|
34228
34249
|
if (result === void 0) throw new Error(`Resource not found when creating DeliverableGroup`);
|
|
@@ -35043,7 +35064,7 @@ function loadConfig() {
|
|
|
35043
35064
|
};
|
|
35044
35065
|
}
|
|
35045
35066
|
async function main() {
|
|
35046
|
-
console.error(`[harmonica-mcp] v${"4.
|
|
35067
|
+
console.error(`[harmonica-mcp] v${"4.4.0"} starting\u2026`);
|
|
35047
35068
|
const config2 = loadConfig();
|
|
35048
35069
|
const client = createHttpClient({
|
|
35049
35070
|
apiBaseUrl: config2.apiBaseUrl,
|