@costgate/momentum-mcp 0.2.1 → 0.2.3
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/README.md +2 -2
- package/momentum-mcp.mjs +403 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ MOMENTUM_TOKEN=mom_agent_xxx \
|
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
整条命令不用自己拼 —— 在 Momentum 的「设置 › Agent」里签发令牌时会直接给出,
|
|
12
|
-
默认用 `@latest`,发新版不用改配置;想固定某一版时把 latest 换成版本号(当前是 0.2.
|
|
12
|
+
默认用 `@latest`,发新版不用改配置;想固定某一版时把 latest 换成版本号(当前是 0.2.3)。
|
|
13
13
|
|
|
14
14
|
| 环境变量 | 必填 | 说明 |
|
|
15
15
|
|---|---|---|
|
|
@@ -18,4 +18,4 @@ MOMENTUM_TOKEN=mom_agent_xxx \
|
|
|
18
18
|
| `MOMENTUM_CF_ACCESS_CLIENT_ID` | 否 | 部署在 Cloudflare Access 后面时才需要 |
|
|
19
19
|
| `MOMENTUM_CF_ACCESS_CLIENT_SECRET` | 否 | 同上 |
|
|
20
20
|
|
|
21
|
-
版本 0.2.
|
|
21
|
+
版本 0.2.3。客户端和服务端版本不一致时,启动日志(stderr)会明确警告。
|
package/momentum-mcp.mjs
CHANGED
|
@@ -15339,7 +15339,7 @@ var allowErrorProps = SuperJSON.allowErrorProps;
|
|
|
15339
15339
|
|
|
15340
15340
|
// src/version.ts
|
|
15341
15341
|
var UNKNOWN = "unknown";
|
|
15342
|
-
var MCP_VERSION = true ? "0.2.
|
|
15342
|
+
var MCP_VERSION = true ? "0.2.3" : versionFromRepo();
|
|
15343
15343
|
var CLIENT_HEADER = `momentum-mcp/${MCP_VERSION}`;
|
|
15344
15344
|
function compareVersions(a, b) {
|
|
15345
15345
|
const parse4 = (v) => {
|
|
@@ -22646,6 +22646,228 @@ var McpZodTypeKind;
|
|
|
22646
22646
|
McpZodTypeKind2["Completable"] = "McpCompletable";
|
|
22647
22647
|
})(McpZodTypeKind || (McpZodTypeKind = {}));
|
|
22648
22648
|
|
|
22649
|
+
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/uriTemplate.js
|
|
22650
|
+
var MAX_TEMPLATE_LENGTH = 1e6;
|
|
22651
|
+
var MAX_VARIABLE_LENGTH = 1e6;
|
|
22652
|
+
var MAX_TEMPLATE_EXPRESSIONS = 1e4;
|
|
22653
|
+
var MAX_REGEX_LENGTH = 1e6;
|
|
22654
|
+
var UriTemplate = class _UriTemplate {
|
|
22655
|
+
/**
|
|
22656
|
+
* Returns true if the given string contains any URI template expressions.
|
|
22657
|
+
* A template expression is a sequence of characters enclosed in curly braces,
|
|
22658
|
+
* like {foo} or {?bar}.
|
|
22659
|
+
*/
|
|
22660
|
+
static isTemplate(str) {
|
|
22661
|
+
return /\{[^}\s]+\}/.test(str);
|
|
22662
|
+
}
|
|
22663
|
+
static validateLength(str, max, context) {
|
|
22664
|
+
if (str.length > max) {
|
|
22665
|
+
throw new Error(`${context} exceeds maximum length of ${max} characters (got ${str.length})`);
|
|
22666
|
+
}
|
|
22667
|
+
}
|
|
22668
|
+
get variableNames() {
|
|
22669
|
+
return this.parts.flatMap((part) => typeof part === "string" ? [] : part.names);
|
|
22670
|
+
}
|
|
22671
|
+
constructor(template) {
|
|
22672
|
+
_UriTemplate.validateLength(template, MAX_TEMPLATE_LENGTH, "Template");
|
|
22673
|
+
this.template = template;
|
|
22674
|
+
this.parts = this.parse(template);
|
|
22675
|
+
}
|
|
22676
|
+
toString() {
|
|
22677
|
+
return this.template;
|
|
22678
|
+
}
|
|
22679
|
+
parse(template) {
|
|
22680
|
+
const parts = [];
|
|
22681
|
+
let currentText = "";
|
|
22682
|
+
let i = 0;
|
|
22683
|
+
let expressionCount = 0;
|
|
22684
|
+
while (i < template.length) {
|
|
22685
|
+
if (template[i] === "{") {
|
|
22686
|
+
if (currentText) {
|
|
22687
|
+
parts.push(currentText);
|
|
22688
|
+
currentText = "";
|
|
22689
|
+
}
|
|
22690
|
+
const end = template.indexOf("}", i);
|
|
22691
|
+
if (end === -1)
|
|
22692
|
+
throw new Error("Unclosed template expression");
|
|
22693
|
+
expressionCount++;
|
|
22694
|
+
if (expressionCount > MAX_TEMPLATE_EXPRESSIONS) {
|
|
22695
|
+
throw new Error(`Template contains too many expressions (max ${MAX_TEMPLATE_EXPRESSIONS})`);
|
|
22696
|
+
}
|
|
22697
|
+
const expr = template.slice(i + 1, end);
|
|
22698
|
+
const operator = this.getOperator(expr);
|
|
22699
|
+
const exploded = expr.includes("*");
|
|
22700
|
+
const names = this.getNames(expr);
|
|
22701
|
+
const name = names[0];
|
|
22702
|
+
for (const name2 of names) {
|
|
22703
|
+
_UriTemplate.validateLength(name2, MAX_VARIABLE_LENGTH, "Variable name");
|
|
22704
|
+
}
|
|
22705
|
+
parts.push({ name, operator, names, exploded });
|
|
22706
|
+
i = end + 1;
|
|
22707
|
+
} else {
|
|
22708
|
+
currentText += template[i];
|
|
22709
|
+
i++;
|
|
22710
|
+
}
|
|
22711
|
+
}
|
|
22712
|
+
if (currentText) {
|
|
22713
|
+
parts.push(currentText);
|
|
22714
|
+
}
|
|
22715
|
+
return parts;
|
|
22716
|
+
}
|
|
22717
|
+
getOperator(expr) {
|
|
22718
|
+
const operators = ["+", "#", ".", "/", "?", "&"];
|
|
22719
|
+
return operators.find((op) => expr.startsWith(op)) || "";
|
|
22720
|
+
}
|
|
22721
|
+
getNames(expr) {
|
|
22722
|
+
const operator = this.getOperator(expr);
|
|
22723
|
+
return expr.slice(operator.length).split(",").map((name) => name.replace("*", "").trim()).filter((name) => name.length > 0);
|
|
22724
|
+
}
|
|
22725
|
+
encodeValue(value, operator) {
|
|
22726
|
+
_UriTemplate.validateLength(value, MAX_VARIABLE_LENGTH, "Variable value");
|
|
22727
|
+
if (operator === "+" || operator === "#") {
|
|
22728
|
+
return encodeURI(value);
|
|
22729
|
+
}
|
|
22730
|
+
return encodeURIComponent(value);
|
|
22731
|
+
}
|
|
22732
|
+
expandPart(part, variables) {
|
|
22733
|
+
if (part.operator === "?" || part.operator === "&") {
|
|
22734
|
+
const pairs = part.names.map((name) => {
|
|
22735
|
+
const value2 = variables[name];
|
|
22736
|
+
if (value2 === void 0)
|
|
22737
|
+
return "";
|
|
22738
|
+
const encoded2 = Array.isArray(value2) ? value2.map((v) => this.encodeValue(v, part.operator)).join(",") : this.encodeValue(value2.toString(), part.operator);
|
|
22739
|
+
return `${name}=${encoded2}`;
|
|
22740
|
+
}).filter((pair) => pair.length > 0);
|
|
22741
|
+
if (pairs.length === 0)
|
|
22742
|
+
return "";
|
|
22743
|
+
const separator = part.operator === "?" ? "?" : "&";
|
|
22744
|
+
return separator + pairs.join("&");
|
|
22745
|
+
}
|
|
22746
|
+
if (part.names.length > 1) {
|
|
22747
|
+
const values2 = part.names.map((name) => variables[name]).filter((v) => v !== void 0);
|
|
22748
|
+
if (values2.length === 0)
|
|
22749
|
+
return "";
|
|
22750
|
+
return values2.map((v) => Array.isArray(v) ? v[0] : v).join(",");
|
|
22751
|
+
}
|
|
22752
|
+
const value = variables[part.name];
|
|
22753
|
+
if (value === void 0)
|
|
22754
|
+
return "";
|
|
22755
|
+
const values = Array.isArray(value) ? value : [value];
|
|
22756
|
+
const encoded = values.map((v) => this.encodeValue(v, part.operator));
|
|
22757
|
+
switch (part.operator) {
|
|
22758
|
+
case "":
|
|
22759
|
+
return encoded.join(",");
|
|
22760
|
+
case "+":
|
|
22761
|
+
return encoded.join(",");
|
|
22762
|
+
case "#":
|
|
22763
|
+
return "#" + encoded.join(",");
|
|
22764
|
+
case ".":
|
|
22765
|
+
return "." + encoded.join(".");
|
|
22766
|
+
case "/":
|
|
22767
|
+
return "/" + encoded.join("/");
|
|
22768
|
+
default:
|
|
22769
|
+
return encoded.join(",");
|
|
22770
|
+
}
|
|
22771
|
+
}
|
|
22772
|
+
expand(variables) {
|
|
22773
|
+
let result = "";
|
|
22774
|
+
let hasQueryParam = false;
|
|
22775
|
+
for (const part of this.parts) {
|
|
22776
|
+
if (typeof part === "string") {
|
|
22777
|
+
result += part;
|
|
22778
|
+
continue;
|
|
22779
|
+
}
|
|
22780
|
+
const expanded = this.expandPart(part, variables);
|
|
22781
|
+
if (!expanded)
|
|
22782
|
+
continue;
|
|
22783
|
+
if ((part.operator === "?" || part.operator === "&") && hasQueryParam) {
|
|
22784
|
+
result += expanded.replace("?", "&");
|
|
22785
|
+
} else {
|
|
22786
|
+
result += expanded;
|
|
22787
|
+
}
|
|
22788
|
+
if (part.operator === "?" || part.operator === "&") {
|
|
22789
|
+
hasQueryParam = true;
|
|
22790
|
+
}
|
|
22791
|
+
}
|
|
22792
|
+
return result;
|
|
22793
|
+
}
|
|
22794
|
+
escapeRegExp(str) {
|
|
22795
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
22796
|
+
}
|
|
22797
|
+
partToRegExp(part) {
|
|
22798
|
+
const patterns = [];
|
|
22799
|
+
for (const name2 of part.names) {
|
|
22800
|
+
_UriTemplate.validateLength(name2, MAX_VARIABLE_LENGTH, "Variable name");
|
|
22801
|
+
}
|
|
22802
|
+
if (part.operator === "?" || part.operator === "&") {
|
|
22803
|
+
for (let i = 0; i < part.names.length; i++) {
|
|
22804
|
+
const name2 = part.names[i];
|
|
22805
|
+
const prefix = i === 0 ? "\\" + part.operator : "&";
|
|
22806
|
+
patterns.push({
|
|
22807
|
+
pattern: prefix + this.escapeRegExp(name2) + "=([^&]+)",
|
|
22808
|
+
name: name2
|
|
22809
|
+
});
|
|
22810
|
+
}
|
|
22811
|
+
return patterns;
|
|
22812
|
+
}
|
|
22813
|
+
let pattern;
|
|
22814
|
+
const name = part.name;
|
|
22815
|
+
switch (part.operator) {
|
|
22816
|
+
case "":
|
|
22817
|
+
pattern = part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)";
|
|
22818
|
+
break;
|
|
22819
|
+
case "+":
|
|
22820
|
+
case "#":
|
|
22821
|
+
pattern = "(.+)";
|
|
22822
|
+
break;
|
|
22823
|
+
case ".":
|
|
22824
|
+
pattern = "\\.([^/,]+)";
|
|
22825
|
+
break;
|
|
22826
|
+
case "/":
|
|
22827
|
+
pattern = "/" + (part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)");
|
|
22828
|
+
break;
|
|
22829
|
+
default:
|
|
22830
|
+
pattern = "([^/]+)";
|
|
22831
|
+
}
|
|
22832
|
+
patterns.push({ pattern, name });
|
|
22833
|
+
return patterns;
|
|
22834
|
+
}
|
|
22835
|
+
match(uri) {
|
|
22836
|
+
_UriTemplate.validateLength(uri, MAX_TEMPLATE_LENGTH, "URI");
|
|
22837
|
+
let pattern = "^";
|
|
22838
|
+
const names = [];
|
|
22839
|
+
for (const part of this.parts) {
|
|
22840
|
+
if (typeof part === "string") {
|
|
22841
|
+
pattern += this.escapeRegExp(part);
|
|
22842
|
+
} else {
|
|
22843
|
+
const patterns = this.partToRegExp(part);
|
|
22844
|
+
for (const { pattern: partPattern, name } of patterns) {
|
|
22845
|
+
pattern += partPattern;
|
|
22846
|
+
names.push({ name, exploded: part.exploded });
|
|
22847
|
+
}
|
|
22848
|
+
}
|
|
22849
|
+
}
|
|
22850
|
+
pattern += "$";
|
|
22851
|
+
_UriTemplate.validateLength(pattern, MAX_REGEX_LENGTH, "Generated regex pattern");
|
|
22852
|
+
const regex = new RegExp(pattern);
|
|
22853
|
+
const match = uri.match(regex);
|
|
22854
|
+
if (!match)
|
|
22855
|
+
return null;
|
|
22856
|
+
const result = {};
|
|
22857
|
+
for (let i = 0; i < names.length; i++) {
|
|
22858
|
+
const { name, exploded } = names[i];
|
|
22859
|
+
const value = match[i + 1];
|
|
22860
|
+
const cleanName = name.replace("*", "");
|
|
22861
|
+
if (exploded && value.includes(",")) {
|
|
22862
|
+
result[cleanName] = value.split(",");
|
|
22863
|
+
} else {
|
|
22864
|
+
result[cleanName] = value;
|
|
22865
|
+
}
|
|
22866
|
+
}
|
|
22867
|
+
return result;
|
|
22868
|
+
}
|
|
22869
|
+
};
|
|
22870
|
+
|
|
22649
22871
|
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.32.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
|
|
22650
22872
|
var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
|
|
22651
22873
|
function validateToolName(name) {
|
|
@@ -23475,6 +23697,30 @@ var McpServer = class {
|
|
|
23475
23697
|
}
|
|
23476
23698
|
}
|
|
23477
23699
|
};
|
|
23700
|
+
var ResourceTemplate = class {
|
|
23701
|
+
constructor(uriTemplate, _callbacks) {
|
|
23702
|
+
this._callbacks = _callbacks;
|
|
23703
|
+
this._uriTemplate = typeof uriTemplate === "string" ? new UriTemplate(uriTemplate) : uriTemplate;
|
|
23704
|
+
}
|
|
23705
|
+
/**
|
|
23706
|
+
* Gets the URI template pattern.
|
|
23707
|
+
*/
|
|
23708
|
+
get uriTemplate() {
|
|
23709
|
+
return this._uriTemplate;
|
|
23710
|
+
}
|
|
23711
|
+
/**
|
|
23712
|
+
* Gets the list callback, if one was provided.
|
|
23713
|
+
*/
|
|
23714
|
+
get listCallback() {
|
|
23715
|
+
return this._callbacks.list;
|
|
23716
|
+
}
|
|
23717
|
+
/**
|
|
23718
|
+
* Gets the callback for completing a specific URI template variable, if one was provided.
|
|
23719
|
+
*/
|
|
23720
|
+
completeCallback(variable) {
|
|
23721
|
+
return this._callbacks.complete?.[variable];
|
|
23722
|
+
}
|
|
23723
|
+
};
|
|
23478
23724
|
var EMPTY_OBJECT_JSON_SCHEMA = {
|
|
23479
23725
|
type: "object",
|
|
23480
23726
|
properties: {}
|
|
@@ -23551,6 +23797,68 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
23551
23797
|
}
|
|
23552
23798
|
};
|
|
23553
23799
|
|
|
23800
|
+
// src/prompts.ts
|
|
23801
|
+
var TASK_ARG = external_exports.string().min(1).describe("\u4EFB\u52A1\u7F16\u53F7\uFF0C\u5982 MOM-12");
|
|
23802
|
+
var user = (lines) => [
|
|
23803
|
+
{ role: "user", content: { type: "text", text: lines.filter((l) => typeof l === "string").join("\n") } }
|
|
23804
|
+
];
|
|
23805
|
+
function createPrompts(profile = "work") {
|
|
23806
|
+
const canDigest = profile === "full";
|
|
23807
|
+
return [
|
|
23808
|
+
{
|
|
23809
|
+
name: "start_task",
|
|
23810
|
+
title: "\u5F00\u59CB\u505A\u4EFB\u52A1",
|
|
23811
|
+
description: "\u9886\u53D6\u4E00\u4E2A Momentum \u4EFB\u52A1\u5E76\u6309\u56E2\u961F\u6D41\u7A0B\u5F00\u5DE5\uFF1A\u8BFB\u4E0A\u4E0B\u6587\u3001\u8BB2\u6E05\u65B9\u6848\u3001\u52A8\u624B\u3001\u5173\u952E\u8282\u70B9\u7559\u8BC4\u8BBA\u3002",
|
|
23812
|
+
argsSchema: { task: TASK_ARG, note: external_exports.string().optional().describe("\u8FD9\u6B21\u8981\u505A\u4EC0\u4E48\u3001\u6709\u4EC0\u4E48\u989D\u5916\u8981\u6C42") },
|
|
23813
|
+
render: ({ task, note }) => user([
|
|
23814
|
+
`\u8BF7\u5F00\u59CB\u505A Momentum \u4EFB\u52A1 ${task}\u3002`,
|
|
23815
|
+
"",
|
|
23816
|
+
`1. \u76F4\u63A5\u8C03 begin_task("${task}")\uFF1A\u5B83\u4E00\u6B21\u5B8C\u6210\u89E3\u6790\u7F16\u53F7\u3001\u5F00\u8FD0\u884C\u3001\u9886\u53D6\u5E76\u8FD4\u56DE\u7D27\u51D1\u4E0A\u4E0B\u6587\uFF0C\u4E0D\u5FC5\u5148 whoami / get_task\u3002\u5FC3\u8DF3\u81EA\u52A8\u7EED\u7EA6\u3002`,
|
|
23817
|
+
"2. \u8BFB\u4E0A\u4E0B\u6587\uFF1A\u63CF\u8FF0\u3001\u9A8C\u6536\u6807\u51C6\uFF08\u8BB0\u4E0B specVersion\uFF0C\u4EA4\u4ED8\u65F6\u8981\u7528\uFF09\u3001\u4F9D\u8D56\u3001\u5DF2\u6709\u51B3\u5B9A\u3001\u5F85\u7B54\u95EE\u9898\u548C\u6700\u65B0\u4EA4\u4ED8\u3002\u88AB\u4F9D\u8D56\u5361\u4F4F\u3001\u6216\u6709\u963B\u585E\u95EE\u9898\u6CA1\u7B54\u65F6\uFF0C\u5148\u505C\u4E0B\u6765\u544A\u8BC9\u6211\u3002",
|
|
23818
|
+
"3. \u52A8\u624B\u524D\u5148\u7528\u5927\u767D\u8BDD\u8BB2\u6E05\u6253\u7B97\u600E\u4E48\u505A\u3001\u6BCF\u6761\u9A8C\u6536\u6807\u51C6\u6253\u7B97\u600E\u4E48\u9A8C\u8BC1\uFF1B\u65B9\u6848\u503C\u5F97\u7559\u6863\u5C31\u7528 update_task_plan \u5199\u8FDB\u6267\u884C\u7EC6\u5316\u3002",
|
|
23819
|
+
"4. \u5F00\u59CB\u5E72\u6D3B\u3002\u7F3A\u4FE1\u606F\u5148\u95EE\u6211\uFF1B\u6211\u7B54\u4E0D\u4E86\u3001\u8981\u56E2\u961F\u62CD\u677F\u7684\u624D\u7528 ask_question\u3002\u5173\u952E\u8282\u70B9\u7528 comment_on_task \u5199\u4E00\u6761\u77ED\u8FDB\u5C55\uFF08\u5148\u7ED3\u8BBA\u3001\u518D\u8981\u70B9\uFF09\u3002",
|
|
23820
|
+
"5. \u505A\u5B8C\u6309\u300C\u5199\u4EA4\u4ED8\u8BF4\u660E\u300D\uFF08write_delivery\uFF09\u7684\u8981\u6C42\u8C03 finish_task_work \u63D0\u4EA4\u4EA4\u4ED8\u5E76\u79FB\u5230\u8BC4\u5BA1\u3002",
|
|
23821
|
+
note && "",
|
|
23822
|
+
note && `\u8865\u5145\u8981\u6C42\uFF1A${note}`
|
|
23823
|
+
])
|
|
23824
|
+
},
|
|
23825
|
+
{
|
|
23826
|
+
name: "digest_discussion",
|
|
23827
|
+
title: "\u6574\u7406\u8BA8\u8BBA\u6210\u63D0\u6848",
|
|
23828
|
+
description: "\u628A\u5F53\u524D\u4F1A\u8BDD\u91CC\u7684\u8BA8\u8BBA\u6574\u7406\u6210\u4EFB\u52A1\u7684\u4FEE\u6539\u63D0\u6848\uFF1A\u62CD\u677F\u7684\u51B3\u5B9A\u3001\u5F85\u7B54\u95EE\u9898\u3001\u8BF4\u660E\u4E0E\u9A8C\u6536\u6807\u51C6\u7684\u6539\u52A8\u3002",
|
|
23829
|
+
argsSchema: { task: TASK_ARG, focus: external_exports.string().optional().describe("\u53EA\u6574\u7406\u8BA8\u8BBA\u91CC\u7684\u54EA\u4E00\u90E8\u5206") },
|
|
23830
|
+
render: ({ task, focus }) => user([
|
|
23831
|
+
`\u628A\u6211\u4EEC\u8FD9\u6BB5\u8BA8\u8BBA\u6574\u7406\u6210\u4EFB\u52A1 ${task} \u7684\u4FEE\u6539\u63D0\u6848\u3002`,
|
|
23832
|
+
focus && `\u53EA\u6574\u7406\u8FD9\u90E8\u5206\uFF1A${focus}`,
|
|
23833
|
+
"",
|
|
23834
|
+
`1. \u5148\u8C03 get_task_context("${task}") \u770B\u5F53\u524D\u8BF4\u660E\u3001\u7248\u672C\u53F7 specVersion\u3001\u5DF2\u6709\u51B3\u5B9A\u548C\u5F85\u7B54\u95EE\u9898\uFF0C\u5DF2\u6709\u7684\u4E0D\u8981\u91CD\u590D\u3002`,
|
|
23835
|
+
"2. \u4ECE\u8BA8\u8BBA\u91CC\u63D0\u53D6\u4E09\u7C7B\u4E1C\u897F\uFF1A",
|
|
23836
|
+
" - \u5DF2\u7ECF\u62CD\u677F\u7684\u51B3\u5B9A\uFF1A\u4E00\u6761\u53EA\u8BF4\u4E00\u4EF6\u4E8B\uFF0C\u5199\u6E05\u7406\u7531\uFF1B",
|
|
23837
|
+
" - \u8FD8\u6CA1\u6709\u7B54\u6848\u7684\u95EE\u9898\uFF1A\u6807\u51FA\u54EA\u4E9B\u4F1A\u5361\u4F4F\u8FDB\u5EA6\uFF1B",
|
|
23838
|
+
" - \u63CF\u8FF0\u6216\u9A8C\u6536\u6807\u51C6\u8981\u600E\u4E48\u6539\uFF1A\u9A8C\u6536\u6807\u51C6\u6BCF\u6761\u53EF\u6838\u5BF9\uFF0C\u4FDD\u7559\u539F\u6709\u7684 id\u3002",
|
|
23839
|
+
" \u8BA8\u8BBA\u4E2D\u7684\u8BBE\u60F3\u3001\u53EA\u6709\u6211\u4E00\u65B9\u63D0\u8FC7\u7684\u60F3\u6CD5\u4E0D\u7B97\u51B3\u5B9A\uFF0C\u4E0D\u80FD\u81EA\u884C\u8BA4\u5B9A\u3002",
|
|
23840
|
+
"3. \u5148\u628A\u8349\u7A3F\u7528\u5927\u767D\u8BDD\u5217\u7ED9\u6211\u8FC7\u76EE\uFF0C\u6211\u786E\u8BA4\u540E\u518D\u63D0\u4EA4\u3002",
|
|
23841
|
+
canDigest ? "4. \u786E\u8BA4\u540E\u8C03 propose_digest\uFF0Cspec_change.expected_version \u586B\u7B2C 1 \u6B65\u8BFB\u5230\u7684 specVersion\u3002\u5B83\u662F\u4E00\u6761\u5F85\u4EBA\u786E\u8BA4\u7684\u63D0\u6848\uFF1A\u63A5\u53D7\u540E\u4E00\u6B21\u751F\u6548\u3001\u8BF4\u660E\u5F62\u6210\u65B0\u7248\u672C\uFF0C\u5728\u90A3\u4E4B\u524D\u4EFB\u52A1\u6CA1\u6709\u4EFB\u4F55\u6539\u52A8\u3002" : "4. \u5F53\u524D\u5DE5\u5177\u6863\uFF08work\uFF09\u6CA1\u6709 propose_digest\uFF1A\u786E\u8BA4\u540E\u628A\u51B3\u5B9A\u548C\u8BF4\u660E\u6539\u52A8\u7528 comment_on_task \u8BB0\u5230\u4EFB\u52A1\u4E0B\uFF0C\u963B\u585E\u95EE\u9898\u7528 ask_question \u63D0\u51FA\u6765\uFF1B\u5E76\u63D0\u9192\u6211\u8BF4\u660E\u672C\u8EAB\u8981\u5728 Momentum \u754C\u9762\u91CC\u6539\uFF0C\u6216\u628A MOMENTUM_TOOL_PROFILE \u8BBE\u6210 full \u540E\u518D\u63D0\u4EA4\u63D0\u6848\u3002"
|
|
23842
|
+
])
|
|
23843
|
+
},
|
|
23844
|
+
{
|
|
23845
|
+
name: "write_delivery",
|
|
23846
|
+
title: "\u5199\u4EA4\u4ED8\u8BF4\u660E",
|
|
23847
|
+
description: "\u6309\u4EFB\u52A1\u8BF4\u660E\u9010\u6761\u8986\u76D6\u9A8C\u6536\u6807\u51C6\uFF0C\u5199\u597D\u4EA4\u4ED8\u8BF4\u660E\u5E76\u8C03 finish_task_work \u63D0\u4EA4\u3001\u79FB\u5230\u8BC4\u5BA1\u3002",
|
|
23848
|
+
argsSchema: { task: TASK_ARG },
|
|
23849
|
+
render: ({ task }) => user([
|
|
23850
|
+
`\u4E3A\u4EFB\u52A1 ${task} \u5199\u4EA4\u4ED8\u8BF4\u660E\u5E76\u63D0\u4EA4\u3002`,
|
|
23851
|
+
"",
|
|
23852
|
+
`1. \u624B\u4E0A\u6CA1\u6709\u9A8C\u6536\u6807\u51C6\u548C specVersion \u65F6\u5148\u8C03 get_task_context("${task}")\u3002based_on_spec_version \u586B\u4F60\u5B9E\u9645\u5BF9\u7167\u7684\u90A3\u4E2A\u7248\u672C\uFF1B\u5F00\u5DE5\u540E\u8BF4\u660E\u5347\u8FC7\u7248\uFF0C\u5148\u5BF9\u7167\u65B0\u7248\u672C\u770B\u8FD8\u5DEE\u4EC0\u4E48\u3002`,
|
|
23853
|
+
"2. summary \u5199\u7ED9\u4EFB\u52A1\u6307\u6D3E\u7684\u4EBA\u770B\uFF1A\u5927\u767D\u8BDD\u8BB2\u505A\u4E86\u4EC0\u4E48\u3001\u5173\u952E\u53D6\u820D\u3001\u5BF9\u4F7F\u7528\u8005\u7684\u5F71\u54CD\uFF1B\u6D41\u7A0B\u6216\u6A21\u5757\u5173\u7CFB\u7528 mermaid \u753B\u51FA\u6765\u3002\u5DE5\u7A0B\u5E08\u591A\u8BB2\u5B9E\u73B0\u53D6\u820D\uFF0C\u5176\u4ED6\u4EBA\u53EA\u8BB2\u7ED3\u679C\u3002",
|
|
23854
|
+
"3. results \u9010\u6761\u8986\u76D6\u6BCF\u4E2A\u9A8C\u6536\u6807\u51C6\uFF08criterionId \u7528\u8BF4\u660E\u91CC\u7684 id\uFF09\uFF1Aoutcome \u53EA\u7528 passed / failed / unverified\uFF0Cevidence \u5199\u522B\u4EBA\u80FD\u6838\u5BF9\u7684\u8BC1\u636E\uFF08\u6D4B\u8BD5\u540D\u3001\u547D\u4EE4\u8F93\u51FA\u3001\u94FE\u63A5\uFF09\u3002\u6CA1\u6709\u771F\u6B63\u9A8C\u8BC1\u8FC7\u7684\u6807 unverified\uFF0C\u4E0D\u8981\u731C\u3002",
|
|
23855
|
+
"4. artifacts \u653E PR\u3001\u6587\u6863\u7B49\u94FE\u63A5\uFF1B\u6CA1\u505A\u5B8C\u7684\u5199\u8FDB remaining_work\uFF1B\u63A5\u624B\u7684\u4EBA\u9700\u8981\u77E5\u9053\u7684\u5199\u8FDB handoff\u3002",
|
|
23856
|
+
"5. \u8C03 finish_task_work \u63D0\u4EA4\uFF1Aidempotency_key \u53D6\u4E00\u4E2A\u7A33\u5B9A\u7684\u503C\uFF0C\u67D0\u4E00\u6B65\u5931\u8D25\u65F6\u7528\u76F8\u540C\u53C2\u6570\u91CD\u8BD5\u3002\u4EFB\u52A1\u4F1A\u8FDB\u8BC4\u5BA1\uFF0C\u8BC4\u5BA1\u901A\u8FC7\u524D\u4E0D\u7B97\u5B8C\u6210\u3002"
|
|
23857
|
+
])
|
|
23858
|
+
}
|
|
23859
|
+
];
|
|
23860
|
+
}
|
|
23861
|
+
|
|
23554
23862
|
// ../../packages/core/src/task-schemas.ts
|
|
23555
23863
|
var markdownSchema = external_exports.string().max(2e5);
|
|
23556
23864
|
var httpUrl = external_exports.string().url().max(2e3).refine((url) => /^https?:\/\//i.test(url), "\u53EA\u652F\u6301 HTTP(S) \u94FE\u63A5");
|
|
@@ -23601,11 +23909,13 @@ var RunHeartbeats = class {
|
|
|
23601
23909
|
lastRunOfTask = /* @__PURE__ */ new Map();
|
|
23602
23910
|
pending = [];
|
|
23603
23911
|
timer = null;
|
|
23912
|
+
closed = false;
|
|
23604
23913
|
intervalMs;
|
|
23605
23914
|
leaseSeconds;
|
|
23606
23915
|
log;
|
|
23607
23916
|
/** 开始替这次运行续约。expectClaim = 它手上应当有领取,续不上要告诉模型 */
|
|
23608
23917
|
track(runId, taskId, expectClaim = false) {
|
|
23918
|
+
if (this.closed) return;
|
|
23609
23919
|
this.runs.set(runId, { taskId, expectClaim });
|
|
23610
23920
|
if (taskId) this.lastRunOfTask.set(taskId, runId);
|
|
23611
23921
|
if (!this.timer) {
|
|
@@ -23628,6 +23938,10 @@ var RunHeartbeats = class {
|
|
|
23628
23938
|
has(runId) {
|
|
23629
23939
|
return this.runs.has(runId);
|
|
23630
23940
|
}
|
|
23941
|
+
/** 正在续约的运行数 */
|
|
23942
|
+
get size() {
|
|
23943
|
+
return this.runs.size;
|
|
23944
|
+
}
|
|
23631
23945
|
/** 取走攒下的提醒。每条只给模型看一次 */
|
|
23632
23946
|
drainNotices() {
|
|
23633
23947
|
return this.pending.splice(0);
|
|
@@ -23636,6 +23950,15 @@ var RunHeartbeats = class {
|
|
|
23636
23950
|
if (this.timer) clearInterval(this.timer);
|
|
23637
23951
|
this.timer = null;
|
|
23638
23952
|
}
|
|
23953
|
+
/**
|
|
23954
|
+
* 永久停掉:之后再 track 也不会续约。远程会话(http.ts)结束时用 —— 会话关掉的那一刻
|
|
23955
|
+
* 可能还有一个 begin_task 在路上,它回来时的 track 不能把一个已经没人的会话重新续活
|
|
23956
|
+
*/
|
|
23957
|
+
close() {
|
|
23958
|
+
this.closed = true;
|
|
23959
|
+
this.runs.clear();
|
|
23960
|
+
this.stop();
|
|
23961
|
+
}
|
|
23639
23962
|
/** 给所有在跟的运行各发一次心跳。定时器调它,测试也直接调它 */
|
|
23640
23963
|
async tick() {
|
|
23641
23964
|
await Promise.all([...this.runs].map(([runId, run2]) => this.beatOne(runId, run2)));
|
|
@@ -23977,6 +24300,14 @@ var BEGIN_OUTPUT = external_exports.object({
|
|
|
23977
24300
|
context: CONTEXT_SCHEMA.optional(),
|
|
23978
24301
|
proposalId: external_exports.string().optional()
|
|
23979
24302
|
}).passthrough();
|
|
24303
|
+
var NAMED = external_exports.object({ id: external_exports.string(), name: external_exports.string() }).passthrough();
|
|
24304
|
+
var WHOAMI_OUTPUT = external_exports.object({
|
|
24305
|
+
kind: external_exports.enum(["agent", "human"]),
|
|
24306
|
+
actorId: external_exports.string().nullable(),
|
|
24307
|
+
orgId: external_exports.string().nullable(),
|
|
24308
|
+
agent: NAMED.optional(),
|
|
24309
|
+
instance: NAMED.optional()
|
|
24310
|
+
}).passthrough();
|
|
23980
24311
|
var PAGE_OUTPUT = external_exports.object({ items: external_exports.array(external_exports.unknown()) }).passthrough();
|
|
23981
24312
|
var TASKS_OUTPUT = external_exports.object({ items: external_exports.array(external_exports.object(TASK_FIELDS).passthrough()) }).passthrough();
|
|
23982
24313
|
var BROWSE_OUTCOME = external_exports.enum(["ok", "no_projects", "no_match", "project_unavailable", "failed"]);
|
|
@@ -23986,7 +24317,7 @@ var BROWSE_OUTPUT = external_exports.object({
|
|
|
23986
24317
|
items: external_exports.array(external_exports.unknown()),
|
|
23987
24318
|
nextOffset: external_exports.number().nullable()
|
|
23988
24319
|
}).passthrough();
|
|
23989
|
-
var READ = { readOnlyHint: true, openWorldHint: false };
|
|
24320
|
+
var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
23990
24321
|
var write = (destructiveHint, idempotentHint) => ({
|
|
23991
24322
|
readOnlyHint: false,
|
|
23992
24323
|
destructiveHint,
|
|
@@ -24010,7 +24341,7 @@ var TOOL_META = {
|
|
|
24010
24341
|
list_task_deliveries: { annotations: READ, outputSchema: PAGE_OUTPUT },
|
|
24011
24342
|
poll_events: { annotations: READ, outputSchema: PAGE_OUTPUT },
|
|
24012
24343
|
current_cycle: { annotations: READ },
|
|
24013
|
-
whoami: { annotations: READ },
|
|
24344
|
+
whoami: { annotations: READ, outputSchema: WHOAMI_OUTPUT },
|
|
24014
24345
|
// 开工 / 收尾:重复调用沿用同一次运行、交付按 idempotency_key 去重
|
|
24015
24346
|
begin_task: { annotations: write(false, true), outputSchema: BEGIN_OUTPUT },
|
|
24016
24347
|
finish_task_work: { annotations: write(false, true), outputSchema: FINISH_OUTPUT },
|
|
@@ -25381,6 +25712,52 @@ function explain(err) {
|
|
|
25381
25712
|
return `\u5931\u8D25\uFF1A${message}`;
|
|
25382
25713
|
}
|
|
25383
25714
|
|
|
25715
|
+
// src/resources.ts
|
|
25716
|
+
var SPEC_URI = (key, version2) => `momentum://task/${encodeURIComponent(key)}/spec${version2 === void 0 ? "" : `/${version2}`}`;
|
|
25717
|
+
var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
25718
|
+
function renderSpec(key, spec, current) {
|
|
25719
|
+
const at = spec.createdAt instanceof Date ? spec.createdAt.toISOString() : spec.createdAt;
|
|
25720
|
+
const criteria = spec.acceptance?.criteria ?? [];
|
|
25721
|
+
return [
|
|
25722
|
+
`# ${key} \xB7 ${spec.title}`,
|
|
25723
|
+
"",
|
|
25724
|
+
`\u8BF4\u660E\u7248\u672C v${spec.version}\uFF08\u4E0D\u53EF\u53D8\uFF0C${current ? "\u5F53\u524D\u7248\uFF0C" : ""}\u5730\u5740 ${SPEC_URI(key, spec.version)}\uFF09\xB7 ${at}`,
|
|
25725
|
+
...spec.changeSummary ? [`\u672C\u7248\u53D8\u66F4\uFF1A${spec.changeSummary}`] : [],
|
|
25726
|
+
"",
|
|
25727
|
+
"## \u63CF\u8FF0",
|
|
25728
|
+
"",
|
|
25729
|
+
spec.descriptionMd?.trim() || "\uFF08\u65E0\u63CF\u8FF0\uFF09",
|
|
25730
|
+
"",
|
|
25731
|
+
"## \u9A8C\u6536\u6807\u51C6",
|
|
25732
|
+
"",
|
|
25733
|
+
...criteria.length ? criteria.map((c) => `- [${c.id}] ${c.text}`) : ["\uFF08\u672A\u5B9A\u4E49\u9A8C\u6536\u6807\u51C6\uFF09"]
|
|
25734
|
+
].join("\n");
|
|
25735
|
+
}
|
|
25736
|
+
function createSpecReader(client) {
|
|
25737
|
+
async function resolve(ref) {
|
|
25738
|
+
const [found] = UUID2.test(ref) ? [await client.task.ref.query({ taskId: ref })] : await client.task.list.query({ key: ref, limit: 1 });
|
|
25739
|
+
if (!found) throw new Error(`\u627E\u4E0D\u5230\u4EFB\u52A1 '${ref}'`);
|
|
25740
|
+
return { id: found.id, key: found.key };
|
|
25741
|
+
}
|
|
25742
|
+
return async function readSpec(ref, version2) {
|
|
25743
|
+
if (version2 !== void 0 && !/^\d+$/.test(version2)) {
|
|
25744
|
+
throw new Error(`\u8BF4\u660E\u7248\u672C\u53F7\u5FC5\u987B\u662F\u975E\u8D1F\u6574\u6570\uFF0C\u6536\u5230 '${version2}'`);
|
|
25745
|
+
}
|
|
25746
|
+
try {
|
|
25747
|
+
const task = await resolve(ref);
|
|
25748
|
+
const current = (await client.task.context.query({ taskId: task.id })).specVersion;
|
|
25749
|
+
const wanted = version2 === void 0 ? current : Number(version2);
|
|
25750
|
+
const spec = await client.task.spec.query({ taskId: task.id, version: wanted });
|
|
25751
|
+
return {
|
|
25752
|
+
mimeType: "text/markdown",
|
|
25753
|
+
text: renderSpec(task.key, spec, wanted === current)
|
|
25754
|
+
};
|
|
25755
|
+
} catch (err) {
|
|
25756
|
+
throw new Error(explain(err));
|
|
25757
|
+
}
|
|
25758
|
+
};
|
|
25759
|
+
}
|
|
25760
|
+
|
|
25384
25761
|
// src/server.ts
|
|
25385
25762
|
var SERVER_INFO = {
|
|
25386
25763
|
name: "momentum",
|
|
@@ -25426,6 +25803,29 @@ function createMcpServer(client, profile = "work", options = {}) {
|
|
|
25426
25803
|
(args, extra) => tool.handler(args, extra._meta)
|
|
25427
25804
|
);
|
|
25428
25805
|
}
|
|
25806
|
+
for (const prompt of createPrompts(profile)) {
|
|
25807
|
+
server.registerPrompt(
|
|
25808
|
+
prompt.name,
|
|
25809
|
+
{ title: prompt.title, description: prompt.description, argsSchema: prompt.argsSchema },
|
|
25810
|
+
(args) => ({ description: prompt.description, messages: prompt.render(args) })
|
|
25811
|
+
);
|
|
25812
|
+
}
|
|
25813
|
+
const readSpec = createSpecReader(client);
|
|
25814
|
+
const one = (value) => Array.isArray(value) ? value[0] ?? "" : value ?? "";
|
|
25815
|
+
server.registerResource(
|
|
25816
|
+
"task_spec",
|
|
25817
|
+
new ResourceTemplate("momentum://task/{key}/spec", { list: void 0 }),
|
|
25818
|
+
{ title: "\u4EFB\u52A1\u8BF4\u660E\uFF08\u5F53\u524D\u7248\uFF09", description: "\u4EFB\u52A1\u5F53\u524D\u7248\u672C\u7684\u6807\u9898\u3001\u63CF\u8FF0\u548C\u9A8C\u6536\u6807\u51C6\uFF0C\u6B63\u6587\u91CC\u5199\u660E\u7248\u672C\u53F7\u548C\u90A3\u4E00\u7248\u7684\u4E0D\u53EF\u53D8\u5730\u5740\u3002", mimeType: "text/markdown" },
|
|
25819
|
+
async (uri, vars) => ({ contents: [{ uri: uri.href, ...await readSpec(decodeURIComponent(one(vars.key))) }] })
|
|
25820
|
+
);
|
|
25821
|
+
server.registerResource(
|
|
25822
|
+
"task_spec_version",
|
|
25823
|
+
new ResourceTemplate("momentum://task/{key}/spec/{version}", { list: void 0 }),
|
|
25824
|
+
{ title: "\u4EFB\u52A1\u8BF4\u660E\uFF08\u6307\u5B9A\u7248\u672C\uFF09", description: "\u4EFB\u52A1\u8BF4\u660E\u7684\u67D0\u4E00\u7248\uFF0C\u5185\u5BB9\u4E0D\u53EF\u53D8\u3002\u4EA4\u4ED8\u65F6 based_on_spec_version \u5BF9\u7167\u7684\u5C31\u662F\u5B83\u3002", mimeType: "text/markdown" },
|
|
25825
|
+
async (uri, vars) => ({
|
|
25826
|
+
contents: [{ uri: uri.href, ...await readSpec(decodeURIComponent(one(vars.key)), decodeURIComponent(one(vars.version))) }]
|
|
25827
|
+
})
|
|
25828
|
+
);
|
|
25429
25829
|
return server;
|
|
25430
25830
|
}
|
|
25431
25831
|
|