agent-planner-mcp 1.5.3 → 1.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/src/tools/bdi/_shared.js
CHANGED
|
@@ -26,6 +26,24 @@ function safeArray(value) {
|
|
|
26
26
|
return Array.isArray(value) ? value : [];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* The web app origin (where /app/plans/:id lives), for building shareable plan
|
|
31
|
+
* links agents can post (e.g. to Slack). Derived from API_URL — the web app
|
|
32
|
+
* shares the origin and the API sits under /api behind nginx — with an explicit
|
|
33
|
+
* AGENTPLANNER_WEB_URL override for local/self-hosted setups where the UI is on
|
|
34
|
+
* a different host/port.
|
|
35
|
+
*/
|
|
36
|
+
function webOrigin() {
|
|
37
|
+
if (process.env.AGENTPLANNER_WEB_URL) return process.env.AGENTPLANNER_WEB_URL.replace(/\/+$/, '');
|
|
38
|
+
const api = process.env.API_URL || 'https://agentplanner.io/api';
|
|
39
|
+
return api.replace(/\/+$/, '').replace(/\/api$/, '') || 'https://agentplanner.io';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Shareable web link to a plan (set the plan's visibility to unlisted/public for a rich unfurl). */
|
|
43
|
+
function planUrl(planId) {
|
|
44
|
+
return planId ? `${webOrigin()}/app/plans/${planId}` : null;
|
|
45
|
+
}
|
|
46
|
+
|
|
29
47
|
/**
|
|
30
48
|
* True when an error means the backend has no /v1 surface (pre-consolidation
|
|
31
49
|
* self-hosted API). Express returns a default 404 with no structured body for
|
|
@@ -38,4 +56,4 @@ function isV1Unavailable(err) {
|
|
|
38
56
|
return !(body && typeof body === 'object' && body.error);
|
|
39
57
|
}
|
|
40
58
|
|
|
41
|
-
module.exports = { asOf, formatResponse, errorResponse, safeArray, isV1Unavailable };
|
|
59
|
+
module.exports = { asOf, formatResponse, errorResponse, safeArray, isV1Unavailable, webOrigin, planUrl };
|
package/src/tools/bdi/beliefs.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* plan_analysis. Each answers one whole agentic question and returns `as_of`.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const { asOf, formatResponse, errorResponse, safeArray, isV1Unavailable } = require('./_shared');
|
|
8
|
+
const { asOf, formatResponse, errorResponse, safeArray, isV1Unavailable, planUrl } = require('./_shared');
|
|
9
9
|
|
|
10
10
|
// ─────────────────────────────────────────────────────────────────────────
|
|
11
11
|
// briefing — bundled mission control state. Replaces 4 round trips.
|
|
@@ -486,6 +486,7 @@ async function listPlansHandler(args, apiClient) {
|
|
|
486
486
|
summary,
|
|
487
487
|
plans: page.map((p) => ({
|
|
488
488
|
id: p.id,
|
|
489
|
+
url: planUrl(p.id),
|
|
489
490
|
title: p.title,
|
|
490
491
|
status: p.status,
|
|
491
492
|
visibility: p.visibility,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* See ../../../docs/MCP_v1.0_FULL_SURFACE.md for design rationale.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
const { asOf, formatResponse, errorResponse, isV1Unavailable } = require('./_shared');
|
|
16
|
+
const { asOf, formatResponse, errorResponse, isV1Unavailable, planUrl } = require('./_shared');
|
|
17
17
|
const { version: PKG_VERSION } = require('../../../package.json');
|
|
18
18
|
|
|
19
19
|
// Provenance tag stamped onto every plan this server creates, so a plan stays
|
|
@@ -854,16 +854,18 @@ async function formIntentionHandler(args, apiClient) {
|
|
|
854
854
|
tree,
|
|
855
855
|
client_version: CLIENT_TAG,
|
|
856
856
|
});
|
|
857
|
+
const facadePlanId = result.plan?.id || result.plan_id;
|
|
857
858
|
return formatResponse({
|
|
858
859
|
...result,
|
|
859
|
-
plan_id:
|
|
860
|
+
plan_id: facadePlanId,
|
|
861
|
+
url: planUrl(facadePlanId),
|
|
860
862
|
goal_id,
|
|
861
863
|
status: result.plan?.status || status,
|
|
862
864
|
is_draft: (result.plan?.status || status) === 'draft',
|
|
863
865
|
nodes_created: Array.isArray(result.tree) ? result.tree.length : undefined,
|
|
864
866
|
next_step: (result.plan?.status || status) === 'draft'
|
|
865
867
|
? "Plan created as draft. Will surface in dashboard pending for human review. Auto-promotes to active when first task moves to in_progress."
|
|
866
|
-
:
|
|
868
|
+
: `Plan active. Claim a task with claim_next_task({plan_id}) to begin work. Shareable link: ${planUrl(facadePlanId)} (set visibility:'unlisted' for a rich Slack/social preview).`,
|
|
867
869
|
});
|
|
868
870
|
} catch {
|
|
869
871
|
// Fall through to the legacy multi-call path for older/self-hosted APIs.
|
|
@@ -938,7 +940,10 @@ async function formIntentionHandler(args, apiClient) {
|
|
|
938
940
|
continue;
|
|
939
941
|
}
|
|
940
942
|
try {
|
|
941
|
-
|
|
943
|
+
// Plan-scoped route — the bare /dependencies path is unmounted (only
|
|
944
|
+
// /dependencies/cross-plan + /external exist), so it 404s and silently
|
|
945
|
+
// dropped every inline edge. POST /plans/:id/dependencies is canonical.
|
|
946
|
+
await apiClient.axiosInstance.post(`/plans/${plan.id}/dependencies`, {
|
|
942
947
|
source_node_id: sourceId,
|
|
943
948
|
target_node_id: intent.targetId,
|
|
944
949
|
dependency_type: 'blocks',
|
|
@@ -963,6 +968,7 @@ async function formIntentionHandler(args, apiClient) {
|
|
|
963
968
|
const response = {
|
|
964
969
|
as_of: asOf(),
|
|
965
970
|
plan_id: plan.id,
|
|
971
|
+
url: planUrl(plan.id),
|
|
966
972
|
goal_id,
|
|
967
973
|
status: plan.status,
|
|
968
974
|
is_draft: plan.status === 'draft',
|
|
@@ -972,7 +978,7 @@ async function formIntentionHandler(args, apiClient) {
|
|
|
972
978
|
structure,
|
|
973
979
|
next_step: plan.status === 'draft'
|
|
974
980
|
? "Plan created as draft. Will surface in dashboard pending for human review. Auto-promotes to active when first task moves to in_progress."
|
|
975
|
-
:
|
|
981
|
+
: `Plan active. Claim a task with claim_next_task({plan_id}) to begin work. Shareable link: ${planUrl(plan.id)} (set visibility:'unlisted' for a rich Slack/social preview).`,
|
|
976
982
|
};
|
|
977
983
|
if (createdWithoutDependencies) {
|
|
978
984
|
response.warning =
|
|
@@ -1133,7 +1139,9 @@ async function proposeResearchChainHandler(args, apiClient) {
|
|
|
1133
1139
|
[created.plan.id, created.implement.id],
|
|
1134
1140
|
]) {
|
|
1135
1141
|
try {
|
|
1136
|
-
|
|
1142
|
+
// Plan-scoped route — bare /dependencies 404s (unmounted), which silently
|
|
1143
|
+
// dropped both chain edges and left 3 orphan tasks.
|
|
1144
|
+
await apiClient.axiosInstance.post(`/plans/${plan_id}/dependencies`, {
|
|
1137
1145
|
source_node_id: from,
|
|
1138
1146
|
target_node_id: to,
|
|
1139
1147
|
dependency_type: 'blocks',
|
|
@@ -1144,9 +1152,11 @@ async function proposeResearchChainHandler(args, apiClient) {
|
|
|
1144
1152
|
}
|
|
1145
1153
|
}
|
|
1146
1154
|
|
|
1155
|
+
const edgeFailures = failures.filter((f) => f.step === 'create_edge');
|
|
1147
1156
|
return formatResponse({
|
|
1148
1157
|
as_of: asOf(),
|
|
1149
1158
|
plan_id,
|
|
1159
|
+
url: planUrl(plan_id),
|
|
1150
1160
|
parent_id,
|
|
1151
1161
|
rationale,
|
|
1152
1162
|
research: { id: created.research.id, title: created.research.title },
|
|
@@ -1154,6 +1164,11 @@ async function proposeResearchChainHandler(args, apiClient) {
|
|
|
1154
1164
|
implement: { id: created.implement.id, title: created.implement.title },
|
|
1155
1165
|
edges,
|
|
1156
1166
|
failures,
|
|
1167
|
+
// Fail loudly: don't let the caller believe the chain is wired when the
|
|
1168
|
+
// blocking edges didn't get created.
|
|
1169
|
+
...(edgeFailures.length
|
|
1170
|
+
? { warning: `Chain tasks created but ${edgeFailures.length} blocking edge(s) FAILED — the Research→Plan→Implement ordering is NOT wired: ${edgeFailures.map((f) => f.error).join('; ')}` }
|
|
1171
|
+
: {}),
|
|
1157
1172
|
next_step: "Claim the Research task with claim_next_task({plan_id}) to begin investigation.",
|
|
1158
1173
|
});
|
|
1159
1174
|
}
|