@brainervirus/workit-codex 1.0.10 → 1.0.12

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.
@@ -6625,9 +6625,9 @@ import { existsSync as existsSync8, realpathSync as realpathSync8, statSync as s
6625
6625
  import path15 from "node:path";
6626
6626
 
6627
6627
  // packages/workit-core/src/core.ts
6628
- import { spawnSync as spawnSync2 } from "node:child_process";
6629
- import { existsSync as existsSync4, realpathSync as realpathSync5 } from "node:fs";
6630
- import path7 from "node:path";
6628
+ import { spawnSync as spawnSync3 } from "node:child_process";
6629
+ import { existsSync as existsSync5, realpathSync as realpathSync6 } from "node:fs";
6630
+ import path10 from "node:path";
6631
6631
 
6632
6632
  // packages/workit-core/src/core/task-contract.ts
6633
6633
  import { createHash, randomUUID } from "node:crypto";
@@ -14201,6 +14201,89 @@ var refSchema = discriminatedUnion("kind", [
14201
14201
  object({ kind: literal("host"), host: hostSchema, handle: nonEmpty }).strict(),
14202
14202
  object({ kind: literal("external"), url: text.url() }).strict()
14203
14203
  ]);
14204
+ var rewriteUnknown = (value, map) => {
14205
+ if (Array.isArray(value)) {
14206
+ const out = [];
14207
+ for (const item of value) {
14208
+ const rewritten = rewriteUnknown(item, map);
14209
+ if (!rewritten.drop)
14210
+ out.push(rewritten.value);
14211
+ }
14212
+ return { drop: false, value: out };
14213
+ }
14214
+ if (typeof value === "object" && value !== null) {
14215
+ if (refSchema.safeParse(value).success) {
14216
+ const mapped = map(value);
14217
+ return mapped === null ? { drop: true } : { drop: false, value: mapped };
14218
+ }
14219
+ const out = {};
14220
+ for (const [key, item] of Object.entries(value)) {
14221
+ const rewritten = rewriteUnknown(item, map);
14222
+ out[key] = rewritten.drop ? null : rewritten.value;
14223
+ }
14224
+ return { drop: false, value: out };
14225
+ }
14226
+ return { drop: false, value };
14227
+ };
14228
+ var rewriteNode = (value, schema, map) => {
14229
+ if (typeof value === "object" && value !== null && !Array.isArray(value) && refSchema.safeParse(value).success) {
14230
+ const mapped = map(value);
14231
+ return mapped === null ? { drop: true } : { drop: false, value: mapped };
14232
+ }
14233
+ let node = schema;
14234
+ while (node instanceof ZodNullable || node instanceof ZodOptional || node instanceof ZodDefault) {
14235
+ node = node.unwrap();
14236
+ }
14237
+ if (node === refSchema) {
14238
+ if (!refSchema.safeParse(value).success)
14239
+ return { drop: false, value };
14240
+ const mapped = map(value);
14241
+ return mapped === null ? { drop: true } : { drop: false, value: mapped };
14242
+ }
14243
+ if (node instanceof ZodArray) {
14244
+ if (!Array.isArray(value))
14245
+ return { drop: false, value };
14246
+ const out = [];
14247
+ for (const item of value) {
14248
+ const rewritten = rewriteNode(item, node.element, map);
14249
+ if (!rewritten.drop)
14250
+ out.push(rewritten.value);
14251
+ }
14252
+ return { drop: false, value: out };
14253
+ }
14254
+ if (node instanceof ZodObject) {
14255
+ if (typeof value !== "object" || value === null || Array.isArray(value))
14256
+ return { drop: false, value };
14257
+ const shape = node.shape;
14258
+ const record = value;
14259
+ const out = {};
14260
+ for (const [key, item] of Object.entries(record)) {
14261
+ if (!(key in shape)) {
14262
+ const scrubbed = rewriteUnknown(item, map);
14263
+ out[key] = scrubbed.drop ? null : scrubbed.value;
14264
+ continue;
14265
+ }
14266
+ const child = shape[key];
14267
+ const rewritten = rewriteNode(item, child, map);
14268
+ if (rewritten.drop) {
14269
+ if (child instanceof ZodOptional || child instanceof ZodDefault)
14270
+ continue;
14271
+ if (child instanceof ZodNullable) {
14272
+ out[key] = null;
14273
+ continue;
14274
+ }
14275
+ return { drop: true };
14276
+ }
14277
+ out[key] = rewritten.value;
14278
+ }
14279
+ return { drop: false, value: out };
14280
+ }
14281
+ return { drop: false, value };
14282
+ };
14283
+ var rewriteRecordRefs = (value, schema, map) => {
14284
+ const rewritten = rewriteNode(value, schema, map);
14285
+ return rewritten.drop ? null : rewritten.value;
14286
+ };
14204
14287
  var provenanceSchema = object({
14205
14288
  kind: _enum(["host_observed", "agent_reported", "imported"]),
14206
14289
  host: hostSchema,
@@ -14420,10 +14503,11 @@ var decisionSchema = object({
14420
14503
  presented: text,
14421
14504
  approvedContent: text,
14422
14505
  displayed: text.optional(),
14423
- contentRefs: array(refSchema)
14506
+ contentRefs: array(refSchema),
14507
+ statedChoice: object({ ref: text, text }).strict().optional()
14424
14508
  }).strict(),
14425
14509
  digest,
14426
- response: _enum(["approved", "rejected"]),
14510
+ response: _enum(["approved", "rejected", "stated"]),
14427
14511
  requirementIds: array(digest),
14428
14512
  revoked: object({ at: utc, reason: text }).strict().nullable(),
14429
14513
  consumption: object({
@@ -15911,11 +15995,11 @@ class TaskStore {
15911
15995
  return workspace;
15912
15996
  if (!workspace.data)
15913
15997
  return failure("not_found", "workspace not found");
15914
- const expectedTaskRevision = input.expectedTaskRevision ?? input.expectedRevision;
15915
- if (!expectedTaskRevision)
15998
+ const expected = input.expectedRevision;
15999
+ if (!expected)
15916
16000
  return failure("invalid_input", "task revision is required");
15917
- if (task.data.revision !== expectedTaskRevision)
15918
- return this.conflict(expectedTaskRevision, task.data.revision);
16001
+ if (task.data.revision !== expected)
16002
+ return this.conflict(expected, task.data.revision);
15919
16003
  if (workspace.data.revision !== input.expectedWorkspaceRevision)
15920
16004
  return this.conflict(input.expectedWorkspaceRevision, workspace.data.revision);
15921
16005
  const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
@@ -16449,6 +16533,7 @@ var same = (left, right) => {
16449
16533
  };
16450
16534
  var UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
16451
16535
  var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
16536
+ var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
16452
16537
  var callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor };
16453
16538
  var validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
16454
16539
  const code = char.charCodeAt(0);
@@ -16510,6 +16595,36 @@ var trustedAuthority = (kind, provenance, expected, binding, caller) => {
16510
16595
  verifiedAuthorities.set(token, { kind, provenance, ...expected, ...binding, caller });
16511
16596
  return token;
16512
16597
  };
16598
+ var chainStepKey = (step) => step.kind === "commit" ? step.message : step.kind === "branch" ? `branch:${step.target}` : "pr";
16599
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
16600
+ var normalizeChainSteps = (steps) => {
16601
+ if (!Array.isArray(steps) || steps.length === 0 || steps.length > 32)
16602
+ return null;
16603
+ const normalized = [];
16604
+ for (const step of steps) {
16605
+ if (typeof step === "string") {
16606
+ if (!step.trim())
16607
+ return null;
16608
+ normalized.push({ kind: "commit", message: step });
16609
+ continue;
16610
+ }
16611
+ if (!isRecord(step))
16612
+ return null;
16613
+ if (typeof step.branch === "string" && step.branch.trim() && Object.keys(step).length === 1) {
16614
+ normalized.push({ kind: "branch", target: step.branch });
16615
+ continue;
16616
+ }
16617
+ if (step.pr === true && Object.keys(step).length === 1) {
16618
+ normalized.push({ kind: "pr" });
16619
+ continue;
16620
+ }
16621
+ return null;
16622
+ }
16623
+ const keys = normalized.map(chainStepKey);
16624
+ if (new Set(keys).size !== keys.length)
16625
+ return null;
16626
+ return normalized;
16627
+ };
16513
16628
  var scopeEqual = (left, right) => canonicalJson(left) === canonicalJson(right);
16514
16629
  var parseSteps = (content) => {
16515
16630
  let value;
@@ -16521,7 +16636,10 @@ var parseSteps = (content) => {
16521
16636
  if (typeof value !== "object" || value === null || !("steps" in value))
16522
16637
  return [];
16523
16638
  const steps = value.steps;
16524
- return Array.isArray(steps) && steps.every((step) => typeof step === "string") ? steps : null;
16639
+ if (!Array.isArray(steps))
16640
+ return null;
16641
+ const normalized = normalizeChainSteps(steps);
16642
+ return normalized;
16525
16643
  };
16526
16644
  var decisionMatches = (entry, purpose, binding) => {
16527
16645
  const decision = entry.data;
@@ -16712,14 +16830,12 @@ var validateAction = (store, task, workspaceId, input, authority) => {
16712
16830
  return content;
16713
16831
  const requestedSteps = parseSteps(decision.binding.approvedContent);
16714
16832
  if (requestedSteps === null)
16715
- return failure("invalid_input", "bounded action steps must be a string array");
16716
- if (input.steps && canonicalJson(input.steps) !== canonicalJson(requestedSteps))
16833
+ return failure("invalid_input", "bounded action steps must be a unique non-empty array");
16834
+ if (input.steps && canonicalJson(input.steps) !== canonicalJson(requestedSteps.map(chainStepKey)))
16717
16835
  return failure("permission_denied", "bounded action steps do not match approved content");
16718
- if (requestedSteps.some((step) => !step || typeof step !== "string") || new Set(requestedSteps).size !== requestedSteps.length)
16719
- return failure("invalid_input", "bounded action steps must be unique and non-empty");
16720
16836
  const stored = task.actionProgress?.find((item) => item.decisionId === input.decisionId);
16721
- const workflow = stored ? { steps: stored.steps, completed: stored.completedSteps } : { steps: requestedSteps, completed: [] };
16722
- if (workflow.steps.length && requestedSteps.length && canonicalJson(workflow.steps) !== canonicalJson(requestedSteps))
16837
+ const workflow = stored ? { steps: stored.steps, completed: stored.completedSteps } : { steps: requestedSteps.map(chainStepKey), completed: [] };
16838
+ if (workflow.steps.length && requestedSteps.length && canonicalJson(workflow.steps) !== canonicalJson(requestedSteps.map(chainStepKey)))
16723
16839
  return failure("permission_denied", "bounded action scope changed");
16724
16840
  return success(null, null, { entry, workflow });
16725
16841
  };
@@ -17274,13 +17390,28 @@ function evaluateEvidence(task, candidate) {
17274
17390
  });
17275
17391
  }
17276
17392
  var scopeCovers2 = scopeCovers;
17393
+ var checkPin = (assignment, beforeCandidateId, candidateId) => {
17394
+ const pin = assignment.candidateId ?? null;
17395
+ if (pin === null)
17396
+ return true;
17397
+ return (beforeCandidateId ?? null) === pin || (candidateId ?? null) === pin;
17398
+ };
17399
+ var resolveBinding = (evidence, pin, currentCandidateId) => {
17400
+ const bound = evidence.kind === "check" || evidence.kind === "review";
17401
+ const fallback = bound ? pin ?? currentCandidateId : null;
17402
+ return {
17403
+ beforeCandidateId: evidence.beforeCandidateId ?? fallback,
17404
+ candidateId: evidence.candidateId ?? fallback
17405
+ };
17406
+ };
17407
+ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId === findingCandidateId);
17277
17408
  var applicableDecision2 = (task, workspace, requirement, checkoutRoot) => task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data).filter((decision) => decision.purpose === "limitation" && decision.response === "approved" && decision.revoked === null && decision.binding.taskId === task.id && decision.binding.workspaceId === workspace.id && decision.requirementIds.includes(requirement.id) && decision.binding.scope && scopeCovers2(decision.binding.scope, requirement.scope) && decision.digest === decisionDigest(decision) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, decision.binding).ok : decision.binding.contentRefs.every((reference) => reference.kind !== "file")));
17278
- var applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && data.response === "approved" && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id }));
17279
- var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
17409
+ var applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && (data.response === "approved" || data.response === "stated") && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id }));
17410
+ var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
17280
17411
  if (kind !== "passed")
17281
17412
  return false;
17282
17413
  if (dimension === "testing" || dimension === "verification")
17283
- return evidenceKind === "check";
17414
+ return evidenceKind === "check" || ruleId === "pre-pr-cleanup" && evidenceKind === "artifact";
17284
17415
  if (dimension === "review")
17285
17416
  return evidenceKind === "review";
17286
17417
  if (dimension === "investigation" || dimension === "challenge")
@@ -17289,6 +17420,21 @@ var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
17289
17420
  return evidenceKind === "artifact";
17290
17421
  return false;
17291
17422
  };
17423
+ var expectedKinds = (dimension, ruleId) => {
17424
+ if (dimension === "testing")
17425
+ return "check";
17426
+ if (dimension === "verification")
17427
+ return ruleId === "pre-pr-cleanup" ? "check or artifact" : "check";
17428
+ if (dimension === "review")
17429
+ return "review";
17430
+ if (dimension === "investigation" || dimension === "challenge")
17431
+ return "investigation";
17432
+ if (dimension === "artifacts" || dimension === "continuity")
17433
+ return "artifact";
17434
+ if (dimension === "decisions")
17435
+ return "decision";
17436
+ return "worker report";
17437
+ };
17292
17438
  function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller) {
17293
17439
  const evidence = evaluateEvidence(task, candidate);
17294
17440
  if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
@@ -17302,7 +17448,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
17302
17448
  return (task.policy?.requirements ?? []).map((requirement) => {
17303
17449
  const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
17304
17450
  const passed = related.filter(({ entry, evaluation }) => {
17305
- if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension))
17451
+ if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
17306
17452
  return false;
17307
17453
  if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
17308
17454
  return false;
@@ -17310,11 +17456,12 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
17310
17456
  return true;
17311
17457
  const reviewSession = sessionFromRef(entry.data.reviewContext);
17312
17458
  const sameImplementation = sameSession(reviewSession, task.intent.provenance.session);
17313
- const sameEvidenceSession = task.evidence.some((other) => other.id !== entry.id && other.data.kind === "review" && sameSession(reviewSession, other.provenance.session));
17459
+ const sameEvidenceSession = related.some(({ entry: other }) => other.id !== entry.id && other.data.kind === "review" && other.data.candidateId !== null && other.data.candidateId === entry.data.candidateId && sameSession(reviewSession, other.provenance.session));
17314
17460
  return entry.data.kind === "review" && reviewSession !== null && sameSession(reviewSession, entry.provenance.session) && (requirement.ruleId === "self-review" || !sameImplementation && !sameEvidenceSession);
17315
17461
  });
17316
17462
  if (passed.length) {
17317
- const withRed = requirement.dimension === "testing" ? passed.filter(({ entry }) => related.some((other) => other.entry.data.kind === "check" && other.evaluation.status === "failed" && other.entry.data.requirementIds.includes(requirement.id) && other.entry.recordedAt <= entry.recordedAt)) : passed;
17463
+ const currentId = candidate?.id ?? null;
17464
+ const withRed = requirement.dimension === "testing" ? passed.filter(({ entry }) => related.some((other) => other.entry.data.kind === "check" && other.evaluation.status === "failed" && other.entry.data.requirementIds.includes(requirement.id) && other.entry.recordedAt <= entry.recordedAt) || related.some((baseline) => baseline.entry.id !== entry.id && baseline.entry.data.kind === "check" && baseline.entry.data.result === "passed" && baseline.entry.data.candidateId !== null && currentId !== null && baseline.entry.data.candidateId !== currentId)) : passed;
17318
17465
  if (withRed.length)
17319
17466
  return {
17320
17467
  requirementId: requirement.id,
@@ -17351,7 +17498,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
17351
17498
  status: "satisfied",
17352
17499
  evidenceIds: [],
17353
17500
  decisionIds: decisions.map((decision) => decision.id),
17354
- reason: "an applicable approved decision satisfies the requirement"
17501
+ reason: "an applicable approved or stated decision satisfies the requirement"
17355
17502
  };
17356
17503
  const limitations = requirement.acceptanceAllowed ? applicableDecision2(task, workspace, requirement, checkoutRoot) : [];
17357
17504
  if (limitations.length)
@@ -17363,6 +17510,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
17363
17510
  reason: "an applicable approved limitation permits the missing evidence"
17364
17511
  };
17365
17512
  const unavailable = capabilities.some((capability) => capability.assurance === "unavailable" && (capability.name === requirement.dimension || capability.surface === requirement.dimension));
17513
+ if (!unavailable && related.length > 0 && !passed.length)
17514
+ return {
17515
+ requirementId: requirement.id,
17516
+ status: "unsatisfied",
17517
+ evidenceIds: related.map(({ entry }) => entry.id),
17518
+ decisionIds: [],
17519
+ reason: `no passing evidence (needs kind:${expectedKinds(requirement.dimension, requirement.ruleId)})`
17520
+ };
17366
17521
  return {
17367
17522
  requirementId: requirement.id,
17368
17523
  status: unavailable ? "unavailable" : "unsatisfied",
@@ -17387,7 +17542,13 @@ function evaluateClosure(requestedOutcome, view) {
17387
17542
  if (entry.data.disposition === "fixed") {
17388
17543
  const verified = entry.data.resolution?.evidenceIds.some((id) => {
17389
17544
  const evidence = view.task.evidence.find((item) => item.id === id);
17390
- return evidenceById.get(id) === "passed" && (evidence?.data.kind === "check" || evidence?.data.kind === "review");
17545
+ if (!evidence)
17546
+ return false;
17547
+ return findingVerificationPasses(entry.data.candidateId, {
17548
+ kind: evidence.data.kind,
17549
+ candidateId: evidence.data.candidateId,
17550
+ status: evidenceById.get(id) ?? "missing"
17551
+ });
17391
17552
  });
17392
17553
  if (!verified)
17393
17554
  return failure("requirements_unsatisfied", "fixed findings require current verification");
@@ -17403,7 +17564,12 @@ function evaluateClosure(requestedOutcome, view) {
17403
17564
  }
17404
17565
  }
17405
17566
  }
17406
- const blocking = evaluations.filter((item) => item.status === "unsatisfied" || item.status === "unavailable");
17567
+ const blocking = evaluations.filter((item) => {
17568
+ if (item.status !== "unsatisfied" && item.status !== "unavailable")
17569
+ return false;
17570
+ const requirement = view.task.policy?.requirements.find((candidate) => candidate.id === item.requirementId);
17571
+ return !requirement || requirement.before === "close";
17572
+ });
17407
17573
  if (requestedOutcome !== "stopped" && blocking.length)
17408
17574
  return failure("requirements_unsatisfied", "applicable requirements are unsatisfied", {
17409
17575
  requirementIds: blocking.map((item) => item.requirementId),
@@ -17472,197 +17638,755 @@ var exportDigest = (bundle) => sha256({
17472
17638
  sourceWorkspaceId: bundle.sourceWorkspaceId,
17473
17639
  task: bundle.task
17474
17640
  });
17475
- // packages/workit-core/src/core/external-action.ts
17476
- var externalActionSchema = discriminatedUnion("operation", [
17477
- object({
17478
- operation: literal("git.branch_setup"),
17479
- payload: object({
17480
- action: _enum(["setup", "reapply_stash"]).optional(),
17481
- sdd_dir: string2().optional(),
17482
- target_branch: string2().optional(),
17483
- stash: _enum(["yes", "no"]).optional()
17484
- }).strict()
17485
- }).strict(),
17486
- object({
17487
- operation: literal("git.commit"),
17488
- payload: object({
17489
- message: string2().min(1).optional(),
17490
- plan_steps: array(string2().min(1)).optional(),
17491
- plan_branch: string2().min(1).optional()
17492
- }).strict()
17493
- }).strict(),
17494
- object({
17495
- operation: literal("git.push"),
17496
- payload: object({ branch: string2().min(1).optional() }).strict()
17497
- }).strict(),
17498
- object({
17499
- operation: literal("hosting.pull_request"),
17500
- payload: object({
17501
- title: string2().min(1),
17502
- body: string2().optional(),
17503
- draft: boolean2().optional(),
17504
- target_branch: string2().optional(),
17505
- babysit: boolean2().optional()
17506
- }).strict()
17507
- }).strict(),
17508
- object({
17509
- operation: literal("youtrack.update"),
17510
- payload: object({
17511
- issueId: string2().min(1),
17512
- markdown: string2().min(1),
17513
- minutes: number2().positive().optional()
17514
- }).strict()
17515
- }).strict(),
17516
- object({
17517
- operation: literal("youtrack.time"),
17518
- payload: object({
17519
- issueId: string2().min(1),
17520
- minutes: number2().positive(),
17521
- text: string2().optional(),
17522
- dateMs: number2().finite().optional()
17523
- }).strict()
17524
- }).strict(),
17525
- object({
17526
- operation: literal("youtrack.meeting"),
17527
- payload: object({
17528
- issueId: string2().min(1),
17529
- minutes: number2().positive(),
17530
- text: string2().min(1)
17531
- }).strict()
17532
- }).strict(),
17533
- object({
17534
- operation: literal("changelog.apply"),
17535
- payload: object({
17536
- entries: union([
17537
- record(string2(), array(string2())),
17538
- array(object({ category: string2().min(1), text: string2().min(1) }).strict())
17539
- ]).optional(),
17540
- path: string2().optional(),
17541
- normalize_only: boolean2().optional()
17542
- }).strict()
17543
- }).strict(),
17544
- object({
17545
- operation: literal("context.read"),
17546
- payload: object({
17547
- kind: _enum([
17548
- "git",
17549
- "pr",
17550
- "youtrack",
17551
- "github_issue",
17552
- "gitlab_issue",
17553
- "changelog",
17554
- "release",
17555
- "affected"
17556
- ]),
17557
- range: string2().optional(),
17558
- issueId: string2().optional(),
17559
- issueUrl: string2().optional(),
17560
- issueRef: string2().optional(),
17561
- mode: string2().optional(),
17562
- specPath: string2().optional(),
17563
- planPath: string2().optional()
17564
- }).strict()
17565
- }).strict()
17566
- ]);
17567
- // packages/workit-core/src/core/task-engine.ts
17568
- import { realpathSync as realpathSync4 } from "node:fs";
17569
-
17570
- // packages/workit-core/src/core/policy-resolver.ts
17571
- var resolverInputSchema = object({
17572
- intent: intentSchema,
17573
- assessment: assessmentSchema,
17574
- constraints: array(constraintSchema),
17575
- prior: object({
17576
- decisions: array(decisionSchema),
17577
- findings: array(findingSchema),
17578
- requirements: array(requirementSchema)
17579
- }).strict(),
17580
- preferences: object({ fast: boolean2().optional(), thorough: boolean2().optional() }).strict().optional()
17581
- }).strict();
17582
- var compareCodeUnits3 = (left, right) => {
17583
- const length = Math.min(left.length, right.length);
17584
- for (let index = 0;index < length; index += 1) {
17585
- const difference = left.charCodeAt(index) - right.charCodeAt(index);
17586
- if (difference !== 0)
17587
- return difference;
17641
+ // packages/workit-core/src/core/git.ts
17642
+ import { execFileSync } from "node:child_process";
17643
+ var run = (cwd, args) => {
17644
+ try {
17645
+ const stdout = execFileSync("git", args, {
17646
+ cwd,
17647
+ encoding: "utf8",
17648
+ stdio: ["pipe", "pipe", "pipe"]
17649
+ });
17650
+ return { stdout: stdout.trimEnd(), stderr: "", exitCode: 0 };
17651
+ } catch (error) {
17652
+ const e = error;
17653
+ return {
17654
+ stdout: (e.stdout ?? "").trimEnd(),
17655
+ stderr: (e.stderr ?? "").trimEnd(),
17656
+ exitCode: e.status ?? 1
17657
+ };
17588
17658
  }
17589
- return left.length - right.length;
17590
17659
  };
17591
- var stableUnique = (values) => [...new Set(values)].sort(compareCodeUnits3);
17592
- var stableList = (values) => [...values].sort((left, right) => compareCodeUnits3(canonicalJson(left), canonicalJson(right)));
17593
- var normalizedScope = (scope) => ({
17594
- description: scope.description,
17595
- paths: stableUnique(scope.paths),
17596
- exclusions: stableUnique(scope.exclusions)
17597
- });
17598
- var normalizedRefList = (values) => stableList(values);
17599
- var normalizeAssessment = (assessment) => ({
17600
- facts: stableList(assessment.facts.map((fact) => ({ ...fact, refs: normalizedRefList(fact.refs) }))),
17601
- signals: Object.fromEntries(Object.entries(assessment.signals).map(([name, signal]) => [
17602
- name,
17603
- { ...signal, refs: normalizedRefList(signal.refs) }
17604
- ])),
17605
- consequences: stableList(assessment.consequences.map((consequence) => ({
17606
- ...consequence,
17607
- fact: { ...consequence.fact, refs: normalizedRefList(consequence.fact.refs) }
17608
- }))),
17609
- verification: stableList(assessment.verification.map((verification) => ({
17610
- ...verification,
17611
- scope: normalizedScope(verification.scope),
17612
- availableChecks: normalizedRefList(verification.availableChecks),
17613
- gaps: stableUnique(verification.gaps)
17614
- })))
17615
- });
17616
- var normalizeConstraint = (constraint) => ({
17617
- ...constraint,
17618
- source: constraint.source,
17619
- requires: stableList(constraint.requires.map((requirement) => ({
17620
- ...requirement,
17621
- scope: normalizedScope(requirement.scope),
17622
- refs: normalizedRefList(requirement.refs)
17623
- })))
17624
- });
17625
- var normalizeDecision = (decision) => ({
17626
- ...decision,
17627
- binding: {
17628
- ...decision.binding,
17629
- scope: normalizedScope(decision.binding.scope),
17630
- contentRefs: normalizedRefList(decision.binding.contentRefs)
17631
- },
17632
- requirementIds: stableUnique(decision.requirementIds)
17633
- });
17634
- var normalizeIntent = (intent) => ({
17635
- ...intent,
17636
- scope: normalizedScope(intent.scope),
17637
- authorityRefs: normalizedRefList(intent.authorityRefs)
17638
- });
17639
- var normalizePrior = (prior) => ({
17640
- decisions: stableList(prior.decisions.map(normalizeDecision)),
17641
- findings: stableList(prior.findings.map((finding) => ({
17642
- ...finding,
17643
- scope: normalizedScope(finding.scope),
17644
- refs: normalizedRefList(finding.refs),
17645
- resolution: finding.resolution ? {
17646
- ...finding.resolution,
17647
- evidenceIds: stableUnique(finding.resolution.evidenceIds),
17648
- decisionIds: stableUnique(finding.resolution.decisionIds)
17649
- } : null
17650
- }))),
17651
- requirements: stableList(prior.requirements.map((requirement) => ({
17652
- ...requirement,
17653
- scope: normalizedScope(requirement.scope)
17654
- })))
17655
- });
17656
- var invalidFields = (error) => ({
17657
- fields: error.issues.map((issue) => ({
17658
- path: issue.code === "unrecognized_keys" ? issue.keys.join(".") : issue.path.join("."),
17659
- reason: issue.message
17660
- }))
17661
- });
17662
- function normalizeResolverInput(input) {
17663
- const parsed = resolverInputSchema.safeParse(input);
17664
- if (!parsed.success)
17665
- return failure("invalid_input", "resolver input is invalid", invalidFields(parsed.error));
17660
+ var gitContext = (workspaceRoot, paths = []) => {
17661
+ const cwd = workspaceRoot;
17662
+ let failure;
17663
+ const runHere = (args) => {
17664
+ const result = run(cwd, args);
17665
+ if (!failure && result.exitCode !== 0) {
17666
+ failure = { stderr: result.stderr, exitCode: result.exitCode };
17667
+ }
17668
+ return result.stdout;
17669
+ };
17670
+ const branch = runHere(["rev-parse", "--abbrev-ref", "HEAD"]) || "unknown";
17671
+ const status_short = runHere(["status", "--porcelain"]);
17672
+ const staged = [];
17673
+ const unstaged = [];
17674
+ const untracked = [];
17675
+ for (const line of status_short.split(`
17676
+ `).filter(Boolean)) {
17677
+ const code = line.slice(0, 2);
17678
+ const file = line.slice(3);
17679
+ if (code === "??") {
17680
+ untracked.push(file);
17681
+ } else {
17682
+ if (code[0] !== " " && code[0] !== "?")
17683
+ staged.push(file);
17684
+ if (code[1] !== " " && code[1] !== "?")
17685
+ unstaged.push(file);
17686
+ }
17687
+ }
17688
+ const pathArgs = paths.length ? ["--", ...paths] : [];
17689
+ const diff_stat = runHere(["diff", "--stat", ...pathArgs]);
17690
+ const cached_stat = runHere(["diff", "--cached", "--stat", ...pathArgs]);
17691
+ const stagedSet = new Set(staged);
17692
+ const unstagedSet = new Set(unstaged);
17693
+ const partial_staged = [...stagedSet].filter((f) => unstagedSet.has(f));
17694
+ return {
17695
+ workspace_root: cwd,
17696
+ branch,
17697
+ status_short,
17698
+ staged,
17699
+ unstaged,
17700
+ untracked,
17701
+ diff_stat: [diff_stat, cached_stat].filter(Boolean).join(`
17702
+ `),
17703
+ partial_staged: partial_staged.length > 0,
17704
+ partial_staged_files: partial_staged,
17705
+ ...failure ? { stderr: failure.stderr, exitCode: failure.exitCode } : {}
17706
+ };
17707
+ };
17708
+
17709
+ // packages/workit-core/src/core/config.ts
17710
+ import {
17711
+ copyFileSync,
17712
+ cpSync,
17713
+ existsSync as existsSync4,
17714
+ mkdirSync as mkdirSync2,
17715
+ readFileSync as readFileSync4,
17716
+ readdirSync as readdirSync3,
17717
+ writeFileSync as writeFileSync2
17718
+ } from "node:fs";
17719
+ import os from "node:os";
17720
+ import path7 from "node:path";
17721
+
17722
+ // packages/workit-core/src/core/boundary.ts
17723
+ import { statSync } from "node:fs";
17724
+ var EVENT = {
17725
+ initialization: "initialization",
17726
+ provenance: "provenance",
17727
+ assets: "assets",
17728
+ configurationSource: "configuration_source",
17729
+ hooks: "hooks",
17730
+ mcpConnection: "mcp_connection",
17731
+ migration: "migration",
17732
+ installSteps: "install_steps",
17733
+ uncaughtFailure: "uncaught_failure",
17734
+ toolsFailed: "tools_failed",
17735
+ doctor: "doctor"
17736
+ };
17737
+ var errorDetail = (err) => {
17738
+ if (err instanceof Error) {
17739
+ return { error_name: err.name, error: err.message };
17740
+ }
17741
+ return { error_name: "unknown", error: String(err) };
17742
+ };
17743
+ var markSourcesLoaded = (files, loadedAt = Date.now()) => ({
17744
+ loadedAt,
17745
+ files: [...files]
17746
+ });
17747
+ var changedSourcesSinceLoad = (marker) => {
17748
+ const changed = [];
17749
+ for (const file of marker.files) {
17750
+ try {
17751
+ if (statSync(file).mtimeMs > marker.loadedAt)
17752
+ changed.push(file);
17753
+ } catch {}
17754
+ }
17755
+ return changed;
17756
+ };
17757
+
17758
+ // packages/workit-core/src/core/config.ts
17759
+ var diagnosticLogger;
17760
+ var PRESETS = {
17761
+ gitflow: {
17762
+ allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
17763
+ protected: ["main", "develop", "master", "prod", "production"]
17764
+ },
17765
+ "github-flow": { allowed: ["*"], protected: ["main"] },
17766
+ "trunk-based": { allowed: ["*"], protected: ["main"] },
17767
+ custom: { allowed: [], protected: [] }
17768
+ };
17769
+ var mergePreset = (preset, input = {}, current = {
17770
+ branchPolicy: { preset, allowed: [], protected: [] }
17771
+ }) => {
17772
+ const defs = PRESETS[preset];
17773
+ return {
17774
+ preset,
17775
+ allowed: preset === "custom" ? input.allowed ?? current.branchPolicy.allowed : [...defs.allowed],
17776
+ protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
17777
+ };
17778
+ };
17779
+ var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path7.join(process.env.XDG_CONFIG_HOME || path7.join(os.homedir(), ".config"), "workit");
17780
+ var migratedDir = null;
17781
+ var migrationFailed = false;
17782
+ var ensureConfigDir = (dir = resolveConfigDir()) => {
17783
+ if (migratedDir === dir)
17784
+ return dir;
17785
+ if (process.env.WORKFLOW_TOOLKIT_CONFIG || process.env.WORKFLOW_TOOLKIT_CONFIG_DIR) {
17786
+ migratedDir = dir;
17787
+ return dir;
17788
+ }
17789
+ const legacy = path7.join(process.env.XDG_CONFIG_HOME || path7.join(os.homedir(), ".config"), "workflow-toolkit");
17790
+ if (!existsSync4(legacy)) {
17791
+ migratedDir = dir;
17792
+ return dir;
17793
+ }
17794
+ if (!migrationFailed && existsSync4(dir)) {
17795
+ migratedDir = dir;
17796
+ return dir;
17797
+ }
17798
+ migrationFailed = false;
17799
+ mkdirSync2(dir, { recursive: true });
17800
+ diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
17801
+ for (const entry of readdirSync3(legacy, { withFileTypes: true })) {
17802
+ const src = path7.join(legacy, entry.name);
17803
+ const dest = path7.join(dir, entry.name);
17804
+ if (existsSync4(dest))
17805
+ continue;
17806
+ try {
17807
+ if (entry.isDirectory())
17808
+ cpSync(src, dest, { recursive: true });
17809
+ else if (entry.isFile())
17810
+ copyFileSync(src, dest);
17811
+ } catch (err) {
17812
+ migrationFailed = true;
17813
+ diagnosticLogger?.warn(EVENT.migration, { from: src, ok: false, ...errorDetail(err) });
17814
+ console.warn(`[workit] config migration: a file could not be copied to ${dir}; it will be retried on the next run`);
17815
+ }
17816
+ }
17817
+ if (!migrationFailed)
17818
+ migratedDir = dir;
17819
+ return dir;
17820
+ };
17821
+ var configDir = () => ensureConfigDir();
17822
+ var LOCALE_RE = /^[a-z]{2,3}(-(?:[A-Z]{2}|[0-9]{3}))?$/;
17823
+ var DEFAULTS = {
17824
+ locale: "en",
17825
+ localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
17826
+ timezone: "America/Santiago",
17827
+ branchPolicy: {
17828
+ preset: "gitflow",
17829
+ allowed: [...PRESETS.gitflow.allowed],
17830
+ protected: [...PRESETS.gitflow.protected]
17831
+ },
17832
+ commitPolicy: { preset: "conventional" }
17833
+ };
17834
+ var readSafe = (p) => {
17835
+ try {
17836
+ return readFileSync4(p, "utf8");
17837
+ } catch {
17838
+ return null;
17839
+ }
17840
+ };
17841
+ var isConfigObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
17842
+ var parseConfigResult = (raw, file) => {
17843
+ if (raw === null)
17844
+ return { status: "missing", path: file };
17845
+ let parsed;
17846
+ try {
17847
+ parsed = JSON.parse(raw);
17848
+ } catch {
17849
+ return { status: "malformed", path: file, error: `${file} is not valid JSON` };
17850
+ }
17851
+ if (!isConfigObject(parsed)) {
17852
+ return { status: "malformed", path: file, error: `${file} is not a JSON object` };
17853
+ }
17854
+ const input = parsed;
17855
+ const locale = LOCALE_RE.test(String(input.locale ?? "")) ? input.locale : DEFAULTS.locale;
17856
+ const preset = Object.hasOwn(PRESETS, input.branchPolicy?.preset) ? input.branchPolicy?.preset : "gitflow";
17857
+ const commitPreset = COMMIT_PRESETS.includes(String(input.commitPolicy?.preset ?? "")) ? input.commitPolicy?.preset : "conventional";
17858
+ return {
17859
+ status: "valid",
17860
+ path: file,
17861
+ config: {
17862
+ locale,
17863
+ localeOptions: Array.isArray(input.localeOptions) ? input.localeOptions : DEFAULTS.localeOptions,
17864
+ timezone: input.timezone ?? DEFAULTS.timezone,
17865
+ branchPolicy: mergePreset(preset, {
17866
+ allowed: Array.isArray(input.branchPolicy?.allowed) ? input.branchPolicy.allowed : undefined,
17867
+ protectedNames: Array.isArray(input.branchPolicy?.protected) ? input.branchPolicy.protected : undefined
17868
+ }, DEFAULTS),
17869
+ commitPolicy: {
17870
+ preset: commitPreset,
17871
+ ...typeof input.commitPolicy?.pattern === "string" ? { pattern: input.commitPolicy.pattern } : {}
17872
+ }
17873
+ }
17874
+ };
17875
+ };
17876
+ var readConfigTyped = (dir) => {
17877
+ const file = path7.join(dir ?? configDir(), "config.json");
17878
+ return parseConfigResult(readSafe(file), file);
17879
+ };
17880
+ var readConfig = () => {
17881
+ const result = readConfigTyped();
17882
+ if (result.status === "malformed")
17883
+ throw new Error(result.error);
17884
+ return result.config ?? DEFAULTS;
17885
+ };
17886
+ var resolveBranchPolicy = (config, workspace) => {
17887
+ const wp = workspace?.branchPolicy ?? {};
17888
+ const preset = Object.hasOwn(PRESETS, wp.preset) ? wp.preset : config.branchPolicy?.preset ?? "gitflow";
17889
+ const merged = mergePreset(preset, {
17890
+ allowed: wp.allowed,
17891
+ protectedNames: wp.protected
17892
+ }, config);
17893
+ const allowed = merged.allowed.map((p) => new RegExp(`^${p.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i"));
17894
+ return {
17895
+ preset,
17896
+ allowed,
17897
+ protected: new Set(merged.protected.map((p) => p.toLowerCase())),
17898
+ integration: wp.integration === "merge" ? "merge" : "pr",
17899
+ defaultTargetBranch: preset === "github-flow" ? "main" : preset === "trunk-based" ? "master" : "develop"
17900
+ };
17901
+ };
17902
+ var COMMIT_PRESETS = [
17903
+ "conventional",
17904
+ "gitmoji",
17905
+ "ticket-prefix",
17906
+ "freeform",
17907
+ "custom",
17908
+ "auto"
17909
+ ];
17910
+
17911
+ // packages/workit-core/src/core/workspaces.ts
17912
+ import { readFileSync as readFileSync5, realpathSync as realpathSync4 } from "node:fs";
17913
+ import path8 from "node:path";
17914
+ var readWorkspacesResult = (dir = configDir()) => {
17915
+ const file = path8.join(dir, "workspaces.json");
17916
+ let raw;
17917
+ try {
17918
+ raw = readFileSync5(file, "utf8");
17919
+ } catch {
17920
+ return { status: "missing", path: file, entries: [] };
17921
+ }
17922
+ let parsed;
17923
+ try {
17924
+ parsed = JSON.parse(raw);
17925
+ } catch {
17926
+ return { status: "malformed", path: file, entries: [], error: `${file} is not valid JSON` };
17927
+ }
17928
+ if (!isConfigObject(parsed)) {
17929
+ return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
17930
+ }
17931
+ const list = parsed.workspaces;
17932
+ return {
17933
+ status: "valid",
17934
+ path: file,
17935
+ entries: Array.isArray(list) ? list : []
17936
+ };
17937
+ };
17938
+ var loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries;
17939
+ var globToRegExp = (glob) => {
17940
+ let out = "";
17941
+ for (let i = 0;i < glob.length; i++) {
17942
+ const c = glob[i];
17943
+ if (c === "*") {
17944
+ if (glob[i + 1] === "*") {
17945
+ if (glob[i + 2] === "/") {
17946
+ if (out === "")
17947
+ out += "/?";
17948
+ out += "(?:[^/]+/)*";
17949
+ i += 2;
17950
+ } else {
17951
+ if (i + 2 >= glob.length) {
17952
+ if (out.endsWith("/")) {
17953
+ out = out.slice(0, -1);
17954
+ out += "(?:/.*)?";
17955
+ } else {
17956
+ out += ".*";
17957
+ }
17958
+ } else {
17959
+ out += ".*";
17960
+ }
17961
+ i++;
17962
+ }
17963
+ } else {
17964
+ out += "[^/]*";
17965
+ }
17966
+ } else {
17967
+ out += c.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
17968
+ }
17969
+ }
17970
+ return new RegExp(`^${out}$`);
17971
+ };
17972
+ var matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/"));
17973
+ var realpathOf = (p) => {
17974
+ const real = realpathSync4.native ?? realpathSync4;
17975
+ try {
17976
+ return real(p);
17977
+ } catch {
17978
+ return p;
17979
+ }
17980
+ };
17981
+ var canonicalGlob = (glob) => {
17982
+ const m = /[*?[\]{]/.exec(glob);
17983
+ const prefix = m ? glob.slice(0, m.index) : glob;
17984
+ const rest = m ? glob.slice(m.index) : "";
17985
+ if (!prefix)
17986
+ return glob;
17987
+ const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
17988
+ return real + (prefix.endsWith("/") ? "/" : "") + rest;
17989
+ };
17990
+ var resolveWorkspaceFrom = (cwd, dir) => {
17991
+ const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
17992
+ for (const entry of loadWorkspacesFrom(dir)) {
17993
+ if (!entry || typeof entry !== "object")
17994
+ continue;
17995
+ const ws = entry;
17996
+ if (typeof ws.glob !== "string" || !ws.glob)
17997
+ continue;
17998
+ const glob = ws.glob.replaceAll("\\", "/");
17999
+ const canonical = canonicalGlob(glob);
18000
+ for (const target of targets) {
18001
+ if (matchWorkspace(glob, target))
18002
+ return ws;
18003
+ if (canonical !== glob && matchWorkspace(canonical, target))
18004
+ return ws;
18005
+ }
18006
+ }
18007
+ return null;
18008
+ };
18009
+ var resolveWorkspace = (cwd) => resolveWorkspaceFrom(cwd, configDir());
18010
+
18011
+ // packages/workit-core/src/core/vcs-config.ts
18012
+ import fs7 from "node:fs";
18013
+ import path9 from "node:path";
18014
+ import { spawnSync as spawnSync2 } from "node:child_process";
18015
+ var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
18016
+ var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path9.join(configDir(), "vcs.json");
18017
+ var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
18018
+ var remoteProvider = (cwd) => {
18019
+ const r = spawnSync2("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
18020
+ if (r.status !== 0)
18021
+ return null;
18022
+ const url = (r.stdout ?? "").trim();
18023
+ if (/github\.com[:/]/.test(url))
18024
+ return "github";
18025
+ if (/gitlab\.com[:/]/.test(url))
18026
+ return "gitlab";
18027
+ return null;
18028
+ };
18029
+ var readVcsConfig = () => {
18030
+ const file = vcsConfigPath();
18031
+ let raw;
18032
+ try {
18033
+ raw = fs7.readFileSync(file, "utf8");
18034
+ } catch {
18035
+ return { status: "missing", path: file, config: {} };
18036
+ }
18037
+ let parsed;
18038
+ try {
18039
+ parsed = JSON.parse(raw);
18040
+ } catch {
18041
+ return { status: "malformed", path: file, config: {}, error: `${file} is not valid JSON` };
18042
+ }
18043
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
18044
+ return { status: "valid", path: file, config: parsed };
18045
+ }
18046
+ return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
18047
+ };
18048
+ function vcsConfig(mode, cwd) {
18049
+ const ws = resolveWorkspace(vcsCwd(cwd));
18050
+ const wsVcs = ws?.vcs ?? {};
18051
+ const wsYt = ws?.youtrack ?? {};
18052
+ const wsIssues = ws?.issues ?? {};
18053
+ const { status: cfgStatus, path: cfgPath, config: cfg, error: cfgError } = readVcsConfig();
18054
+ const root = vcsCwd(cwd);
18055
+ const determinateRoot = cwd !== undefined || process.env.WORKFLOW_WORKSPACE_ROOT !== undefined;
18056
+ const rawProvider = wsVcs.provider ?? (determinateRoot ? remoteProvider(root) : null) ?? cfg.provider ?? null;
18057
+ const provider = rawProvider === null || rawProvider === undefined ? null : String(rawProvider).toLowerCase() || null;
18058
+ const wp = ws?.branchPolicy ?? {};
18059
+ const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
18060
+ const policyDefault = resolveBranchPolicyFor(root).defaultTargetBranch;
18061
+ const defaultTarget = String(wsVcs.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : cfg.defaultTargetBranch ?? policyDefault) ?? "develop");
18062
+ const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
18063
+ const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
18064
+ let issuesProvider = null;
18065
+ let linkOnPr = null;
18066
+ if (provider === "github" && typeof wsIssues.provider === "string" && wsIssues.provider.toLowerCase() === "github") {
18067
+ issuesProvider = "github";
18068
+ linkOnPr = typeof wsIssues.link_on_pr === "boolean" ? wsIssues.link_on_pr : null;
18069
+ }
18070
+ if (mode === "resolve") {
18071
+ if (cfgStatus === "malformed")
18072
+ return { ok: false, error: cfgError, configPath: cfgPath };
18073
+ return {
18074
+ ok: true,
18075
+ workspace_name: ws?.name ?? null,
18076
+ provider,
18077
+ defaultTargetBranch: defaultTarget,
18078
+ link_issues: linkIssues,
18079
+ youtrack_base_url: youtrackBaseUrl,
18080
+ issues_provider: issuesProvider,
18081
+ link_on_pr: linkOnPr
18082
+ };
18083
+ }
18084
+ if (cfgStatus === "malformed") {
18085
+ return { ok: false, error: cfgError, configPath: cfgPath };
18086
+ }
18087
+ if (cfgStatus === "missing") {
18088
+ return { ok: false, error: `vcs.json is missing: ${cfgPath}`, configPath: cfgPath };
18089
+ }
18090
+ if (provider === null) {
18091
+ return {
18092
+ ok: false,
18093
+ error: `no vcs provider configured (set vcs.provider in ${cfgPath}, match a workspace entry, or run inside a checkout with a recognized origin remote)`,
18094
+ configPath: cfgPath
18095
+ };
18096
+ }
18097
+ const prov = cfg[provider] ?? {};
18098
+ const wsTokenFile = typeof wsVcs.tokenFile === "string" && wsVcs.tokenFile.trim() !== "" ? wsVcs.tokenFile : null;
18099
+ const tokenFile = String(wsTokenFile ?? prov.tokenFile ?? path9.join(configDir(), `${provider}.token`));
18100
+ const tokenPath = path9.resolve(tokenFile);
18101
+ let tokenOk = false;
18102
+ if (fs7.existsSync(tokenPath)) {
18103
+ const token = fs7.readFileSync(tokenPath, "utf8").trim();
18104
+ const placeholder = !token || token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER);
18105
+ tokenOk = !placeholder;
18106
+ }
18107
+ const out = {
18108
+ ok: true,
18109
+ configPath: path9.resolve(cfgPath),
18110
+ provider,
18111
+ defaultTargetBranch: defaultTarget,
18112
+ pr: cfg.pr ?? {},
18113
+ tokenPath,
18114
+ tokenPresent: fs7.existsSync(tokenPath),
18115
+ tokenReady: tokenOk,
18116
+ workspace_name: ws?.name ?? null,
18117
+ link_issues: linkIssues,
18118
+ youtrack_base_url: youtrackBaseUrl,
18119
+ issues_provider: issuesProvider,
18120
+ link_on_pr: linkOnPr
18121
+ };
18122
+ if (provider === "gitlab") {
18123
+ out.gitlab = {
18124
+ host: prov.host ?? "gitlab.com",
18125
+ apiUrl: prov.apiUrl ?? "https://gitlab.com/api/v4"
18126
+ };
18127
+ } else if (provider === "github") {
18128
+ out.github = { host: prov.host ?? "github.com" };
18129
+ }
18130
+ if (mode === "summary")
18131
+ delete out.tokenPath;
18132
+ return out;
18133
+ }
18134
+ function mergedPrStyle(limit = 6, cwd) {
18135
+ const cfg = vcsConfig("load", cwd);
18136
+ if (!cfg.ok || !cfg.tokenReady)
18137
+ return { ok: false, error: "vcs not configured" };
18138
+ const provider = cfg.provider;
18139
+ const token = fs7.readFileSync(cfg.tokenPath, "utf8").trim();
18140
+ const examples = [];
18141
+ const descInfo = (desc, caseInsensitiveNotes = false) => ({
18142
+ hasNotesSection: caseInsensitiveNotes ? /##\s*notes/i.test(desc) : /##\s*Notes/.test(desc),
18143
+ sections: desc.split(`
18144
+ `).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
18145
+ descriptionPreview: desc.slice(0, 600)
18146
+ });
18147
+ if (provider === "gitlab") {
18148
+ const remote = spawnSync2("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
18149
+ if (remote.status !== 0)
18150
+ return { ok: false, error: "no origin remote" };
18151
+ const url = (remote.stdout ?? "").trim();
18152
+ const m = /gitlab\.com[:/](.+?)(?:\.git)?$/.exec(url);
18153
+ if (!m)
18154
+ return { ok: false, error: "not a gitlab.com origin" };
18155
+ const project = m[1];
18156
+ const env = { ...process.env, GITLAB_TOKEN: token };
18157
+ const run = (args) => spawnSync2("glab", ["api", ...args], { cwd, encoding: "utf8", env });
18158
+ let r = run([
18159
+ `projects/${project.replaceAll("/", "%2F")}/merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`
18160
+ ]);
18161
+ if (r.status !== 0) {
18162
+ r = run([`merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`]);
18163
+ }
18164
+ if (r.status !== 0)
18165
+ return { ok: false, error: "could not list merge requests" };
18166
+ for (const mr of JSON.parse(r.stdout ?? "[]")) {
18167
+ const desc = String(mr.description ?? "").trim();
18168
+ examples.push({
18169
+ title: mr.title,
18170
+ url: mr.web_url,
18171
+ squash: mr.squash,
18172
+ ...descInfo(desc, true)
18173
+ });
18174
+ }
18175
+ } else if (provider === "github") {
18176
+ const r = spawnSync2("gh", ["pr", "list", "--state", "merged", "--limit", String(limit), "--json", "title,url,body"], { cwd, encoding: "utf8", env: { ...process.env, GH_TOKEN: token } });
18177
+ if (r.status !== 0)
18178
+ return { ok: false, error: "could not list pull requests" };
18179
+ for (const pr of JSON.parse(r.stdout ?? "[]")) {
18180
+ const desc = String(pr.body ?? "").trim();
18181
+ examples.push({ title: pr.title, url: pr.url, ...descInfo(desc) });
18182
+ }
18183
+ }
18184
+ return {
18185
+ ok: true,
18186
+ provider,
18187
+ count: examples.length,
18188
+ styleHints: [
18189
+ "Prefer ## Summary bullets + ## Validation or ## Test plan only",
18190
+ "Do not add ## Notes with branch names, commit counts, or diff stats",
18191
+ "Do not paste commit log or diff stat into the body"
18192
+ ],
18193
+ examples
18194
+ };
18195
+ }
18196
+
18197
+ // packages/workit-core/src/core/branch.ts
18198
+ var resolveBranchPolicyFor = (workspaceRoot) => resolveBranchPolicy(readConfig(), resolveWorkspace(workspaceRoot));
18199
+ // packages/workit-core/src/core/external-action.ts
18200
+ var externalActionSchema = discriminatedUnion("operation", [
18201
+ object({
18202
+ operation: literal("git.branch_setup"),
18203
+ payload: object({
18204
+ action: _enum(["setup", "reapply_stash"]).optional(),
18205
+ sdd_dir: string2().optional(),
18206
+ target_branch: string2().optional(),
18207
+ stash: _enum(["yes", "no"]).optional()
18208
+ }).strict()
18209
+ }).strict(),
18210
+ object({
18211
+ operation: literal("git.commit"),
18212
+ payload: object({
18213
+ message: string2().min(1).optional(),
18214
+ plan_steps: array(string2().min(1)).optional(),
18215
+ plan_branch: string2().min(1).optional()
18216
+ }).strict()
18217
+ }).strict(),
18218
+ object({
18219
+ operation: literal("git.push"),
18220
+ payload: object({ branch: string2().min(1).optional() }).strict()
18221
+ }).strict(),
18222
+ object({
18223
+ operation: literal("hosting.pull_request"),
18224
+ payload: object({
18225
+ title: string2().min(1),
18226
+ body: string2().optional(),
18227
+ draft: boolean2().optional(),
18228
+ target_branch: string2().optional(),
18229
+ babysit: boolean2().optional()
18230
+ }).strict()
18231
+ }).strict(),
18232
+ object({
18233
+ operation: literal("youtrack.update"),
18234
+ payload: object({
18235
+ issueId: string2().min(1),
18236
+ markdown: string2().min(1),
18237
+ minutes: number2().positive().optional()
18238
+ }).strict()
18239
+ }).strict(),
18240
+ object({
18241
+ operation: literal("youtrack.time"),
18242
+ payload: object({
18243
+ issueId: string2().min(1),
18244
+ minutes: number2().positive(),
18245
+ text: string2().optional(),
18246
+ dateMs: number2().finite().optional()
18247
+ }).strict()
18248
+ }).strict(),
18249
+ object({
18250
+ operation: literal("youtrack.meeting"),
18251
+ payload: object({
18252
+ issueId: string2().min(1),
18253
+ minutes: number2().positive(),
18254
+ text: string2().min(1)
18255
+ }).strict()
18256
+ }).strict(),
18257
+ object({
18258
+ operation: literal("changelog.apply"),
18259
+ payload: object({
18260
+ entries: union([
18261
+ record(string2(), array(string2())),
18262
+ array(object({ category: string2().min(1), text: string2().min(1) }).strict())
18263
+ ]).optional(),
18264
+ path: string2().optional(),
18265
+ normalize_only: boolean2().optional()
18266
+ }).strict()
18267
+ }).strict(),
18268
+ object({
18269
+ operation: literal("context.read"),
18270
+ payload: object({
18271
+ kind: _enum([
18272
+ "git",
18273
+ "pr",
18274
+ "youtrack",
18275
+ "github_issue",
18276
+ "gitlab_issue",
18277
+ "changelog",
18278
+ "release",
18279
+ "affected"
18280
+ ]),
18281
+ range: string2().optional(),
18282
+ issueId: string2().optional(),
18283
+ issueUrl: string2().optional(),
18284
+ issueRef: string2().optional(),
18285
+ mode: string2().optional(),
18286
+ specPath: string2().optional(),
18287
+ planPath: string2().optional()
18288
+ }).strict()
18289
+ }).strict()
18290
+ ]);
18291
+ // packages/workit-core/src/core/task-engine.ts
18292
+ import { realpathSync as realpathSync5 } from "node:fs";
18293
+
18294
+ // packages/workit-core/src/core/policy-resolver.ts
18295
+ var resolverInputSchema = object({
18296
+ intent: intentSchema,
18297
+ assessment: assessmentSchema,
18298
+ constraints: array(constraintSchema),
18299
+ prior: object({
18300
+ decisions: array(decisionSchema),
18301
+ findings: array(findingSchema),
18302
+ requirements: array(requirementSchema)
18303
+ }).strict(),
18304
+ preferences: object({ fast: boolean2().optional(), thorough: boolean2().optional() }).strict().optional()
18305
+ }).strict();
18306
+ var compareCodeUnits3 = (left, right) => {
18307
+ const length = Math.min(left.length, right.length);
18308
+ for (let index = 0;index < length; index += 1) {
18309
+ const difference = left.charCodeAt(index) - right.charCodeAt(index);
18310
+ if (difference !== 0)
18311
+ return difference;
18312
+ }
18313
+ return left.length - right.length;
18314
+ };
18315
+ var stableUnique = (values) => [...new Set(values)].sort(compareCodeUnits3);
18316
+ var stableList = (values) => [...values].sort((left, right) => compareCodeUnits3(canonicalJson(left), canonicalJson(right)));
18317
+ var normalizedScope = (scope) => ({
18318
+ description: scope.description,
18319
+ paths: stableUnique(scope.paths),
18320
+ exclusions: stableUnique(scope.exclusions)
18321
+ });
18322
+ var normalizedRefList = (values) => stableList(values);
18323
+ var normalizeAssessment = (assessment) => ({
18324
+ facts: stableList(assessment.facts.map((fact) => ({ ...fact, refs: normalizedRefList(fact.refs) }))),
18325
+ signals: Object.fromEntries(Object.entries(assessment.signals).map(([name, signal]) => [
18326
+ name,
18327
+ { ...signal, refs: normalizedRefList(signal.refs) }
18328
+ ])),
18329
+ consequences: stableList(assessment.consequences.map((consequence) => ({
18330
+ ...consequence,
18331
+ fact: { ...consequence.fact, refs: normalizedRefList(consequence.fact.refs) }
18332
+ }))),
18333
+ verification: stableList(assessment.verification.map((verification) => ({
18334
+ ...verification,
18335
+ scope: normalizedScope(verification.scope),
18336
+ availableChecks: normalizedRefList(verification.availableChecks),
18337
+ gaps: stableUnique(verification.gaps)
18338
+ })))
18339
+ });
18340
+ var normalizeConstraint = (constraint) => ({
18341
+ ...constraint,
18342
+ source: constraint.source,
18343
+ requires: stableList(constraint.requires.map((requirement) => ({
18344
+ ...requirement,
18345
+ scope: normalizedScope(requirement.scope),
18346
+ refs: normalizedRefList(requirement.refs)
18347
+ })))
18348
+ });
18349
+ var normalizeDecision = (decision) => ({
18350
+ ...decision,
18351
+ binding: {
18352
+ ...decision.binding,
18353
+ scope: normalizedScope(decision.binding.scope),
18354
+ contentRefs: normalizedRefList(decision.binding.contentRefs)
18355
+ },
18356
+ requirementIds: stableUnique(decision.requirementIds)
18357
+ });
18358
+ var normalizeIntent = (intent) => ({
18359
+ ...intent,
18360
+ scope: normalizedScope(intent.scope),
18361
+ authorityRefs: normalizedRefList(intent.authorityRefs)
18362
+ });
18363
+ var normalizePrior = (prior) => ({
18364
+ decisions: stableList(prior.decisions.map(normalizeDecision)),
18365
+ findings: stableList(prior.findings.map((finding) => ({
18366
+ ...finding,
18367
+ scope: normalizedScope(finding.scope),
18368
+ refs: normalizedRefList(finding.refs),
18369
+ resolution: finding.resolution ? {
18370
+ ...finding.resolution,
18371
+ evidenceIds: stableUnique(finding.resolution.evidenceIds),
18372
+ decisionIds: stableUnique(finding.resolution.decisionIds)
18373
+ } : null
18374
+ }))),
18375
+ requirements: stableList(prior.requirements.map((requirement) => ({
18376
+ ...requirement,
18377
+ scope: normalizedScope(requirement.scope)
18378
+ })))
18379
+ });
18380
+ var invalidFields = (error) => ({
18381
+ fields: error.issues.map((issue) => ({
18382
+ path: issue.code === "unrecognized_keys" ? issue.keys.join(".") : issue.path.join("."),
18383
+ reason: issue.message
18384
+ }))
18385
+ });
18386
+ function normalizeResolverInput(input) {
18387
+ const parsed = resolverInputSchema.safeParse(input);
18388
+ if (!parsed.success)
18389
+ return failure("invalid_input", "resolver input is invalid", invalidFields(parsed.error));
17666
18390
  const value = parsed.data;
17667
18391
  const signals = value.assessment.signals;
17668
18392
  if (signals.behaviorChange.value === true && signals.mechanicalLowRisk.value === true)
@@ -18009,6 +18733,8 @@ var applyWorkerLifecycle = (input) => {
18009
18733
  if (task.data.status === "paused" && (input.state === "running" || input.state === "cancelling"))
18010
18734
  return failure("invalid_transition", "paused task cannot run a worker");
18011
18735
  const notStarted = input.dispatch === true;
18736
+ if (notStarted && entry.data.state === "stopped" && input.session === null)
18737
+ return success(task.data.revision, workspace.data.revision, entry);
18012
18738
  if (notStarted && (input.state !== "stopped" || input.session !== null))
18013
18739
  return failure("invalid_input", "a never-dispatched worker stops with no session");
18014
18740
  if (notStarted && entry.data.session !== null)
@@ -18040,7 +18766,7 @@ var applyWorkerLifecycle = (input) => {
18040
18766
  return success(task.data.revision, workspace.data.revision, entry);
18041
18767
  const changed = input.store.mutateTaskAndWorkspace({
18042
18768
  taskId: task.data.id,
18043
- expectedTaskRevision: input.expectedRevision,
18769
+ expectedRevision: input.expectedRevision,
18044
18770
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
18045
18771
  now: input.now,
18046
18772
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
@@ -18088,90 +18814,22 @@ var importedProvenance = (context) => ({
18088
18814
  receipts: []
18089
18815
  });
18090
18816
  var portableTask = (task) => {
18091
- const clone = structuredClone(task);
18092
- const portableProvenance = (value) => ({
18093
- ...value,
18094
- session: null,
18095
- receipts: []
18096
- });
18097
- const entries = [
18098
- clone.intent,
18099
- ...clone.assessments,
18100
- ...clone.evidence,
18101
- ...clone.decisions,
18102
- ...clone.findings,
18103
- ...clone.workers
18104
- ];
18105
- for (const entry of entries)
18106
- entry.provenance = portableProvenance(entry.provenance);
18107
- for (const worker of clone.workers)
18108
- worker.data.session = null;
18109
- clone.intent = {
18110
- ...clone.intent,
18111
- data: { ...clone.intent.data, authorityRefs: portableRefs(clone.intent.data.authorityRefs) }
18112
- };
18113
- clone.constraints = clone.constraints.flatMap((constraint) => {
18114
- const source = portableRef(constraint.source);
18115
- return source ? [
18116
- {
18117
- ...constraint,
18118
- source,
18119
- requires: constraint.requires.map((requirement) => ({
18120
- ...requirement,
18121
- refs: portableRefs(requirement.refs)
18122
- }))
18123
- }
18124
- ] : [];
18125
- });
18126
- clone.assessments = clone.assessments.map((entry) => ({
18127
- ...entry,
18128
- data: {
18129
- ...entry.data,
18130
- facts: entry.data.facts.map(portableFact),
18131
- signals: Object.fromEntries(Object.entries(entry.data.signals).map(([name, signal]) => [name, portableSignal(signal)])),
18132
- consequences: entry.data.consequences.map((consequence) => ({
18133
- ...consequence,
18134
- fact: portableFact(consequence.fact)
18135
- })),
18136
- verification: entry.data.verification.map((verification) => ({
18137
- ...verification,
18138
- availableChecks: portableRefs(verification.availableChecks)
18139
- }))
18140
- }
18141
- }));
18142
- clone.progress = {
18143
- ...clone.progress,
18144
- blockers: clone.progress.blockers.map((blocker) => ({
18145
- ...blocker,
18146
- refs: portableRefs(blocker.refs)
18147
- }))
18148
- };
18149
- clone.evidence = clone.evidence.map((entry) => ({
18150
- ...entry,
18151
- data: {
18152
- ...entry.data,
18153
- refs: portableRefs(entry.data.refs),
18154
- reviewContext: portableRef(entry.data.reviewContext)
18155
- }
18156
- }));
18157
- clone.decisions = clone.decisions.map((entry) => ({
18158
- ...entry,
18159
- data: {
18160
- ...entry.data,
18161
- consumption: entry.data.consumption ? portableRef(entry.data.consumption.actionRef) ? {
18162
- ...entry.data.consumption,
18163
- actionRef: portableRef(entry.data.consumption.actionRef)
18164
- } : null : null,
18165
- binding: {
18166
- ...entry.data.binding,
18167
- contentRefs: portableRefs(entry.data.binding.contentRefs)
18168
- }
18169
- }
18170
- }));
18171
- clone.findings = clone.findings.map((entry) => ({
18172
- ...entry,
18173
- data: { ...entry.data, refs: portableRefs(entry.data.refs) }
18174
- }));
18817
+ const stripped = rewriteRecordRefs(task, taskRecordSchema, portableRef);
18818
+ if (typeof stripped !== "object" || stripped === null || Array.isArray(stripped))
18819
+ throw new TypeError("portable task rewrite produced a non-record");
18820
+ const clone = structuredClone(stripped);
18821
+ const demote = (facts, signals, consequences) => {
18822
+ for (const fact of facts)
18823
+ Object.assign(fact, portableFact(fact));
18824
+ for (const name of Object.keys(signals)) {
18825
+ const signal = signals[name];
18826
+ Object.assign(signal, portableSignal(signal));
18827
+ }
18828
+ for (const consequence of consequences)
18829
+ Object.assign(consequence.fact, portableFact(consequence.fact));
18830
+ };
18831
+ for (const entry of clone.assessments)
18832
+ demote(entry.data.facts, entry.data.signals, entry.data.consequences);
18175
18833
  clone.candidates = [];
18176
18834
  return clone;
18177
18835
  };
@@ -18196,15 +18854,13 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
18196
18854
  allocate(entry.id);
18197
18855
  const fresh = (id) => allocate(id);
18198
18856
  const provenance = importedProvenance(context);
18857
+ const remap = (ref) => mapRef(ref, ids);
18199
18858
  const intent = {
18200
18859
  ...source.intent,
18201
18860
  id: fresh(source.intent.id),
18202
18861
  recordedAt: timestamp,
18203
18862
  provenance,
18204
- data: {
18205
- ...source.intent.data,
18206
- authorityRefs: source.intent.data.authorityRefs.map((ref) => mapRef(ref, ids))
18207
- }
18863
+ data: rewriteRecordRefs(source.intent.data, intentSchema, remap)
18208
18864
  };
18209
18865
  const assessments = source.assessments.map((entry) => {
18210
18866
  const data = entry.data;
@@ -18213,62 +18869,39 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
18213
18869
  id: fresh(entry.id),
18214
18870
  recordedAt: timestamp,
18215
18871
  provenance,
18216
- data: {
18217
- ...data,
18218
- facts: data.facts.map((fact) => ({
18219
- ...fact,
18220
- refs: fact.refs.map((ref) => mapRef(ref, ids))
18221
- })),
18222
- signals: Object.fromEntries(Object.entries(data.signals).map(([name, signal]) => [
18223
- name,
18224
- { ...signal, refs: signal.refs.map((ref) => mapRef(ref, ids)) }
18225
- ])),
18226
- consequences: data.consequences.map((consequence) => ({
18227
- ...consequence,
18228
- fact: {
18229
- ...consequence.fact,
18230
- refs: consequence.fact.refs.map((ref) => mapRef(ref, ids))
18231
- }
18232
- })),
18233
- verification: data.verification.map((verification) => ({
18234
- ...verification,
18235
- availableChecks: verification.availableChecks.map((ref) => mapRef(ref, ids))
18236
- }))
18237
- }
18872
+ data: rewriteRecordRefs(data, assessmentSchema, remap)
18238
18873
  };
18239
18874
  });
18240
18875
  const evidence = source.evidence.map((entry) => ({
18241
18876
  ...entry,
18242
18877
  id: fresh(entry.id),
18243
18878
  recordedAt: timestamp,
18244
- provenance,
18245
- data: {
18246
- ...entry.data,
18247
- refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
18248
- reviewContext: entry.data.reviewContext ? mapRef(entry.data.reviewContext, ids) : null
18249
- }
18250
- }));
18251
- const decisions = source.decisions.map((entry) => ({
18252
- ...entry,
18253
- id: fresh(entry.id),
18254
- recordedAt: timestamp,
18255
- provenance
18879
+ provenance,
18880
+ data: rewriteRecordRefs(entry.data, evidenceSchema, remap)
18256
18881
  }));
18257
- const findings = source.findings.map((entry) => ({
18882
+ const decisions = source.decisions.map((entry) => ({
18258
18883
  ...entry,
18259
18884
  id: fresh(entry.id),
18260
18885
  recordedAt: timestamp,
18261
- provenance,
18262
- data: {
18263
- ...entry.data,
18264
- refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
18265
- resolution: entry.data.resolution ? {
18266
- ...entry.data.resolution,
18267
- evidenceIds: entry.data.resolution.evidenceIds.map((id) => ids.get(id) ?? id),
18268
- decisionIds: entry.data.resolution.decisionIds.map((id) => ids.get(id) ?? id)
18269
- } : null
18270
- }
18886
+ provenance
18271
18887
  }));
18888
+ const findings = source.findings.map((entry) => {
18889
+ const data = rewriteRecordRefs(entry.data, findingSchema, remap);
18890
+ return {
18891
+ ...entry,
18892
+ id: fresh(entry.id),
18893
+ recordedAt: timestamp,
18894
+ provenance,
18895
+ data: {
18896
+ ...data,
18897
+ resolution: data.resolution ? {
18898
+ ...data.resolution,
18899
+ evidenceIds: data.resolution.evidenceIds.map((id) => ids.get(id) ?? id),
18900
+ decisionIds: data.resolution.decisionIds.map((id) => ids.get(id) ?? id)
18901
+ } : null
18902
+ }
18903
+ };
18904
+ });
18272
18905
  const workers = source.workers.map((entry) => ({
18273
18906
  ...entry,
18274
18907
  id: fresh(entry.id),
@@ -18311,19 +18944,21 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
18311
18944
  closure: null,
18312
18945
  assessments,
18313
18946
  evidence,
18314
- decisions: decisions.map((entry) => ({
18315
- ...entry,
18316
- data: {
18317
- ...entry.data,
18318
- binding: {
18319
- ...entry.data.binding,
18320
- taskId: "",
18321
- workspaceId: destinationWorkspaceId,
18322
- contentRefs: entry.data.binding.contentRefs.map((ref) => mapRef(ref, ids))
18323
- },
18324
- consumption: null
18325
- }
18326
- })),
18947
+ decisions: decisions.map((entry) => {
18948
+ const data = rewriteRecordRefs(entry.data, decisionSchema, remap);
18949
+ return {
18950
+ ...entry,
18951
+ data: {
18952
+ ...data,
18953
+ binding: {
18954
+ ...data.binding,
18955
+ taskId: "",
18956
+ workspaceId: destinationWorkspaceId
18957
+ },
18958
+ consumption: null
18959
+ }
18960
+ };
18961
+ }),
18327
18962
  ...actionProgress ? { actionProgress } : {},
18328
18963
  findings,
18329
18964
  workers,
@@ -18361,7 +18996,7 @@ class WorkitCore {
18361
18996
  }
18362
18997
  contextRootError() {
18363
18998
  try {
18364
- if (realpathSync4(this.context.root) !== this.store.root)
18999
+ if (realpathSync5(this.context.root) !== this.store.root)
18365
19000
  return failure("invalid_input", "operation context root does not match the task store root");
18366
19001
  } catch {
18367
19002
  return failure("invalid_input", "operation context root cannot be resolved");
@@ -18406,7 +19041,7 @@ class WorkitCore {
18406
19041
  activeWorkerBlocker(task, workspace) {
18407
19042
  if (workspace.writer)
18408
19043
  return failure("recovery_required", "writer ownership must be released first");
18409
- if (task.workers.some((entry) => isUncertainWorker(entry.data.state)))
19044
+ if (task.workers.some((entry) => workerBlocksTransition("close", entry.data.state)))
18410
19045
  return failure("recovery_required", "worker state requires reconciliation");
18411
19046
  return null;
18412
19047
  }
@@ -18652,37 +19287,33 @@ class WorkitCore {
18652
19287
  const currentCandidate = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
18653
19288
  if (!currentCandidate.ok)
18654
19289
  return currentCandidate;
18655
- if (evidence.kind === "check" || evidence.kind === "review") {
18656
- evidence.beforeCandidateId ??= currentCandidate.data.id;
18657
- evidence.candidateId ??= currentCandidate.data.id;
18658
- } else {
18659
- evidence.beforeCandidateId ??= null;
18660
- evidence.candidateId ??= null;
18661
- }
19290
+ const pin = helper && helper.ok ? helper.data.data.assignment.candidateId : null;
19291
+ const bound = resolveBinding(evidence, pin, currentCandidate.data.id);
18662
19292
  if (helper?.ok) {
18663
19293
  const assignment = helper.data.data.assignment;
18664
- if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || assignment.candidateId !== null && evidence.candidateId !== assignment.candidateId && evidence.beforeCandidateId !== assignment.candidateId || !refsWithinScope(evidence.refs, assignment.scope) || !refsWithinScope(evidence.refs, task.data.intent.data.scope))
19294
+ if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || !checkPin(assignment, bound.beforeCandidateId, bound.candidateId) || !refsWithinScope(evidence.refs, assignment.scope) || !refsWithinScope(evidence.refs, task.data.intent.data.scope))
18665
19295
  return failure("permission_denied", "evidence is outside the worker assignment");
18666
19296
  }
18667
19297
  const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
18668
- for (const candidateId of [evidence.beforeCandidateId, evidence.candidateId]) {
19298
+ for (const candidateId of [bound.beforeCandidateId, bound.candidateId]) {
18669
19299
  if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
18670
19300
  return failure("invalid_input", "evidence candidate is not a captured candidate");
18671
19301
  }
19302
+ const boundEvidence = { ...evidence, ...bound };
18672
19303
  const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
18673
19304
  const candidates = current.candidates.some((item) => item.id === currentCandidate.data.id) ? current.candidates : [...current.candidates, currentCandidate.data];
18674
19305
  const entry = {
18675
19306
  id: newId(),
18676
19307
  recordedAt: mutation.now,
18677
19308
  provenance: provenance(this.context, "agent_reported"),
18678
- data: evidence
19309
+ data: boundEvidence
18679
19310
  };
18680
19311
  return success(mutation.revision, null, {
18681
19312
  ...current,
18682
19313
  candidates,
18683
19314
  evidence: [...current.evidence, entry],
18684
19315
  findings: current.findings.map((finding) => {
18685
- if (finding.data.disposition === "open" || evidence.result === "skipped")
19316
+ if (finding.data.disposition === "open" || boundEvidence.result === "skipped")
18686
19317
  return finding;
18687
19318
  if (finding.data.disposition !== "fixed")
18688
19319
  return finding;
@@ -18691,8 +19322,12 @@ class WorkitCore {
18691
19322
  item.evidenceId,
18692
19323
  item.status
18693
19324
  ]));
18694
- const verified = withEntry.evidence.some((item) => statuses.get(item.id) === "passed" && (item.data.kind === "check" || item.data.kind === "review") && (finding.data.candidateId === null || item.data.candidateId === finding.data.candidateId));
18695
- const contradicted = evidence.result === "failed" && finding.data.refs.some((left) => evidence.refs.some((right) => JSON.stringify(left) === JSON.stringify(right)));
19325
+ const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
19326
+ kind: item.data.kind,
19327
+ candidateId: item.data.candidateId,
19328
+ status: statuses.get(item.id) ?? "missing"
19329
+ }));
19330
+ const contradicted = boundEvidence.result === "failed" && finding.data.refs.some((left) => boundEvidence.refs.some((right) => sameValue(left, right)));
18696
19331
  return verified && !contradicted ? finding : { ...finding, data: { ...finding.data, disposition: "open", resolution: null } };
18697
19332
  })
18698
19333
  });
@@ -18717,7 +19352,12 @@ class WorkitCore {
18717
19352
  const input = parsed.data;
18718
19353
  if (nativeRequired && input.action !== "record")
18719
19354
  return failure("permission_denied", "native observation is only valid for recording decisions");
18720
- if (nativeRequired && nativeObservation === undefined)
19355
+ const stated = input.action === "record" && input.response === "stated";
19356
+ if (stated && input.purpose === "action")
19357
+ return failure("permission_denied", "stated choices cannot authorize mutating actions");
19358
+ if (stated && !input.binding?.statedChoice)
19359
+ return failure("invalid_input", "stated choices require binding.statedChoice");
19360
+ if (nativeRequired && nativeObservation === undefined && !stated)
18721
19361
  return failure("permission_denied", "native decision observation is required");
18722
19362
  const task = this.store.readTask(input.taskId);
18723
19363
  if (!task.ok)
@@ -18750,7 +19390,7 @@ class WorkitCore {
18750
19390
  consumption: null
18751
19391
  };
18752
19392
  const data = { ...base, digest: decisionDigest(base) };
18753
- const native = nativeRequired ? verifyNativeDecision(this.context.nativeAuthority, {
19393
+ const native = nativeRequired && !stated ? verifyNativeDecision(this.context.nativeAuthority, {
18754
19394
  observation: nativeObservation,
18755
19395
  expected: {
18756
19396
  taskId: task.data.id,
@@ -18845,7 +19485,7 @@ class WorkitCore {
18845
19485
  if (input.action === "record") {
18846
19486
  if (helper?.ok) {
18847
19487
  const assignment = helper.data.data.assignment;
18848
- if (!bindingCovers(assignment.scope, input.scope) || !bindingCovers(task.data.intent.data.scope, input.scope) || assignment.candidateId !== null && input.candidateId !== assignment.candidateId || !refsWithinScope(input.refs, assignment.scope) || !refsWithinScope(input.refs, task.data.intent.data.scope))
19488
+ if (!bindingCovers(assignment.scope, input.scope) || !bindingCovers(task.data.intent.data.scope, input.scope) || !checkPin(assignment, null, input.candidateId ?? null) || !refsWithinScope(input.refs, assignment.scope) || !refsWithinScope(input.refs, task.data.intent.data.scope))
18849
19489
  return failure("permission_denied", "finding is outside the worker assignment");
18850
19490
  }
18851
19491
  if (input.candidateId && !task.data.candidates.some((candidate) => candidate.id === input.candidateId))
@@ -18892,7 +19532,11 @@ class WorkitCore {
18892
19532
  const evaluations = evaluateEvidence(task.data, current.data);
18893
19533
  const verified = evidence.some((entry) => {
18894
19534
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
18895
- return evaluation?.status === "passed" && (entry.data.kind === "check" || entry.data.kind === "review") && (finding.data.candidateId === null || entry.data.candidateId === finding.data.candidateId);
19535
+ return findingVerificationPasses(finding.data.candidateId, {
19536
+ kind: entry.data.kind,
19537
+ candidateId: entry.data.candidateId,
19538
+ status: evaluation?.status ?? "missing"
19539
+ });
18896
19540
  });
18897
19541
  if (!verified)
18898
19542
  return failure("permission_denied", "fixed findings require passing verification evidence");
@@ -18904,7 +19548,7 @@ class WorkitCore {
18904
19548
  if (entry.data.result === "missing" || entry.data.result === "skipped")
18905
19549
  return false;
18906
19550
  const candidateMatches = finding.data.candidateId === null ? entry.data.candidateId === null : entry.data.candidateId === finding.data.candidateId || entry.data.beforeCandidateId === finding.data.candidateId;
18907
- const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) => JSON.stringify(left) === JSON.stringify(right)));
19551
+ const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) => sameValue(left, right)));
18908
19552
  return candidateMatches && (referenceMatches || entry.data.claim === finding.data.claim);
18909
19553
  });
18910
19554
  if (!relevant)
@@ -18982,7 +19626,7 @@ class WorkitCore {
18982
19626
  const workerId = newId();
18983
19627
  const changed = this.store.mutateTaskAndWorkspace({
18984
19628
  taskId: task.data.id,
18985
- expectedTaskRevision: input.expectedRevision,
19629
+ expectedRevision: input.expectedRevision,
18986
19630
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
18987
19631
  now: trustedNow(this.context),
18988
19632
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -19020,7 +19664,7 @@ class WorkitCore {
19020
19664
  return failure("permission_denied", "worker report is outside the worker assignment");
19021
19665
  const changed = this.store.mutateTaskAndWorkspace({
19022
19666
  taskId: task.data.id,
19023
- expectedTaskRevision: input.expectedRevision,
19667
+ expectedRevision: input.expectedRevision,
19024
19668
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19025
19669
  now: trustedNow(this.context),
19026
19670
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -19044,24 +19688,25 @@ class WorkitCore {
19044
19688
  return failure("invalid_transition", "closed task cannot cancel workers");
19045
19689
  if (entry.data.state === "unknown")
19046
19690
  return failure("recovery_required", "worker state requires recovery");
19047
- if (entry.data.state === "stopped")
19048
- return failure("invalid_transition", "stopped worker cannot be cancelled");
19049
- const settles = entry.data.report !== null || entry.data.state === "cancelling";
19691
+ if (entry.data.state === "stopped") {
19692
+ const stopped = task.data.workers.find((candidate) => candidate.id === input.workerId);
19693
+ return success(task.data.revision, workspace.data.revision, stopped);
19694
+ }
19050
19695
  const changed = this.store.mutateTaskAndWorkspace({
19051
19696
  taskId: task.data.id,
19052
- expectedTaskRevision: input.expectedRevision,
19697
+ expectedRevision: input.expectedRevision,
19053
19698
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19054
19699
  now: trustedNow(this.context),
19055
- workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
19700
+ workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
19701
+ ...current,
19702
+ writer: current.writer?.owner.workerId === input.workerId ? null : current.writer
19703
+ }),
19056
19704
  task: (current, mutation) => success(mutation.revision, null, {
19057
19705
  ...current,
19058
19706
  workers: current.workers.map((candidate) => candidate.id === input.workerId ? {
19059
19707
  ...candidate,
19060
19708
  recordedAt: mutation.now,
19061
- data: {
19062
- ...candidate.data,
19063
- state: settles ? "stopped" : "cancelling"
19064
- }
19709
+ data: { ...candidate.data, state: "stopped" }
19065
19710
  } : candidate)
19066
19711
  })
19067
19712
  });
@@ -19158,7 +19803,7 @@ class WorkitCore {
19158
19803
  return attested;
19159
19804
  const changed = this.store.mutateTaskAndWorkspace({
19160
19805
  taskId: task.data.id,
19161
- expectedTaskRevision: input.expectedRevision,
19806
+ expectedRevision: input.expectedRevision,
19162
19807
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19163
19808
  now: trustedNow(this.context),
19164
19809
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -19324,6 +19969,12 @@ class WorkitCore {
19324
19969
  if (!sameSession2(helper.data.data.session, this.context))
19325
19970
  return failure("permission_denied", "worker session does not match the caller");
19326
19971
  }
19972
+ const gate = this.view(task.data);
19973
+ if (gate.ok) {
19974
+ const writeGates = (task.data.policy?.requirements ?? []).filter((requirement) => requirement.before === "write" && gate.data.requirements.some((evaluation) => evaluation.requirementId === requirement.id && (evaluation.status === "unsatisfied" || evaluation.status === "unavailable")));
19975
+ if (writeGates.length)
19976
+ return failure("requirements_unsatisfied", "writer ownership is gated by unsatisfied requirements; record their evidence or an approved limitation waiver first", { requirementIds: writeGates.map((requirement) => requirement.id) });
19977
+ }
19327
19978
  const owner = {
19328
19979
  taskId: task.data.id,
19329
19980
  workerId: helperId,
@@ -19335,7 +19986,7 @@ class WorkitCore {
19335
19986
  };
19336
19987
  const changed = this.store.mutateTaskAndWorkspace({
19337
19988
  taskId: task.data.id,
19338
- expectedTaskRevision: input.expectedRevision,
19989
+ expectedRevision: input.expectedRevision,
19339
19990
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19340
19991
  now: trustedNow(this.context),
19341
19992
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
@@ -19366,7 +20017,7 @@ class WorkitCore {
19366
20017
  }
19367
20018
  const changed = this.store.mutateTaskAndWorkspace({
19368
20019
  taskId: task.data.id,
19369
- expectedTaskRevision: input.expectedRevision,
20020
+ expectedRevision: input.expectedRevision,
19370
20021
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19371
20022
  now: trustedNow(this.context),
19372
20023
  workspace: (currentWorkspace, mutation) => success(mutation.revision, mutation.revision, { ...currentWorkspace, writer: null }),
@@ -19598,7 +20249,7 @@ class WorkitCore {
19598
20249
  if (view.task.workspaceId !== view.workspace.id)
19599
20250
  return failure("invalid_input", "task and workspace bindings are invalid");
19600
20251
  try {
19601
- if (realpathSync4(view.workspace.root) !== this.store.root)
20252
+ if (realpathSync5(view.workspace.root) !== this.store.root)
19602
20253
  return failure("invalid_input", "task view root does not match the task store");
19603
20254
  } catch {
19604
20255
  return failure("invalid_input", "task view root cannot be resolved");
@@ -19615,7 +20266,15 @@ class WorkitCore {
19615
20266
  if (seen.has(observation.workerId))
19616
20267
  return failure("invalid_input", "worker observations must be unique");
19617
20268
  seen.add(observation.workerId);
19618
- if (observation.taskId !== view.task.id || observation.expectedRevision !== view.task.revision || observation.expectedWorkspaceRevision !== view.workspace.revision)
20269
+ const freshTask = this.store.readTask(view.task.id);
20270
+ const freshWorkspace = this.store.readWorkspace();
20271
+ if (!freshTask.ok)
20272
+ return freshTask;
20273
+ if (!freshWorkspace.ok)
20274
+ return freshWorkspace;
20275
+ if (!freshWorkspace.data)
20276
+ return failure("not_found", "workspace not found");
20277
+ if (observation.taskId !== view.task.id || observation.expectedRevision !== freshTask.data.revision || observation.expectedWorkspaceRevision !== freshWorkspace.data.revision)
19619
20278
  return failure("revision_conflict", "worker observation is stale");
19620
20279
  if (!view.task.workers.some((entry) => entry.id === observation.workerId))
19621
20280
  return failure("permission_denied", "worker observation is not assigned");
@@ -19701,19 +20360,26 @@ class WorkitCore {
19701
20360
  return failure("permission_denied", "imported resume requires native destination approval");
19702
20361
  resumeCandidate = current.data.candidate;
19703
20362
  }
19704
- const blocker = this.activeWorkerBlocker(task.data, workspace.data);
20363
+ const blocker = status === "paused" ? workspace.data.writer ? failure("recovery_required", "writer ownership must be released first") : null : this.activeWorkerBlocker(task.data, workspace.data);
19705
20364
  if (blocker)
19706
20365
  return blocker;
20366
+ let pauseCandidate = null;
20367
+ if (status === "paused") {
20368
+ const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
20369
+ if (!captured.ok)
20370
+ return captured;
20371
+ pauseCandidate = captured.data;
20372
+ }
19707
20373
  const changed = this.store.mutateTaskAndWorkspace({
19708
20374
  taskId: task.data.id,
19709
- expectedTaskRevision: input.expectedRevision,
20375
+ expectedRevision: input.expectedRevision,
19710
20376
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19711
20377
  now: trustedNow(this.context),
19712
20378
  workspace: (workspace, context) => success(context.revision, context.revision, workspace),
19713
20379
  task: (current, context) => success(context.revision, null, {
19714
20380
  ...current,
19715
20381
  status,
19716
- candidates: resumeCandidate && !current.candidates.some((candidate) => candidate.id === resumeCandidate.id) ? [...current.candidates, resumeCandidate] : current.candidates,
20382
+ candidates: (resumeCandidate ?? pauseCandidate) && !current.candidates.some((candidate) => candidate.id === (resumeCandidate ?? pauseCandidate).id) ? [...current.candidates, resumeCandidate ?? pauseCandidate] : current.candidates,
19717
20383
  progress: current.progress,
19718
20384
  pauseReason: status === "paused" ? input.reason ?? null : null
19719
20385
  })
@@ -19751,12 +20417,13 @@ class WorkitCore {
19751
20417
  return workspace;
19752
20418
  if (!workspace.data)
19753
20419
  return failure("not_found", "workspace not found");
19754
- if (workspace.data.writer || task.data.workers.some((entry) => isUncertainWorker(entry.data.state)))
19755
- return failure("recovery_required", "scope revision requires worker ownership reconciliation");
20420
+ const revisionBlocker = this.activeWorkerBlocker(task.data, workspace.data);
20421
+ if (revisionBlocker)
20422
+ return revisionBlocker;
19756
20423
  this.fillRevisions(input, task.data, workspace.data);
19757
20424
  const changed = this.store.mutateTaskAndWorkspace({
19758
20425
  taskId: task.data.id,
19759
- expectedTaskRevision: input.expectedRevision,
20426
+ expectedRevision: input.expectedRevision,
19760
20427
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19761
20428
  now: trustedNow(this.context),
19762
20429
  workspace: (workspace, context) => success(context.revision, context.revision, workspace),
@@ -19812,7 +20479,7 @@ class WorkitCore {
19812
20479
  return closure;
19813
20480
  const changed = this.store.mutateTaskAndWorkspace({
19814
20481
  taskId: task.data.id,
19815
- expectedTaskRevision: input.expectedRevision,
20482
+ expectedRevision: input.expectedRevision,
19816
20483
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19817
20484
  now: trustedNow(this.context),
19818
20485
  workspace: (value, context) => success(context.revision, context.revision, value),
@@ -19849,7 +20516,7 @@ function gitRevisionParts(value) {
19849
20516
  }
19850
20517
  function resolveGitRevision(root, value) {
19851
20518
  for (const part of gitRevisionParts(value)) {
19852
- const result = run(root, "git", [
20519
+ const result = run2(root, "git", [
19853
20520
  "rev-parse",
19854
20521
  "--verify",
19855
20522
  "--quiet",
@@ -19861,23 +20528,23 @@ function resolveGitRevision(root, value) {
19861
20528
  }
19862
20529
  }
19863
20530
  function resolveInside(root, candidate) {
19864
- const base = realpathSync5(root);
19865
- const target = path7.resolve(base, candidate);
19866
- if (target !== base && !target.startsWith(base + path7.sep)) {
20531
+ const base = realpathSync6(root);
20532
+ const target = path10.resolve(base, candidate);
20533
+ if (target !== base && !target.startsWith(base + path10.sep)) {
19867
20534
  throw new Error("path must stay inside repository root");
19868
20535
  }
19869
20536
  let ancestor = target;
19870
- while (!existsSync4(ancestor))
19871
- ancestor = path7.dirname(ancestor);
19872
- const canonicalAncestor = realpathSync5(ancestor);
19873
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path7.sep)) {
20537
+ while (!existsSync5(ancestor))
20538
+ ancestor = path10.dirname(ancestor);
20539
+ const canonicalAncestor = realpathSync6(ancestor);
20540
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path10.sep)) {
19874
20541
  throw new Error("path must stay inside repository root");
19875
20542
  }
19876
20543
  return target;
19877
20544
  }
19878
- function run(root, executable, args, env = {}) {
19879
- const cwd = realpathSync5(root);
19880
- const result = spawnSync2(executable, args, {
20545
+ function run2(root, executable, args, env = {}) {
20546
+ const cwd = realpathSync6(root);
20547
+ const result = spawnSync3(executable, args, {
19881
20548
  cwd,
19882
20549
  encoding: "utf8",
19883
20550
  env: { ...process.env, ...env }
@@ -24517,1029 +25184,707 @@ class Protocol {
24517
25184
  this._onrequest(message, extra);
24518
25185
  } else if (isJSONRPCNotification(message)) {
24519
25186
  this._onnotification(message);
24520
- } else {
24521
- this._onerror(new Error(`Unknown message type: ${JSON.stringify(message)}`));
24522
- }
24523
- };
24524
- await this._transport.start();
24525
- }
24526
- _onclose() {
24527
- const responseHandlers = this._responseHandlers;
24528
- this._responseHandlers = new Map;
24529
- this._progressHandlers.clear();
24530
- this._taskProgressTokens.clear();
24531
- this._pendingDebouncedNotifications.clear();
24532
- for (const info of this._timeoutInfo.values()) {
24533
- clearTimeout(info.timeoutId);
24534
- }
24535
- this._timeoutInfo.clear();
24536
- for (const controller of this._requestHandlerAbortControllers.values()) {
24537
- controller.abort();
24538
- }
24539
- this._requestHandlerAbortControllers.clear();
24540
- const error = McpError.fromError(ErrorCode.ConnectionClosed, "Connection closed");
24541
- this._transport = undefined;
24542
- this.onclose?.();
24543
- for (const handler of responseHandlers.values()) {
24544
- handler(error);
24545
- }
24546
- }
24547
- _onerror(error) {
24548
- this.onerror?.(error);
24549
- }
24550
- _onnotification(notification) {
24551
- const handler = this._notificationHandlers.get(notification.method) ?? this.fallbackNotificationHandler;
24552
- if (handler === undefined) {
24553
- return;
24554
- }
24555
- Promise.resolve().then(() => handler(notification)).catch((error) => this._onerror(new Error(`Uncaught error in notification handler: ${error}`)));
24556
- }
24557
- _onrequest(request, extra) {
24558
- const handler = this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
24559
- const capturedTransport = this._transport;
24560
- const relatedTaskId = request.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
24561
- if (handler === undefined) {
24562
- const errorResponse = {
24563
- jsonrpc: "2.0",
24564
- id: request.id,
24565
- error: {
24566
- code: ErrorCode.MethodNotFound,
24567
- message: "Method not found"
24568
- }
24569
- };
24570
- if (relatedTaskId && this._taskMessageQueue) {
24571
- this._enqueueTaskMessage(relatedTaskId, {
24572
- type: "error",
24573
- message: errorResponse,
24574
- timestamp: Date.now()
24575
- }, capturedTransport?.sessionId).catch((error) => this._onerror(new Error(`Failed to enqueue error response: ${error}`)));
24576
- } else {
24577
- capturedTransport?.send(errorResponse).catch((error) => this._onerror(new Error(`Failed to send an error response: ${error}`)));
24578
- }
24579
- return;
24580
- }
24581
- const abortController = new AbortController;
24582
- this._requestHandlerAbortControllers.set(request.id, abortController);
24583
- const taskCreationParams = isTaskAugmentedRequestParams(request.params) ? request.params.task : undefined;
24584
- const taskStore = this._taskStore ? this.requestTaskStore(request, capturedTransport?.sessionId) : undefined;
24585
- const fullExtra = {
24586
- signal: abortController.signal,
24587
- sessionId: capturedTransport?.sessionId,
24588
- _meta: request.params?._meta,
24589
- sendNotification: async (notification) => {
24590
- if (abortController.signal.aborted)
24591
- return;
24592
- const notificationOptions = { relatedRequestId: request.id };
24593
- if (relatedTaskId) {
24594
- notificationOptions.relatedTask = { taskId: relatedTaskId };
24595
- }
24596
- await this.notification(notification, notificationOptions);
24597
- },
24598
- sendRequest: async (r, resultSchema, options) => {
24599
- if (abortController.signal.aborted) {
24600
- throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
24601
- }
24602
- const requestOptions = { ...options, relatedRequestId: request.id };
24603
- if (relatedTaskId && !requestOptions.relatedTask) {
24604
- requestOptions.relatedTask = { taskId: relatedTaskId };
24605
- }
24606
- const effectiveTaskId = requestOptions.relatedTask?.taskId ?? relatedTaskId;
24607
- if (effectiveTaskId && taskStore) {
24608
- await taskStore.updateTaskStatus(effectiveTaskId, "input_required");
24609
- }
24610
- return await this.request(r, resultSchema, requestOptions);
24611
- },
24612
- authInfo: extra?.authInfo,
24613
- requestId: request.id,
24614
- requestInfo: extra?.requestInfo,
24615
- taskId: relatedTaskId,
24616
- taskStore,
24617
- taskRequestedTtl: taskCreationParams?.ttl,
24618
- closeSSEStream: extra?.closeSSEStream,
24619
- closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
24620
- };
24621
- Promise.resolve().then(() => {
24622
- if (taskCreationParams) {
24623
- this.assertTaskHandlerCapability(request.method);
24624
- }
24625
- }).then(() => handler(request, fullExtra)).then(async (result) => {
24626
- if (abortController.signal.aborted) {
24627
- return;
24628
- }
24629
- const response = {
24630
- result,
24631
- jsonrpc: "2.0",
24632
- id: request.id
24633
- };
24634
- if (relatedTaskId && this._taskMessageQueue) {
24635
- await this._enqueueTaskMessage(relatedTaskId, {
24636
- type: "response",
24637
- message: response,
24638
- timestamp: Date.now()
24639
- }, capturedTransport?.sessionId);
24640
- } else {
24641
- await capturedTransport?.send(response);
24642
- }
24643
- }, async (error) => {
24644
- if (abortController.signal.aborted) {
24645
- return;
24646
- }
24647
- const errorResponse = {
24648
- jsonrpc: "2.0",
24649
- id: request.id,
24650
- error: {
24651
- code: Number.isSafeInteger(error["code"]) ? error["code"] : ErrorCode.InternalError,
24652
- message: error.message ?? "Internal error",
24653
- ...error["data"] !== undefined && { data: error["data"] }
24654
- }
24655
- };
24656
- if (relatedTaskId && this._taskMessageQueue) {
24657
- await this._enqueueTaskMessage(relatedTaskId, {
24658
- type: "error",
24659
- message: errorResponse,
24660
- timestamp: Date.now()
24661
- }, capturedTransport?.sessionId);
24662
- } else {
24663
- await capturedTransport?.send(errorResponse);
24664
- }
24665
- }).catch((error) => this._onerror(new Error(`Failed to send response: ${error}`))).finally(() => {
24666
- if (this._requestHandlerAbortControllers.get(request.id) === abortController) {
24667
- this._requestHandlerAbortControllers.delete(request.id);
24668
- }
24669
- });
24670
- }
24671
- _onprogress(notification) {
24672
- const { progressToken, ...params } = notification.params;
24673
- const messageId = Number(progressToken);
24674
- const handler = this._progressHandlers.get(messageId);
24675
- if (!handler) {
24676
- this._onerror(new Error(`Received a progress notification for an unknown token: ${JSON.stringify(notification)}`));
24677
- return;
24678
- }
24679
- const responseHandler = this._responseHandlers.get(messageId);
24680
- const timeoutInfo = this._timeoutInfo.get(messageId);
24681
- if (timeoutInfo && responseHandler && timeoutInfo.resetTimeoutOnProgress) {
24682
- try {
24683
- this._resetTimeout(messageId);
24684
- } catch (error) {
24685
- this._responseHandlers.delete(messageId);
24686
- this._progressHandlers.delete(messageId);
24687
- this._cleanupTimeout(messageId);
24688
- responseHandler(error);
24689
- return;
24690
- }
24691
- }
24692
- handler(params);
24693
- }
24694
- _onresponse(response) {
24695
- const messageId = Number(response.id);
24696
- const resolver = this._requestResolvers.get(messageId);
24697
- if (resolver) {
24698
- this._requestResolvers.delete(messageId);
24699
- if (isJSONRPCResultResponse(response)) {
24700
- resolver(response);
24701
- } else {
24702
- const error = new McpError(response.error.code, response.error.message, response.error.data);
24703
- resolver(error);
24704
- }
24705
- return;
24706
- }
24707
- const handler = this._responseHandlers.get(messageId);
24708
- if (handler === undefined) {
24709
- this._onerror(new Error(`Received a response for an unknown message ID: ${JSON.stringify(response)}`));
24710
- return;
24711
- }
24712
- this._responseHandlers.delete(messageId);
24713
- this._cleanupTimeout(messageId);
24714
- let isTaskResponse = false;
24715
- if (isJSONRPCResultResponse(response) && response.result && typeof response.result === "object") {
24716
- const result = response.result;
24717
- if (result.task && typeof result.task === "object") {
24718
- const task = result.task;
24719
- if (typeof task.taskId === "string") {
24720
- isTaskResponse = true;
24721
- this._taskProgressTokens.set(task.taskId, messageId);
24722
- }
25187
+ } else {
25188
+ this._onerror(new Error(`Unknown message type: ${JSON.stringify(message)}`));
24723
25189
  }
25190
+ };
25191
+ await this._transport.start();
25192
+ }
25193
+ _onclose() {
25194
+ const responseHandlers = this._responseHandlers;
25195
+ this._responseHandlers = new Map;
25196
+ this._progressHandlers.clear();
25197
+ this._taskProgressTokens.clear();
25198
+ this._pendingDebouncedNotifications.clear();
25199
+ for (const info of this._timeoutInfo.values()) {
25200
+ clearTimeout(info.timeoutId);
24724
25201
  }
24725
- if (!isTaskResponse) {
24726
- this._progressHandlers.delete(messageId);
25202
+ this._timeoutInfo.clear();
25203
+ for (const controller of this._requestHandlerAbortControllers.values()) {
25204
+ controller.abort();
24727
25205
  }
24728
- if (isJSONRPCResultResponse(response)) {
24729
- handler(response);
24730
- } else {
24731
- const error = McpError.fromError(response.error.code, response.error.message, response.error.data);
25206
+ this._requestHandlerAbortControllers.clear();
25207
+ const error = McpError.fromError(ErrorCode.ConnectionClosed, "Connection closed");
25208
+ this._transport = undefined;
25209
+ this.onclose?.();
25210
+ for (const handler of responseHandlers.values()) {
24732
25211
  handler(error);
24733
25212
  }
24734
25213
  }
24735
- get transport() {
24736
- return this._transport;
24737
- }
24738
- async close() {
24739
- await this._transport?.close();
25214
+ _onerror(error) {
25215
+ this.onerror?.(error);
24740
25216
  }
24741
- async* requestStream(request, resultSchema, options) {
24742
- const { task } = options ?? {};
24743
- if (!task) {
24744
- try {
24745
- const result = await this.request(request, resultSchema, options);
24746
- yield { type: "result", result };
24747
- } catch (error) {
24748
- yield {
24749
- type: "error",
24750
- error: error instanceof McpError ? error : new McpError(ErrorCode.InternalError, String(error))
24751
- };
24752
- }
25217
+ _onnotification(notification) {
25218
+ const handler = this._notificationHandlers.get(notification.method) ?? this.fallbackNotificationHandler;
25219
+ if (handler === undefined) {
24753
25220
  return;
24754
25221
  }
24755
- let taskId;
24756
- try {
24757
- const createResult = await this.request(request, CreateTaskResultSchema, options);
24758
- if (createResult.task) {
24759
- taskId = createResult.task.taskId;
24760
- yield { type: "taskCreated", task: createResult.task };
24761
- } else {
24762
- throw new McpError(ErrorCode.InternalError, "Task creation did not return a task");
24763
- }
24764
- while (true) {
24765
- const task = await this.getTask({ taskId }, options);
24766
- yield { type: "taskStatus", task };
24767
- if (isTerminal(task.status)) {
24768
- if (task.status === "completed") {
24769
- const result = await this.getTaskResult({ taskId }, resultSchema, options);
24770
- yield { type: "result", result };
24771
- } else if (task.status === "failed") {
24772
- yield {
24773
- type: "error",
24774
- error: new McpError(ErrorCode.InternalError, `Task ${taskId} failed`)
24775
- };
24776
- } else if (task.status === "cancelled") {
24777
- yield {
24778
- type: "error",
24779
- error: new McpError(ErrorCode.InternalError, `Task ${taskId} was cancelled`)
24780
- };
24781
- }
24782
- return;
24783
- }
24784
- if (task.status === "input_required") {
24785
- const result = await this.getTaskResult({ taskId }, resultSchema, options);
24786
- yield { type: "result", result };
24787
- return;
24788
- }
24789
- const pollInterval = task.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
24790
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
24791
- options?.signal?.throwIfAborted();
24792
- }
24793
- } catch (error) {
24794
- yield {
24795
- type: "error",
24796
- error: error instanceof McpError ? error : new McpError(ErrorCode.InternalError, String(error))
24797
- };
24798
- }
25222
+ Promise.resolve().then(() => handler(notification)).catch((error) => this._onerror(new Error(`Uncaught error in notification handler: ${error}`)));
24799
25223
  }
24800
- request(request, resultSchema, options) {
24801
- const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
24802
- return new Promise((resolve, reject) => {
24803
- const earlyReject = (error) => {
24804
- reject(error);
24805
- };
24806
- if (!this._transport) {
24807
- earlyReject(new Error("Not connected"));
24808
- return;
24809
- }
24810
- if (this._options?.enforceStrictCapabilities === true) {
24811
- try {
24812
- this.assertCapabilityForMethod(request.method);
24813
- if (task) {
24814
- this.assertTaskCapability(request.method);
24815
- }
24816
- } catch (e) {
24817
- earlyReject(e);
24818
- return;
24819
- }
24820
- }
24821
- options?.signal?.throwIfAborted();
24822
- const messageId = this._requestMessageId++;
24823
- const jsonrpcRequest = {
24824
- ...request,
25224
+ _onrequest(request, extra) {
25225
+ const handler = this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
25226
+ const capturedTransport = this._transport;
25227
+ const relatedTaskId = request.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
25228
+ if (handler === undefined) {
25229
+ const errorResponse = {
24825
25230
  jsonrpc: "2.0",
24826
- id: messageId
25231
+ id: request.id,
25232
+ error: {
25233
+ code: ErrorCode.MethodNotFound,
25234
+ message: "Method not found"
25235
+ }
24827
25236
  };
24828
- if (options?.onprogress) {
24829
- this._progressHandlers.set(messageId, options.onprogress);
24830
- jsonrpcRequest.params = {
24831
- ...request.params,
24832
- _meta: {
24833
- ...request.params?._meta || {},
24834
- progressToken: messageId
24835
- }
24836
- };
24837
- }
24838
- if (task) {
24839
- jsonrpcRequest.params = {
24840
- ...jsonrpcRequest.params,
24841
- task
24842
- };
24843
- }
24844
- if (relatedTask) {
24845
- jsonrpcRequest.params = {
24846
- ...jsonrpcRequest.params,
24847
- _meta: {
24848
- ...jsonrpcRequest.params?._meta || {},
24849
- [RELATED_TASK_META_KEY]: relatedTask
24850
- }
24851
- };
25237
+ if (relatedTaskId && this._taskMessageQueue) {
25238
+ this._enqueueTaskMessage(relatedTaskId, {
25239
+ type: "error",
25240
+ message: errorResponse,
25241
+ timestamp: Date.now()
25242
+ }, capturedTransport?.sessionId).catch((error) => this._onerror(new Error(`Failed to enqueue error response: ${error}`)));
25243
+ } else {
25244
+ capturedTransport?.send(errorResponse).catch((error) => this._onerror(new Error(`Failed to send an error response: ${error}`)));
24852
25245
  }
24853
- const cancel = (reason) => {
24854
- this._responseHandlers.delete(messageId);
24855
- this._progressHandlers.delete(messageId);
24856
- this._cleanupTimeout(messageId);
24857
- this._transport?.send({
24858
- jsonrpc: "2.0",
24859
- method: "notifications/cancelled",
24860
- params: {
24861
- requestId: messageId,
24862
- reason: String(reason)
24863
- }
24864
- }, { relatedRequestId, resumptionToken, onresumptiontoken }).catch((error) => this._onerror(new Error(`Failed to send cancellation: ${error}`)));
24865
- const error = reason instanceof McpError ? reason : new McpError(ErrorCode.RequestTimeout, String(reason));
24866
- reject(error);
24867
- };
24868
- this._responseHandlers.set(messageId, (response) => {
24869
- if (options?.signal?.aborted) {
25246
+ return;
25247
+ }
25248
+ const abortController = new AbortController;
25249
+ this._requestHandlerAbortControllers.set(request.id, abortController);
25250
+ const taskCreationParams = isTaskAugmentedRequestParams(request.params) ? request.params.task : undefined;
25251
+ const taskStore = this._taskStore ? this.requestTaskStore(request, capturedTransport?.sessionId) : undefined;
25252
+ const fullExtra = {
25253
+ signal: abortController.signal,
25254
+ sessionId: capturedTransport?.sessionId,
25255
+ _meta: request.params?._meta,
25256
+ sendNotification: async (notification) => {
25257
+ if (abortController.signal.aborted)
24870
25258
  return;
25259
+ const notificationOptions = { relatedRequestId: request.id };
25260
+ if (relatedTaskId) {
25261
+ notificationOptions.relatedTask = { taskId: relatedTaskId };
24871
25262
  }
24872
- if (response instanceof Error) {
24873
- return reject(response);
25263
+ await this.notification(notification, notificationOptions);
25264
+ },
25265
+ sendRequest: async (r, resultSchema, options) => {
25266
+ if (abortController.signal.aborted) {
25267
+ throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
24874
25268
  }
24875
- try {
24876
- const parseResult = safeParse4(resultSchema, response.result);
24877
- if (!parseResult.success) {
24878
- reject(parseResult.error);
24879
- } else {
24880
- resolve(parseResult.data);
24881
- }
24882
- } catch (error) {
24883
- reject(error);
25269
+ const requestOptions = { ...options, relatedRequestId: request.id };
25270
+ if (relatedTaskId && !requestOptions.relatedTask) {
25271
+ requestOptions.relatedTask = { taskId: relatedTaskId };
24884
25272
  }
24885
- });
24886
- options?.signal?.addEventListener("abort", () => {
24887
- cancel(options?.signal?.reason);
24888
- });
24889
- const timeout = options?.timeout ?? DEFAULT_REQUEST_TIMEOUT_MSEC;
24890
- const timeoutHandler = () => cancel(McpError.fromError(ErrorCode.RequestTimeout, "Request timed out", { timeout }));
24891
- this._setupTimeout(messageId, timeout, options?.maxTotalTimeout, timeoutHandler, options?.resetTimeoutOnProgress ?? false);
24892
- const relatedTaskId = relatedTask?.taskId;
24893
- if (relatedTaskId) {
24894
- const responseResolver = (response) => {
24895
- const handler = this._responseHandlers.get(messageId);
24896
- if (handler) {
24897
- handler(response);
24898
- } else {
24899
- this._onerror(new Error(`Response handler missing for side-channeled request ${messageId}`));
24900
- }
24901
- };
24902
- this._requestResolvers.set(messageId, responseResolver);
24903
- this._enqueueTaskMessage(relatedTaskId, {
24904
- type: "request",
24905
- message: jsonrpcRequest,
25273
+ const effectiveTaskId = requestOptions.relatedTask?.taskId ?? relatedTaskId;
25274
+ if (effectiveTaskId && taskStore) {
25275
+ await taskStore.updateTaskStatus(effectiveTaskId, "input_required");
25276
+ }
25277
+ return await this.request(r, resultSchema, requestOptions);
25278
+ },
25279
+ authInfo: extra?.authInfo,
25280
+ requestId: request.id,
25281
+ requestInfo: extra?.requestInfo,
25282
+ taskId: relatedTaskId,
25283
+ taskStore,
25284
+ taskRequestedTtl: taskCreationParams?.ttl,
25285
+ closeSSEStream: extra?.closeSSEStream,
25286
+ closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
25287
+ };
25288
+ Promise.resolve().then(() => {
25289
+ if (taskCreationParams) {
25290
+ this.assertTaskHandlerCapability(request.method);
25291
+ }
25292
+ }).then(() => handler(request, fullExtra)).then(async (result) => {
25293
+ if (abortController.signal.aborted) {
25294
+ return;
25295
+ }
25296
+ const response = {
25297
+ result,
25298
+ jsonrpc: "2.0",
25299
+ id: request.id
25300
+ };
25301
+ if (relatedTaskId && this._taskMessageQueue) {
25302
+ await this._enqueueTaskMessage(relatedTaskId, {
25303
+ type: "response",
25304
+ message: response,
24906
25305
  timestamp: Date.now()
24907
- }).catch((error) => {
24908
- this._cleanupTimeout(messageId);
24909
- reject(error);
24910
- });
25306
+ }, capturedTransport?.sessionId);
24911
25307
  } else {
24912
- this._transport.send(jsonrpcRequest, { relatedRequestId, resumptionToken, onresumptiontoken }).catch((error) => {
24913
- this._cleanupTimeout(messageId);
24914
- reject(error);
24915
- });
25308
+ await capturedTransport?.send(response);
24916
25309
  }
24917
- });
24918
- }
24919
- async getTask(params, options) {
24920
- return this.request({ method: "tasks/get", params }, GetTaskResultSchema, options);
24921
- }
24922
- async getTaskResult(params, resultSchema, options) {
24923
- return this.request({ method: "tasks/result", params }, resultSchema, options);
24924
- }
24925
- async listTasks(params, options) {
24926
- return this.request({ method: "tasks/list", params }, ListTasksResultSchema, options);
24927
- }
24928
- async cancelTask(params, options) {
24929
- return this.request({ method: "tasks/cancel", params }, CancelTaskResultSchema, options);
24930
- }
24931
- async notification(notification, options) {
24932
- if (!this._transport) {
24933
- throw new Error("Not connected");
24934
- }
24935
- this.assertNotificationCapability(notification.method);
24936
- const relatedTaskId = options?.relatedTask?.taskId;
24937
- if (relatedTaskId) {
24938
- const jsonrpcNotification = {
24939
- ...notification,
25310
+ }, async (error) => {
25311
+ if (abortController.signal.aborted) {
25312
+ return;
25313
+ }
25314
+ const errorResponse = {
24940
25315
  jsonrpc: "2.0",
24941
- params: {
24942
- ...notification.params,
24943
- _meta: {
24944
- ...notification.params?._meta || {},
24945
- [RELATED_TASK_META_KEY]: options.relatedTask
24946
- }
25316
+ id: request.id,
25317
+ error: {
25318
+ code: Number.isSafeInteger(error["code"]) ? error["code"] : ErrorCode.InternalError,
25319
+ message: error.message ?? "Internal error",
25320
+ ...error["data"] !== undefined && { data: error["data"] }
24947
25321
  }
24948
25322
  };
24949
- await this._enqueueTaskMessage(relatedTaskId, {
24950
- type: "notification",
24951
- message: jsonrpcNotification,
24952
- timestamp: Date.now()
24953
- });
25323
+ if (relatedTaskId && this._taskMessageQueue) {
25324
+ await this._enqueueTaskMessage(relatedTaskId, {
25325
+ type: "error",
25326
+ message: errorResponse,
25327
+ timestamp: Date.now()
25328
+ }, capturedTransport?.sessionId);
25329
+ } else {
25330
+ await capturedTransport?.send(errorResponse);
25331
+ }
25332
+ }).catch((error) => this._onerror(new Error(`Failed to send response: ${error}`))).finally(() => {
25333
+ if (this._requestHandlerAbortControllers.get(request.id) === abortController) {
25334
+ this._requestHandlerAbortControllers.delete(request.id);
25335
+ }
25336
+ });
25337
+ }
25338
+ _onprogress(notification) {
25339
+ const { progressToken, ...params } = notification.params;
25340
+ const messageId = Number(progressToken);
25341
+ const handler = this._progressHandlers.get(messageId);
25342
+ if (!handler) {
25343
+ this._onerror(new Error(`Received a progress notification for an unknown token: ${JSON.stringify(notification)}`));
24954
25344
  return;
24955
25345
  }
24956
- const debouncedMethods = this._options?.debouncedNotificationMethods ?? [];
24957
- const canDebounce = debouncedMethods.includes(notification.method) && !notification.params && !options?.relatedRequestId && !options?.relatedTask;
24958
- if (canDebounce) {
24959
- if (this._pendingDebouncedNotifications.has(notification.method)) {
25346
+ const responseHandler = this._responseHandlers.get(messageId);
25347
+ const timeoutInfo = this._timeoutInfo.get(messageId);
25348
+ if (timeoutInfo && responseHandler && timeoutInfo.resetTimeoutOnProgress) {
25349
+ try {
25350
+ this._resetTimeout(messageId);
25351
+ } catch (error) {
25352
+ this._responseHandlers.delete(messageId);
25353
+ this._progressHandlers.delete(messageId);
25354
+ this._cleanupTimeout(messageId);
25355
+ responseHandler(error);
24960
25356
  return;
24961
25357
  }
24962
- this._pendingDebouncedNotifications.add(notification.method);
24963
- Promise.resolve().then(() => {
24964
- this._pendingDebouncedNotifications.delete(notification.method);
24965
- if (!this._transport) {
24966
- return;
24967
- }
24968
- let jsonrpcNotification = {
24969
- ...notification,
24970
- jsonrpc: "2.0"
24971
- };
24972
- if (options?.relatedTask) {
24973
- jsonrpcNotification = {
24974
- ...jsonrpcNotification,
24975
- params: {
24976
- ...jsonrpcNotification.params,
24977
- _meta: {
24978
- ...jsonrpcNotification.params?._meta || {},
24979
- [RELATED_TASK_META_KEY]: options.relatedTask
24980
- }
24981
- }
24982
- };
24983
- }
24984
- this._transport?.send(jsonrpcNotification, options).catch((error) => this._onerror(error));
24985
- });
25358
+ }
25359
+ handler(params);
25360
+ }
25361
+ _onresponse(response) {
25362
+ const messageId = Number(response.id);
25363
+ const resolver = this._requestResolvers.get(messageId);
25364
+ if (resolver) {
25365
+ this._requestResolvers.delete(messageId);
25366
+ if (isJSONRPCResultResponse(response)) {
25367
+ resolver(response);
25368
+ } else {
25369
+ const error = new McpError(response.error.code, response.error.message, response.error.data);
25370
+ resolver(error);
25371
+ }
24986
25372
  return;
24987
25373
  }
24988
- let jsonrpcNotification = {
24989
- ...notification,
24990
- jsonrpc: "2.0"
24991
- };
24992
- if (options?.relatedTask) {
24993
- jsonrpcNotification = {
24994
- ...jsonrpcNotification,
24995
- params: {
24996
- ...jsonrpcNotification.params,
24997
- _meta: {
24998
- ...jsonrpcNotification.params?._meta || {},
24999
- [RELATED_TASK_META_KEY]: options.relatedTask
25000
- }
25374
+ const handler = this._responseHandlers.get(messageId);
25375
+ if (handler === undefined) {
25376
+ this._onerror(new Error(`Received a response for an unknown message ID: ${JSON.stringify(response)}`));
25377
+ return;
25378
+ }
25379
+ this._responseHandlers.delete(messageId);
25380
+ this._cleanupTimeout(messageId);
25381
+ let isTaskResponse = false;
25382
+ if (isJSONRPCResultResponse(response) && response.result && typeof response.result === "object") {
25383
+ const result = response.result;
25384
+ if (result.task && typeof result.task === "object") {
25385
+ const task = result.task;
25386
+ if (typeof task.taskId === "string") {
25387
+ isTaskResponse = true;
25388
+ this._taskProgressTokens.set(task.taskId, messageId);
25001
25389
  }
25002
- };
25390
+ }
25003
25391
  }
25004
- await this._transport.send(jsonrpcNotification, options);
25005
- }
25006
- setRequestHandler(requestSchema, handler) {
25007
- const method = getMethodLiteral(requestSchema);
25008
- this.assertRequestHandlerCapability(method);
25009
- this._requestHandlers.set(method, (request, extra) => {
25010
- const parsed = parseWithCompat(requestSchema, request);
25011
- return Promise.resolve(handler(parsed, extra));
25012
- });
25013
- }
25014
- removeRequestHandler(method) {
25015
- this._requestHandlers.delete(method);
25016
- }
25017
- assertCanSetRequestHandler(method) {
25018
- if (this._requestHandlers.has(method)) {
25019
- throw new Error(`A request handler for ${method} already exists, which would be overridden`);
25392
+ if (!isTaskResponse) {
25393
+ this._progressHandlers.delete(messageId);
25394
+ }
25395
+ if (isJSONRPCResultResponse(response)) {
25396
+ handler(response);
25397
+ } else {
25398
+ const error = McpError.fromError(response.error.code, response.error.message, response.error.data);
25399
+ handler(error);
25020
25400
  }
25021
25401
  }
25022
- setNotificationHandler(notificationSchema, handler) {
25023
- const method = getMethodLiteral(notificationSchema);
25024
- this._notificationHandlers.set(method, (notification) => {
25025
- const parsed = parseWithCompat(notificationSchema, notification);
25026
- return Promise.resolve(handler(parsed));
25027
- });
25028
- }
25029
- removeNotificationHandler(method) {
25030
- this._notificationHandlers.delete(method);
25402
+ get transport() {
25403
+ return this._transport;
25031
25404
  }
25032
- _cleanupTaskProgressHandler(taskId) {
25033
- const progressToken = this._taskProgressTokens.get(taskId);
25034
- if (progressToken !== undefined) {
25035
- this._progressHandlers.delete(progressToken);
25036
- this._taskProgressTokens.delete(taskId);
25037
- }
25405
+ async close() {
25406
+ await this._transport?.close();
25038
25407
  }
25039
- async _enqueueTaskMessage(taskId, message, sessionId) {
25040
- if (!this._taskStore || !this._taskMessageQueue) {
25041
- throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
25408
+ async* requestStream(request, resultSchema, options) {
25409
+ const { task } = options ?? {};
25410
+ if (!task) {
25411
+ try {
25412
+ const result = await this.request(request, resultSchema, options);
25413
+ yield { type: "result", result };
25414
+ } catch (error) {
25415
+ yield {
25416
+ type: "error",
25417
+ error: error instanceof McpError ? error : new McpError(ErrorCode.InternalError, String(error))
25418
+ };
25419
+ }
25420
+ return;
25042
25421
  }
25043
- const maxQueueSize = this._options?.maxTaskQueueSize;
25044
- await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
25045
- }
25046
- async _clearTaskQueue(taskId, sessionId) {
25047
- if (this._taskMessageQueue) {
25048
- const messages = await this._taskMessageQueue.dequeueAll(taskId, sessionId);
25049
- for (const message of messages) {
25050
- if (message.type === "request" && isJSONRPCRequest(message.message)) {
25051
- const requestId = message.message.id;
25052
- const resolver = this._requestResolvers.get(requestId);
25053
- if (resolver) {
25054
- resolver(new McpError(ErrorCode.InternalError, "Task cancelled or completed"));
25055
- this._requestResolvers.delete(requestId);
25056
- } else {
25057
- this._onerror(new Error(`Resolver missing for request ${requestId} during task ${taskId} cleanup`));
25422
+ let taskId;
25423
+ try {
25424
+ const createResult = await this.request(request, CreateTaskResultSchema, options);
25425
+ if (createResult.task) {
25426
+ taskId = createResult.task.taskId;
25427
+ yield { type: "taskCreated", task: createResult.task };
25428
+ } else {
25429
+ throw new McpError(ErrorCode.InternalError, "Task creation did not return a task");
25430
+ }
25431
+ while (true) {
25432
+ const task = await this.getTask({ taskId }, options);
25433
+ yield { type: "taskStatus", task };
25434
+ if (isTerminal(task.status)) {
25435
+ if (task.status === "completed") {
25436
+ const result = await this.getTaskResult({ taskId }, resultSchema, options);
25437
+ yield { type: "result", result };
25438
+ } else if (task.status === "failed") {
25439
+ yield {
25440
+ type: "error",
25441
+ error: new McpError(ErrorCode.InternalError, `Task ${taskId} failed`)
25442
+ };
25443
+ } else if (task.status === "cancelled") {
25444
+ yield {
25445
+ type: "error",
25446
+ error: new McpError(ErrorCode.InternalError, `Task ${taskId} was cancelled`)
25447
+ };
25058
25448
  }
25449
+ return;
25450
+ }
25451
+ if (task.status === "input_required") {
25452
+ const result = await this.getTaskResult({ taskId }, resultSchema, options);
25453
+ yield { type: "result", result };
25454
+ return;
25059
25455
  }
25456
+ const pollInterval = task.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
25457
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
25458
+ options?.signal?.throwIfAborted();
25060
25459
  }
25460
+ } catch (error) {
25461
+ yield {
25462
+ type: "error",
25463
+ error: error instanceof McpError ? error : new McpError(ErrorCode.InternalError, String(error))
25464
+ };
25061
25465
  }
25062
25466
  }
25063
- async _waitForTaskUpdate(taskId, signal) {
25064
- let interval = this._options?.defaultTaskPollInterval ?? 1000;
25065
- try {
25066
- const task = await this._taskStore?.getTask(taskId);
25067
- if (task?.pollInterval) {
25068
- interval = task.pollInterval;
25069
- }
25070
- } catch {}
25467
+ request(request, resultSchema, options) {
25468
+ const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
25071
25469
  return new Promise((resolve, reject) => {
25072
- if (signal.aborted) {
25073
- reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
25470
+ const earlyReject = (error) => {
25471
+ reject(error);
25472
+ };
25473
+ if (!this._transport) {
25474
+ earlyReject(new Error("Not connected"));
25074
25475
  return;
25075
25476
  }
25076
- const timeoutId = setTimeout(resolve, interval);
25077
- signal.addEventListener("abort", () => {
25078
- clearTimeout(timeoutId);
25079
- reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
25080
- }, { once: true });
25081
- });
25082
- }
25083
- requestTaskStore(request, sessionId) {
25084
- const taskStore = this._taskStore;
25085
- if (!taskStore) {
25086
- throw new Error("No task store configured");
25087
- }
25088
- return {
25089
- createTask: async (taskParams) => {
25090
- if (!request) {
25091
- throw new Error("No request provided");
25092
- }
25093
- return await taskStore.createTask(taskParams, request.id, {
25094
- method: request.method,
25095
- params: request.params
25096
- }, sessionId);
25097
- },
25098
- getTask: async (taskId) => {
25099
- const task = await taskStore.getTask(taskId, sessionId);
25100
- if (!task) {
25101
- throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
25102
- }
25103
- return task;
25104
- },
25105
- storeTaskResult: async (taskId, status, result) => {
25106
- await taskStore.storeTaskResult(taskId, status, result, sessionId);
25107
- const task = await taskStore.getTask(taskId, sessionId);
25108
- if (task) {
25109
- const notification = TaskStatusNotificationSchema.parse({
25110
- method: "notifications/tasks/status",
25111
- params: task
25112
- });
25113
- await this.notification(notification);
25114
- if (isTerminal(task.status)) {
25115
- this._cleanupTaskProgressHandler(taskId);
25116
- }
25117
- }
25118
- },
25119
- getTaskResult: (taskId) => {
25120
- return taskStore.getTaskResult(taskId, sessionId);
25121
- },
25122
- updateTaskStatus: async (taskId, status, statusMessage) => {
25123
- const task = await taskStore.getTask(taskId, sessionId);
25124
- if (!task) {
25125
- throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
25126
- }
25127
- if (isTerminal(task.status)) {
25128
- throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
25129
- }
25130
- await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId);
25131
- const updatedTask = await taskStore.getTask(taskId, sessionId);
25132
- if (updatedTask) {
25133
- const notification = TaskStatusNotificationSchema.parse({
25134
- method: "notifications/tasks/status",
25135
- params: updatedTask
25136
- });
25137
- await this.notification(notification);
25138
- if (isTerminal(updatedTask.status)) {
25139
- this._cleanupTaskProgressHandler(taskId);
25477
+ if (this._options?.enforceStrictCapabilities === true) {
25478
+ try {
25479
+ this.assertCapabilityForMethod(request.method);
25480
+ if (task) {
25481
+ this.assertTaskCapability(request.method);
25140
25482
  }
25483
+ } catch (e) {
25484
+ earlyReject(e);
25485
+ return;
25141
25486
  }
25142
- },
25143
- listTasks: (cursor) => {
25144
- return taskStore.listTasks(cursor, sessionId);
25145
25487
  }
25146
- };
25147
- }
25148
- }
25149
- function isPlainObject3(value) {
25150
- return value !== null && typeof value === "object" && !Array.isArray(value);
25151
- }
25152
- function mergeCapabilities(base, additional) {
25153
- const result = { ...base };
25154
- for (const key in additional) {
25155
- const k = key;
25156
- const addValue = additional[k];
25157
- if (addValue === undefined)
25158
- continue;
25159
- const baseValue = result[k];
25160
- if (isPlainObject3(baseValue) && isPlainObject3(addValue)) {
25161
- result[k] = { ...baseValue, ...addValue };
25162
- } else {
25163
- result[k] = addValue;
25164
- }
25165
- }
25166
- return result;
25167
- }
25168
-
25169
- // node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
25170
- var import_ajv = __toESM(require_ajv(), 1);
25171
- var import_ajv_formats = __toESM(require_dist(), 1);
25172
- function createDefaultAjvInstance() {
25173
- const ajv = new import_ajv.default({
25174
- strict: false,
25175
- validateFormats: true,
25176
- validateSchema: false,
25177
- allErrors: true
25178
- });
25179
- const addFormats = import_ajv_formats.default;
25180
- addFormats(ajv);
25181
- return ajv;
25182
- }
25183
-
25184
- class AjvJsonSchemaValidator {
25185
- constructor(ajv) {
25186
- this._ajv = ajv ?? createDefaultAjvInstance();
25187
- }
25188
- getValidator(schema) {
25189
- const ajvValidator = "$id" in schema && typeof schema.$id === "string" ? this._ajv.getSchema(schema.$id) ?? this._ajv.compile(schema) : this._ajv.compile(schema);
25190
- return (input) => {
25191
- const valid = ajvValidator(input);
25192
- if (valid) {
25193
- return {
25194
- valid: true,
25195
- data: input,
25196
- errorMessage: undefined
25488
+ options?.signal?.throwIfAborted();
25489
+ const messageId = this._requestMessageId++;
25490
+ const jsonrpcRequest = {
25491
+ ...request,
25492
+ jsonrpc: "2.0",
25493
+ id: messageId
25494
+ };
25495
+ if (options?.onprogress) {
25496
+ this._progressHandlers.set(messageId, options.onprogress);
25497
+ jsonrpcRequest.params = {
25498
+ ...request.params,
25499
+ _meta: {
25500
+ ...request.params?._meta || {},
25501
+ progressToken: messageId
25502
+ }
25197
25503
  };
25198
- } else {
25199
- return {
25200
- valid: false,
25201
- data: undefined,
25202
- errorMessage: this._ajv.errorsText(ajvValidator.errors)
25504
+ }
25505
+ if (task) {
25506
+ jsonrpcRequest.params = {
25507
+ ...jsonrpcRequest.params,
25508
+ task
25203
25509
  };
25204
25510
  }
25205
- };
25206
- }
25207
- }
25208
-
25209
- // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
25210
- class ExperimentalServerTasks {
25211
- constructor(_server) {
25212
- this._server = _server;
25213
- }
25214
- requestStream(request, resultSchema, options) {
25215
- return this._server.requestStream(request, resultSchema, options);
25216
- }
25217
- createMessageStream(params, options) {
25218
- const clientCapabilities = this._server.getClientCapabilities();
25219
- if ((params.tools || params.toolChoice) && !clientCapabilities?.sampling?.tools) {
25220
- throw new Error("Client does not support sampling tools capability.");
25221
- }
25222
- if (params.messages.length > 0) {
25223
- const lastMessage = params.messages[params.messages.length - 1];
25224
- const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
25225
- const hasToolResults = lastContent.some((c) => c.type === "tool_result");
25226
- const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : undefined;
25227
- const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
25228
- const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
25229
- if (hasToolResults) {
25230
- if (lastContent.some((c) => c.type !== "tool_result")) {
25231
- throw new Error("The last message must contain only tool_result content if any is present");
25232
- }
25233
- if (!hasPreviousToolUse) {
25234
- throw new Error("tool_result blocks are not matching any tool_use from the previous message");
25235
- }
25511
+ if (relatedTask) {
25512
+ jsonrpcRequest.params = {
25513
+ ...jsonrpcRequest.params,
25514
+ _meta: {
25515
+ ...jsonrpcRequest.params?._meta || {},
25516
+ [RELATED_TASK_META_KEY]: relatedTask
25517
+ }
25518
+ };
25236
25519
  }
25237
- if (hasPreviousToolUse) {
25238
- const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
25239
- const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
25240
- if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
25241
- throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
25520
+ const cancel = (reason) => {
25521
+ this._responseHandlers.delete(messageId);
25522
+ this._progressHandlers.delete(messageId);
25523
+ this._cleanupTimeout(messageId);
25524
+ this._transport?.send({
25525
+ jsonrpc: "2.0",
25526
+ method: "notifications/cancelled",
25527
+ params: {
25528
+ requestId: messageId,
25529
+ reason: String(reason)
25530
+ }
25531
+ }, { relatedRequestId, resumptionToken, onresumptiontoken }).catch((error) => this._onerror(new Error(`Failed to send cancellation: ${error}`)));
25532
+ const error = reason instanceof McpError ? reason : new McpError(ErrorCode.RequestTimeout, String(reason));
25533
+ reject(error);
25534
+ };
25535
+ this._responseHandlers.set(messageId, (response) => {
25536
+ if (options?.signal?.aborted) {
25537
+ return;
25242
25538
  }
25243
- }
25244
- }
25245
- return this.requestStream({
25246
- method: "sampling/createMessage",
25247
- params
25248
- }, CreateMessageResultSchema, options);
25249
- }
25250
- elicitInputStream(params, options) {
25251
- const clientCapabilities = this._server.getClientCapabilities();
25252
- const mode = params.mode ?? "form";
25253
- switch (mode) {
25254
- case "url": {
25255
- if (!clientCapabilities?.elicitation?.url) {
25256
- throw new Error("Client does not support url elicitation.");
25539
+ if (response instanceof Error) {
25540
+ return reject(response);
25257
25541
  }
25258
- break;
25259
- }
25260
- case "form": {
25261
- if (!clientCapabilities?.elicitation?.form) {
25262
- throw new Error("Client does not support form elicitation.");
25542
+ try {
25543
+ const parseResult = safeParse4(resultSchema, response.result);
25544
+ if (!parseResult.success) {
25545
+ reject(parseResult.error);
25546
+ } else {
25547
+ resolve(parseResult.data);
25548
+ }
25549
+ } catch (error) {
25550
+ reject(error);
25263
25551
  }
25264
- break;
25552
+ });
25553
+ options?.signal?.addEventListener("abort", () => {
25554
+ cancel(options?.signal?.reason);
25555
+ });
25556
+ const timeout = options?.timeout ?? DEFAULT_REQUEST_TIMEOUT_MSEC;
25557
+ const timeoutHandler = () => cancel(McpError.fromError(ErrorCode.RequestTimeout, "Request timed out", { timeout }));
25558
+ this._setupTimeout(messageId, timeout, options?.maxTotalTimeout, timeoutHandler, options?.resetTimeoutOnProgress ?? false);
25559
+ const relatedTaskId = relatedTask?.taskId;
25560
+ if (relatedTaskId) {
25561
+ const responseResolver = (response) => {
25562
+ const handler = this._responseHandlers.get(messageId);
25563
+ if (handler) {
25564
+ handler(response);
25565
+ } else {
25566
+ this._onerror(new Error(`Response handler missing for side-channeled request ${messageId}`));
25567
+ }
25568
+ };
25569
+ this._requestResolvers.set(messageId, responseResolver);
25570
+ this._enqueueTaskMessage(relatedTaskId, {
25571
+ type: "request",
25572
+ message: jsonrpcRequest,
25573
+ timestamp: Date.now()
25574
+ }).catch((error) => {
25575
+ this._cleanupTimeout(messageId);
25576
+ reject(error);
25577
+ });
25578
+ } else {
25579
+ this._transport.send(jsonrpcRequest, { relatedRequestId, resumptionToken, onresumptiontoken }).catch((error) => {
25580
+ this._cleanupTimeout(messageId);
25581
+ reject(error);
25582
+ });
25265
25583
  }
25266
- }
25267
- const normalizedParams = mode === "form" && params.mode === undefined ? { ...params, mode: "form" } : params;
25268
- return this.requestStream({
25269
- method: "elicitation/create",
25270
- params: normalizedParams
25271
- }, ElicitResultSchema, options);
25272
- }
25273
- async getTask(taskId, options) {
25274
- return this._server.getTask({ taskId }, options);
25275
- }
25276
- async getTaskResult(taskId, resultSchema, options) {
25277
- return this._server.getTaskResult({ taskId }, resultSchema, options);
25278
- }
25279
- async listTasks(cursor, options) {
25280
- return this._server.listTasks(cursor ? { cursor } : undefined, options);
25281
- }
25282
- async cancelTask(taskId, options) {
25283
- return this._server.cancelTask({ taskId }, options);
25584
+ });
25284
25585
  }
25285
- }
25286
-
25287
- // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
25288
- function assertToolsCallTaskCapability(requests, method, entityName) {
25289
- if (!requests) {
25290
- throw new Error(`${entityName} does not support task creation (required for ${method})`);
25586
+ async getTask(params, options) {
25587
+ return this.request({ method: "tasks/get", params }, GetTaskResultSchema, options);
25291
25588
  }
25292
- switch (method) {
25293
- case "tools/call":
25294
- if (!requests.tools?.call) {
25295
- throw new Error(`${entityName} does not support task creation for tools/call (required for ${method})`);
25296
- }
25297
- break;
25298
- default:
25299
- break;
25589
+ async getTaskResult(params, resultSchema, options) {
25590
+ return this.request({ method: "tasks/result", params }, resultSchema, options);
25300
25591
  }
25301
- }
25302
- function assertClientRequestTaskCapability(requests, method, entityName) {
25303
- if (!requests) {
25304
- throw new Error(`${entityName} does not support task creation (required for ${method})`);
25592
+ async listTasks(params, options) {
25593
+ return this.request({ method: "tasks/list", params }, ListTasksResultSchema, options);
25305
25594
  }
25306
- switch (method) {
25307
- case "sampling/createMessage":
25308
- if (!requests.sampling?.createMessage) {
25309
- throw new Error(`${entityName} does not support task creation for sampling/createMessage (required for ${method})`);
25310
- }
25311
- break;
25312
- case "elicitation/create":
25313
- if (!requests.elicitation?.create) {
25314
- throw new Error(`${entityName} does not support task creation for elicitation/create (required for ${method})`);
25315
- }
25316
- break;
25317
- default:
25318
- break;
25595
+ async cancelTask(params, options) {
25596
+ return this.request({ method: "tasks/cancel", params }, CancelTaskResultSchema, options);
25319
25597
  }
25320
- }
25321
-
25322
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
25323
- class Server extends Protocol {
25324
- constructor(_serverInfo, options) {
25325
- super(options);
25326
- this._serverInfo = _serverInfo;
25327
- this._loggingLevels = new Map;
25328
- this.LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index) => [level, index]));
25329
- this.isMessageIgnored = (level, sessionId) => {
25330
- const currentLevel = this._loggingLevels.get(sessionId);
25331
- return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
25332
- };
25333
- this._capabilities = options?.capabilities ?? {};
25334
- this._instructions = options?.instructions;
25335
- this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new AjvJsonSchemaValidator;
25336
- this.setRequestHandler(InitializeRequestSchema, (request) => this._oninitialize(request));
25337
- this.setNotificationHandler(InitializedNotificationSchema, () => this.oninitialized?.());
25338
- if (this._capabilities.logging) {
25339
- this.setRequestHandler(SetLevelRequestSchema, async (request, extra) => {
25340
- const transportSessionId = extra.sessionId || extra.requestInfo?.headers["mcp-session-id"] || undefined;
25341
- const { level } = request.params;
25342
- const parseResult = LoggingLevelSchema.safeParse(level);
25343
- if (parseResult.success) {
25344
- this._loggingLevels.set(transportSessionId, parseResult.data);
25345
- }
25346
- return {};
25347
- });
25598
+ async notification(notification, options) {
25599
+ if (!this._transport) {
25600
+ throw new Error("Not connected");
25348
25601
  }
25349
- }
25350
- get experimental() {
25351
- if (!this._experimental) {
25352
- this._experimental = {
25353
- tasks: new ExperimentalServerTasks(this)
25602
+ this.assertNotificationCapability(notification.method);
25603
+ const relatedTaskId = options?.relatedTask?.taskId;
25604
+ if (relatedTaskId) {
25605
+ const jsonrpcNotification = {
25606
+ ...notification,
25607
+ jsonrpc: "2.0",
25608
+ params: {
25609
+ ...notification.params,
25610
+ _meta: {
25611
+ ...notification.params?._meta || {},
25612
+ [RELATED_TASK_META_KEY]: options.relatedTask
25613
+ }
25614
+ }
25354
25615
  };
25616
+ await this._enqueueTaskMessage(relatedTaskId, {
25617
+ type: "notification",
25618
+ message: jsonrpcNotification,
25619
+ timestamp: Date.now()
25620
+ });
25621
+ return;
25355
25622
  }
25356
- return this._experimental;
25357
- }
25358
- registerCapabilities(capabilities) {
25359
- if (this.transport) {
25360
- throw new Error("Cannot register capabilities after connecting to transport");
25361
- }
25362
- this._capabilities = mergeCapabilities(this._capabilities, capabilities);
25363
- }
25364
- setRequestHandler(requestSchema, handler) {
25365
- const shape = getObjectShape(requestSchema);
25366
- const methodSchema = shape?.method;
25367
- if (!methodSchema) {
25368
- throw new Error("Schema is missing a method literal");
25369
- }
25370
- const methodValue = getLiteralValue(methodSchema);
25371
- if (typeof methodValue !== "string") {
25372
- throw new Error("Schema method literal must be a string");
25373
- }
25374
- const method = methodValue;
25375
- if (method === "tools/call") {
25376
- const wrappedHandler = async (request, extra) => {
25377
- const validatedRequest = safeParse4(CallToolRequestSchema, request);
25378
- if (!validatedRequest.success) {
25379
- const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
25380
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage}`);
25623
+ const debouncedMethods = this._options?.debouncedNotificationMethods ?? [];
25624
+ const canDebounce = debouncedMethods.includes(notification.method) && !notification.params && !options?.relatedRequestId && !options?.relatedTask;
25625
+ if (canDebounce) {
25626
+ if (this._pendingDebouncedNotifications.has(notification.method)) {
25627
+ return;
25628
+ }
25629
+ this._pendingDebouncedNotifications.add(notification.method);
25630
+ Promise.resolve().then(() => {
25631
+ this._pendingDebouncedNotifications.delete(notification.method);
25632
+ if (!this._transport) {
25633
+ return;
25381
25634
  }
25382
- const { params } = validatedRequest.data;
25383
- const result = await Promise.resolve(handler(request, extra));
25384
- if (params.task) {
25385
- const taskValidationResult = safeParse4(CreateTaskResultSchema, result);
25386
- if (!taskValidationResult.success) {
25387
- const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
25388
- throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
25389
- }
25390
- return taskValidationResult.data;
25635
+ let jsonrpcNotification = {
25636
+ ...notification,
25637
+ jsonrpc: "2.0"
25638
+ };
25639
+ if (options?.relatedTask) {
25640
+ jsonrpcNotification = {
25641
+ ...jsonrpcNotification,
25642
+ params: {
25643
+ ...jsonrpcNotification.params,
25644
+ _meta: {
25645
+ ...jsonrpcNotification.params?._meta || {},
25646
+ [RELATED_TASK_META_KEY]: options.relatedTask
25647
+ }
25648
+ }
25649
+ };
25391
25650
  }
25392
- const validationResult = safeParse4(CallToolResultSchema, result);
25393
- if (!validationResult.success) {
25394
- const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
25395
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage}`);
25651
+ this._transport?.send(jsonrpcNotification, options).catch((error) => this._onerror(error));
25652
+ });
25653
+ return;
25654
+ }
25655
+ let jsonrpcNotification = {
25656
+ ...notification,
25657
+ jsonrpc: "2.0"
25658
+ };
25659
+ if (options?.relatedTask) {
25660
+ jsonrpcNotification = {
25661
+ ...jsonrpcNotification,
25662
+ params: {
25663
+ ...jsonrpcNotification.params,
25664
+ _meta: {
25665
+ ...jsonrpcNotification.params?._meta || {},
25666
+ [RELATED_TASK_META_KEY]: options.relatedTask
25667
+ }
25396
25668
  }
25397
- return validationResult.data;
25398
25669
  };
25399
- return super.setRequestHandler(requestSchema, wrappedHandler);
25400
25670
  }
25401
- return super.setRequestHandler(requestSchema, handler);
25671
+ await this._transport.send(jsonrpcNotification, options);
25402
25672
  }
25403
- assertCapabilityForMethod(method) {
25404
- switch (method) {
25405
- case "sampling/createMessage":
25406
- if (!this._clientCapabilities?.sampling) {
25407
- throw new Error(`Client does not support sampling (required for ${method})`);
25408
- }
25409
- break;
25410
- case "elicitation/create":
25411
- if (!this._clientCapabilities?.elicitation) {
25412
- throw new Error(`Client does not support elicitation (required for ${method})`);
25413
- }
25414
- break;
25415
- case "roots/list":
25416
- if (!this._clientCapabilities?.roots) {
25417
- throw new Error(`Client does not support listing roots (required for ${method})`);
25418
- }
25419
- break;
25420
- case "ping":
25421
- break;
25673
+ setRequestHandler(requestSchema, handler) {
25674
+ const method = getMethodLiteral(requestSchema);
25675
+ this.assertRequestHandlerCapability(method);
25676
+ this._requestHandlers.set(method, (request, extra) => {
25677
+ const parsed = parseWithCompat(requestSchema, request);
25678
+ return Promise.resolve(handler(parsed, extra));
25679
+ });
25680
+ }
25681
+ removeRequestHandler(method) {
25682
+ this._requestHandlers.delete(method);
25683
+ }
25684
+ assertCanSetRequestHandler(method) {
25685
+ if (this._requestHandlers.has(method)) {
25686
+ throw new Error(`A request handler for ${method} already exists, which would be overridden`);
25422
25687
  }
25423
25688
  }
25424
- assertNotificationCapability(method) {
25425
- switch (method) {
25426
- case "notifications/message":
25427
- if (!this._capabilities.logging) {
25428
- throw new Error(`Server does not support logging (required for ${method})`);
25429
- }
25430
- break;
25431
- case "notifications/resources/updated":
25432
- case "notifications/resources/list_changed":
25433
- if (!this._capabilities.resources) {
25434
- throw new Error(`Server does not support notifying about resources (required for ${method})`);
25435
- }
25436
- break;
25437
- case "notifications/tools/list_changed":
25438
- if (!this._capabilities.tools) {
25439
- throw new Error(`Server does not support notifying of tool list changes (required for ${method})`);
25440
- }
25441
- break;
25442
- case "notifications/prompts/list_changed":
25443
- if (!this._capabilities.prompts) {
25444
- throw new Error(`Server does not support notifying of prompt list changes (required for ${method})`);
25445
- }
25446
- break;
25447
- case "notifications/elicitation/complete":
25448
- if (!this._clientCapabilities?.elicitation?.url) {
25449
- throw new Error(`Client does not support URL elicitation (required for ${method})`);
25689
+ setNotificationHandler(notificationSchema, handler) {
25690
+ const method = getMethodLiteral(notificationSchema);
25691
+ this._notificationHandlers.set(method, (notification) => {
25692
+ const parsed = parseWithCompat(notificationSchema, notification);
25693
+ return Promise.resolve(handler(parsed));
25694
+ });
25695
+ }
25696
+ removeNotificationHandler(method) {
25697
+ this._notificationHandlers.delete(method);
25698
+ }
25699
+ _cleanupTaskProgressHandler(taskId) {
25700
+ const progressToken = this._taskProgressTokens.get(taskId);
25701
+ if (progressToken !== undefined) {
25702
+ this._progressHandlers.delete(progressToken);
25703
+ this._taskProgressTokens.delete(taskId);
25704
+ }
25705
+ }
25706
+ async _enqueueTaskMessage(taskId, message, sessionId) {
25707
+ if (!this._taskStore || !this._taskMessageQueue) {
25708
+ throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
25709
+ }
25710
+ const maxQueueSize = this._options?.maxTaskQueueSize;
25711
+ await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
25712
+ }
25713
+ async _clearTaskQueue(taskId, sessionId) {
25714
+ if (this._taskMessageQueue) {
25715
+ const messages = await this._taskMessageQueue.dequeueAll(taskId, sessionId);
25716
+ for (const message of messages) {
25717
+ if (message.type === "request" && isJSONRPCRequest(message.message)) {
25718
+ const requestId = message.message.id;
25719
+ const resolver = this._requestResolvers.get(requestId);
25720
+ if (resolver) {
25721
+ resolver(new McpError(ErrorCode.InternalError, "Task cancelled or completed"));
25722
+ this._requestResolvers.delete(requestId);
25723
+ } else {
25724
+ this._onerror(new Error(`Resolver missing for request ${requestId} during task ${taskId} cleanup`));
25725
+ }
25450
25726
  }
25451
- break;
25452
- case "notifications/cancelled":
25453
- break;
25454
- case "notifications/progress":
25455
- break;
25727
+ }
25456
25728
  }
25457
25729
  }
25458
- assertRequestHandlerCapability(method) {
25459
- if (!this._capabilities) {
25460
- return;
25730
+ async _waitForTaskUpdate(taskId, signal) {
25731
+ let interval = this._options?.defaultTaskPollInterval ?? 1000;
25732
+ try {
25733
+ const task = await this._taskStore?.getTask(taskId);
25734
+ if (task?.pollInterval) {
25735
+ interval = task.pollInterval;
25736
+ }
25737
+ } catch {}
25738
+ return new Promise((resolve, reject) => {
25739
+ if (signal.aborted) {
25740
+ reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
25741
+ return;
25742
+ }
25743
+ const timeoutId = setTimeout(resolve, interval);
25744
+ signal.addEventListener("abort", () => {
25745
+ clearTimeout(timeoutId);
25746
+ reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
25747
+ }, { once: true });
25748
+ });
25749
+ }
25750
+ requestTaskStore(request, sessionId) {
25751
+ const taskStore = this._taskStore;
25752
+ if (!taskStore) {
25753
+ throw new Error("No task store configured");
25461
25754
  }
25462
- switch (method) {
25463
- case "completion/complete":
25464
- if (!this._capabilities.completions) {
25465
- throw new Error(`Server does not support completions (required for ${method})`);
25755
+ return {
25756
+ createTask: async (taskParams) => {
25757
+ if (!request) {
25758
+ throw new Error("No request provided");
25466
25759
  }
25467
- break;
25468
- case "logging/setLevel":
25469
- if (!this._capabilities.logging) {
25470
- throw new Error(`Server does not support logging (required for ${method})`);
25760
+ return await taskStore.createTask(taskParams, request.id, {
25761
+ method: request.method,
25762
+ params: request.params
25763
+ }, sessionId);
25764
+ },
25765
+ getTask: async (taskId) => {
25766
+ const task = await taskStore.getTask(taskId, sessionId);
25767
+ if (!task) {
25768
+ throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
25471
25769
  }
25472
- break;
25473
- case "prompts/get":
25474
- case "prompts/list":
25475
- if (!this._capabilities.prompts) {
25476
- throw new Error(`Server does not support prompts (required for ${method})`);
25770
+ return task;
25771
+ },
25772
+ storeTaskResult: async (taskId, status, result) => {
25773
+ await taskStore.storeTaskResult(taskId, status, result, sessionId);
25774
+ const task = await taskStore.getTask(taskId, sessionId);
25775
+ if (task) {
25776
+ const notification = TaskStatusNotificationSchema.parse({
25777
+ method: "notifications/tasks/status",
25778
+ params: task
25779
+ });
25780
+ await this.notification(notification);
25781
+ if (isTerminal(task.status)) {
25782
+ this._cleanupTaskProgressHandler(taskId);
25783
+ }
25477
25784
  }
25478
- break;
25479
- case "resources/list":
25480
- case "resources/templates/list":
25481
- case "resources/read":
25482
- if (!this._capabilities.resources) {
25483
- throw new Error(`Server does not support resources (required for ${method})`);
25785
+ },
25786
+ getTaskResult: (taskId) => {
25787
+ return taskStore.getTaskResult(taskId, sessionId);
25788
+ },
25789
+ updateTaskStatus: async (taskId, status, statusMessage) => {
25790
+ const task = await taskStore.getTask(taskId, sessionId);
25791
+ if (!task) {
25792
+ throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
25484
25793
  }
25485
- break;
25486
- case "tools/call":
25487
- case "tools/list":
25488
- if (!this._capabilities.tools) {
25489
- throw new Error(`Server does not support tools (required for ${method})`);
25794
+ if (isTerminal(task.status)) {
25795
+ throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
25490
25796
  }
25491
- break;
25492
- case "tasks/get":
25493
- case "tasks/list":
25494
- case "tasks/result":
25495
- case "tasks/cancel":
25496
- if (!this._capabilities.tasks) {
25497
- throw new Error(`Server does not support tasks capability (required for ${method})`);
25797
+ await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId);
25798
+ const updatedTask = await taskStore.getTask(taskId, sessionId);
25799
+ if (updatedTask) {
25800
+ const notification = TaskStatusNotificationSchema.parse({
25801
+ method: "notifications/tasks/status",
25802
+ params: updatedTask
25803
+ });
25804
+ await this.notification(notification);
25805
+ if (isTerminal(updatedTask.status)) {
25806
+ this._cleanupTaskProgressHandler(taskId);
25807
+ }
25498
25808
  }
25499
- break;
25500
- case "ping":
25501
- case "initialize":
25502
- break;
25503
- }
25504
- }
25505
- assertTaskCapability(method) {
25506
- assertClientRequestTaskCapability(this._clientCapabilities?.tasks?.requests, method, "Client");
25507
- }
25508
- assertTaskHandlerCapability(method) {
25509
- if (!this._capabilities) {
25510
- return;
25511
- }
25512
- assertToolsCallTaskCapability(this._capabilities.tasks?.requests, method, "Server");
25513
- }
25514
- async _oninitialize(request) {
25515
- const requestedVersion = request.params.protocolVersion;
25516
- this._clientCapabilities = request.params.capabilities;
25517
- this._clientVersion = request.params.clientInfo;
25518
- const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : LATEST_PROTOCOL_VERSION;
25519
- return {
25520
- protocolVersion,
25521
- capabilities: this.getCapabilities(),
25522
- serverInfo: this._serverInfo,
25523
- ...this._instructions && { instructions: this._instructions }
25809
+ },
25810
+ listTasks: (cursor) => {
25811
+ return taskStore.listTasks(cursor, sessionId);
25812
+ }
25524
25813
  };
25525
25814
  }
25526
- getClientCapabilities() {
25527
- return this._clientCapabilities;
25815
+ }
25816
+ function isPlainObject3(value) {
25817
+ return value !== null && typeof value === "object" && !Array.isArray(value);
25818
+ }
25819
+ function mergeCapabilities(base, additional) {
25820
+ const result = { ...base };
25821
+ for (const key in additional) {
25822
+ const k = key;
25823
+ const addValue = additional[k];
25824
+ if (addValue === undefined)
25825
+ continue;
25826
+ const baseValue = result[k];
25827
+ if (isPlainObject3(baseValue) && isPlainObject3(addValue)) {
25828
+ result[k] = { ...baseValue, ...addValue };
25829
+ } else {
25830
+ result[k] = addValue;
25831
+ }
25832
+ }
25833
+ return result;
25834
+ }
25835
+
25836
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
25837
+ var import_ajv = __toESM(require_ajv(), 1);
25838
+ var import_ajv_formats = __toESM(require_dist(), 1);
25839
+ function createDefaultAjvInstance() {
25840
+ const ajv = new import_ajv.default({
25841
+ strict: false,
25842
+ validateFormats: true,
25843
+ validateSchema: false,
25844
+ allErrors: true
25845
+ });
25846
+ const addFormats = import_ajv_formats.default;
25847
+ addFormats(ajv);
25848
+ return ajv;
25849
+ }
25850
+
25851
+ class AjvJsonSchemaValidator {
25852
+ constructor(ajv) {
25853
+ this._ajv = ajv ?? createDefaultAjvInstance();
25528
25854
  }
25529
- getClientVersion() {
25530
- return this._clientVersion;
25855
+ getValidator(schema) {
25856
+ const ajvValidator = "$id" in schema && typeof schema.$id === "string" ? this._ajv.getSchema(schema.$id) ?? this._ajv.compile(schema) : this._ajv.compile(schema);
25857
+ return (input) => {
25858
+ const valid = ajvValidator(input);
25859
+ if (valid) {
25860
+ return {
25861
+ valid: true,
25862
+ data: input,
25863
+ errorMessage: undefined
25864
+ };
25865
+ } else {
25866
+ return {
25867
+ valid: false,
25868
+ data: undefined,
25869
+ errorMessage: this._ajv.errorsText(ajvValidator.errors)
25870
+ };
25871
+ }
25872
+ };
25531
25873
  }
25532
- getCapabilities() {
25533
- return this._capabilities;
25874
+ }
25875
+
25876
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
25877
+ class ExperimentalServerTasks {
25878
+ constructor(_server) {
25879
+ this._server = _server;
25534
25880
  }
25535
- async ping() {
25536
- return this.request({ method: "ping" }, EmptyResultSchema);
25881
+ requestStream(request, resultSchema, options) {
25882
+ return this._server.requestStream(request, resultSchema, options);
25537
25883
  }
25538
- async createMessage(params, options) {
25539
- if (params.tools || params.toolChoice) {
25540
- if (!this._clientCapabilities?.sampling?.tools) {
25541
- throw new Error("Client does not support sampling tools capability.");
25542
- }
25884
+ createMessageStream(params, options) {
25885
+ const clientCapabilities = this._server.getClientCapabilities();
25886
+ if ((params.tools || params.toolChoice) && !clientCapabilities?.sampling?.tools) {
25887
+ throw new Error("Client does not support sampling tools capability.");
25543
25888
  }
25544
25889
  if (params.messages.length > 0) {
25545
25890
  const lastMessage = params.messages[params.messages.length - 1];
@@ -25564,807 +25909,572 @@ class Server extends Protocol {
25564
25909
  }
25565
25910
  }
25566
25911
  }
25567
- if (params.tools) {
25568
- return this.request({ method: "sampling/createMessage", params }, CreateMessageResultWithToolsSchema, options);
25569
- }
25570
- return this.request({ method: "sampling/createMessage", params }, CreateMessageResultSchema, options);
25912
+ return this.requestStream({
25913
+ method: "sampling/createMessage",
25914
+ params
25915
+ }, CreateMessageResultSchema, options);
25571
25916
  }
25572
- async elicitInput(params, options) {
25917
+ elicitInputStream(params, options) {
25918
+ const clientCapabilities = this._server.getClientCapabilities();
25573
25919
  const mode = params.mode ?? "form";
25574
25920
  switch (mode) {
25575
25921
  case "url": {
25576
- if (!this._clientCapabilities?.elicitation?.url) {
25922
+ if (!clientCapabilities?.elicitation?.url) {
25577
25923
  throw new Error("Client does not support url elicitation.");
25578
25924
  }
25579
- const urlParams = params;
25580
- return this.request({ method: "elicitation/create", params: urlParams }, ElicitResultSchema, options);
25925
+ break;
25581
25926
  }
25582
25927
  case "form": {
25583
- if (!this._clientCapabilities?.elicitation?.form) {
25928
+ if (!clientCapabilities?.elicitation?.form) {
25584
25929
  throw new Error("Client does not support form elicitation.");
25585
25930
  }
25586
- const formParams = params.mode === "form" ? params : { ...params, mode: "form" };
25587
- const result = await this.request({ method: "elicitation/create", params: formParams }, ElicitResultSchema, options);
25588
- if (result.action === "accept" && result.content && formParams.requestedSchema) {
25589
- try {
25590
- const validator = this._jsonSchemaValidator.getValidator(formParams.requestedSchema);
25591
- const validationResult = validator(result.content);
25592
- if (!validationResult.valid) {
25593
- throw new McpError(ErrorCode.InvalidParams, `Elicitation response content does not match requested schema: ${validationResult.errorMessage}`);
25594
- }
25595
- } catch (error) {
25596
- if (error instanceof McpError) {
25597
- throw error;
25598
- }
25599
- throw new McpError(ErrorCode.InternalError, `Error validating elicitation response: ${error instanceof Error ? error.message : String(error)}`);
25600
- }
25601
- }
25602
- return result;
25603
- }
25604
- }
25605
- }
25606
- createElicitationCompletionNotifier(elicitationId, options) {
25607
- if (!this._clientCapabilities?.elicitation?.url) {
25608
- throw new Error("Client does not support URL elicitation (required for notifications/elicitation/complete)");
25609
- }
25610
- return () => this.notification({
25611
- method: "notifications/elicitation/complete",
25612
- params: {
25613
- elicitationId
25614
- }
25615
- }, options);
25616
- }
25617
- async listRoots(params, options) {
25618
- return this.request({ method: "roots/list", params }, ListRootsResultSchema, options);
25619
- }
25620
- async sendLoggingMessage(params, sessionId) {
25621
- if (this._capabilities.logging) {
25622
- if (!this.isMessageIgnored(params.level, sessionId)) {
25623
- return this.notification({ method: "notifications/message", params });
25931
+ break;
25624
25932
  }
25625
25933
  }
25934
+ const normalizedParams = mode === "form" && params.mode === undefined ? { ...params, mode: "form" } : params;
25935
+ return this.requestStream({
25936
+ method: "elicitation/create",
25937
+ params: normalizedParams
25938
+ }, ElicitResultSchema, options);
25626
25939
  }
25627
- async sendResourceUpdated(params) {
25628
- return this.notification({
25629
- method: "notifications/resources/updated",
25630
- params
25631
- });
25632
- }
25633
- async sendResourceListChanged() {
25634
- return this.notification({
25635
- method: "notifications/resources/list_changed"
25636
- });
25637
- }
25638
- async sendToolListChanged() {
25639
- return this.notification({ method: "notifications/tools/list_changed" });
25640
- }
25641
- async sendPromptListChanged() {
25642
- return this.notification({ method: "notifications/prompts/list_changed" });
25643
- }
25644
- }
25645
-
25646
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
25647
- import process3 from "node:process";
25648
-
25649
- // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
25650
- var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
25651
-
25652
- class ReadBuffer {
25653
- constructor(options) {
25654
- this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
25940
+ async getTask(taskId, options) {
25941
+ return this._server.getTask({ taskId }, options);
25655
25942
  }
25656
- append(chunk) {
25657
- const newSize = (this._buffer?.length ?? 0) + chunk.length;
25658
- if (newSize > this._maxBufferSize) {
25659
- this.clear();
25660
- throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
25661
- }
25662
- this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
25943
+ async getTaskResult(taskId, resultSchema, options) {
25944
+ return this._server.getTaskResult({ taskId }, resultSchema, options);
25663
25945
  }
25664
- readMessage() {
25665
- if (!this._buffer) {
25666
- return null;
25667
- }
25668
- const index = this._buffer.indexOf(`
25669
- `);
25670
- if (index === -1) {
25671
- return null;
25672
- }
25673
- const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
25674
- this._buffer = this._buffer.subarray(index + 1);
25675
- return deserializeMessage(line);
25946
+ async listTasks(cursor, options) {
25947
+ return this._server.listTasks(cursor ? { cursor } : undefined, options);
25676
25948
  }
25677
- clear() {
25678
- this._buffer = undefined;
25949
+ async cancelTask(taskId, options) {
25950
+ return this._server.cancelTask({ taskId }, options);
25679
25951
  }
25680
25952
  }
25681
- function deserializeMessage(line) {
25682
- return JSONRPCMessageSchema.parse(JSON.parse(line));
25683
- }
25684
- function serializeMessage(message) {
25685
- return JSON.stringify(message) + `
25686
- `;
25687
- }
25688
25953
 
25689
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
25690
- class StdioServerTransport {
25691
- constructor(_stdin = process3.stdin, _stdout = process3.stdout, options) {
25692
- this._stdin = _stdin;
25693
- this._stdout = _stdout;
25694
- this._started = false;
25695
- this._ondata = (chunk) => {
25696
- try {
25697
- this._readBuffer.append(chunk);
25698
- this.processReadBuffer();
25699
- } catch (error) {
25700
- this.onerror?.(error);
25701
- this.close().catch(() => {});
25702
- }
25703
- };
25704
- this._onerror = (error) => {
25705
- this.onerror?.(error);
25706
- };
25707
- this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
25708
- }
25709
- async start() {
25710
- if (this._started) {
25711
- throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
25712
- }
25713
- this._started = true;
25714
- this._stdin.on("data", this._ondata);
25715
- this._stdin.on("error", this._onerror);
25716
- }
25717
- processReadBuffer() {
25718
- while (true) {
25719
- try {
25720
- const message = this._readBuffer.readMessage();
25721
- if (message === null) {
25722
- break;
25723
- }
25724
- this.onmessage?.(message);
25725
- } catch (error) {
25726
- this.onerror?.(error);
25727
- }
25728
- }
25729
- }
25730
- async close() {
25731
- this._stdin.off("data", this._ondata);
25732
- this._stdin.off("error", this._onerror);
25733
- const remainingDataListeners = this._stdin.listenerCount("data");
25734
- if (remainingDataListeners === 0) {
25735
- this._stdin.pause();
25736
- }
25737
- this._readBuffer.clear();
25738
- this.onclose?.();
25954
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
25955
+ function assertToolsCallTaskCapability(requests, method, entityName) {
25956
+ if (!requests) {
25957
+ throw new Error(`${entityName} does not support task creation (required for ${method})`);
25739
25958
  }
25740
- send(message) {
25741
- return new Promise((resolve) => {
25742
- const json = serializeMessage(message);
25743
- if (this._stdout.write(json)) {
25744
- resolve();
25745
- } else {
25746
- this._stdout.once("drain", resolve);
25959
+ switch (method) {
25960
+ case "tools/call":
25961
+ if (!requests.tools?.call) {
25962
+ throw new Error(`${entityName} does not support task creation for tools/call (required for ${method})`);
25747
25963
  }
25748
- });
25964
+ break;
25965
+ default:
25966
+ break;
25749
25967
  }
25750
25968
  }
25751
-
25752
- // packages/workit-mcp/src/server.ts
25753
- import { fileURLToPath as fileURLToPath2 } from "node:url";
25754
-
25755
- // packages/workit-core/src/core/boundary.ts
25756
- import { statSync } from "node:fs";
25757
- var EVENT = {
25758
- initialization: "initialization",
25759
- provenance: "provenance",
25760
- assets: "assets",
25761
- configurationSource: "configuration_source",
25762
- hooks: "hooks",
25763
- mcpConnection: "mcp_connection",
25764
- migration: "migration",
25765
- installSteps: "install_steps",
25766
- uncaughtFailure: "uncaught_failure",
25767
- toolsFailed: "tools_failed",
25768
- doctor: "doctor"
25769
- };
25770
- var errorDetail = (err) => {
25771
- if (err instanceof Error) {
25772
- return { error_name: err.name, error: err.message };
25969
+ function assertClientRequestTaskCapability(requests, method, entityName) {
25970
+ if (!requests) {
25971
+ throw new Error(`${entityName} does not support task creation (required for ${method})`);
25773
25972
  }
25774
- return { error_name: "unknown", error: String(err) };
25775
- };
25776
- var markSourcesLoaded = (files, loadedAt = Date.now()) => ({
25777
- loadedAt,
25778
- files: [...files]
25779
- });
25780
- var changedSourcesSinceLoad = (marker) => {
25781
- const changed = [];
25782
- for (const file of marker.files) {
25783
- try {
25784
- if (statSync(file).mtimeMs > marker.loadedAt)
25785
- changed.push(file);
25786
- } catch {}
25973
+ switch (method) {
25974
+ case "sampling/createMessage":
25975
+ if (!requests.sampling?.createMessage) {
25976
+ throw new Error(`${entityName} does not support task creation for sampling/createMessage (required for ${method})`);
25977
+ }
25978
+ break;
25979
+ case "elicitation/create":
25980
+ if (!requests.elicitation?.create) {
25981
+ throw new Error(`${entityName} does not support task creation for elicitation/create (required for ${method})`);
25982
+ }
25983
+ break;
25984
+ default:
25985
+ break;
25787
25986
  }
25788
- return changed;
25789
- };
25790
-
25791
- // packages/workit-core/src/core/logger.ts
25792
- import os from "node:os";
25793
- var REDACTED = "[REDACTED]";
25794
- var SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
25795
- var KEY_EQ_VALUE = /\b([A-Za-z0-9_-]*(?:token|secret|password|passwd|apikey|api[_-]?key|authorization|credential|bearer)[A-Za-z0-9_-]*)([:=]\s*).+/gi;
25796
- var URL_QUERY = /(https?:\/\/[^?#\s]+)\?[^#\s]*/g;
25797
- var SENSITIVE_WORDS = new Set([
25798
- "token",
25799
- "secret",
25800
- "password",
25801
- "passwd",
25802
- "authorization",
25803
- "credential",
25804
- "credentials",
25805
- "cookie",
25806
- "api",
25807
- "apikey",
25808
- "apitoken",
25809
- "accesstoken",
25810
- "clientsecret",
25811
- "refreshtoken",
25812
- "key",
25813
- "bearer",
25814
- "prompt",
25815
- "message",
25816
- "messages",
25817
- "content",
25818
- "body",
25819
- "args",
25820
- "argument",
25821
- "result",
25822
- "results",
25823
- "output",
25824
- "issue",
25825
- "description",
25826
- "summary",
25827
- "payload",
25828
- "script",
25829
- "command",
25830
- "text",
25831
- "query"
25832
- ]);
25833
- var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
25834
- var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25835
- var homePattern = new RegExp(`${escapeRegExp(os.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
25836
- var applyPatterns = (value) => {
25837
- let out = value;
25838
- out = out.replace(SECRET_VALUE, REDACTED);
25839
- out = out.replace(KEY_EQ_VALUE, "$1$2[REDACTED]");
25840
- out = out.replace(URL_QUERY, "$1?[REDACTED]");
25841
- out = out.replace(homePattern, "~");
25842
- if (process.platform === "win32")
25843
- out = out.replace(/\\/g, "/");
25844
- return out;
25845
- };
25846
- var redactSecrets = (value) => applyPatterns(value);
25987
+ }
25847
25988
 
25848
- // packages/workit-core/src/core/git.ts
25849
- import { execFileSync } from "node:child_process";
25850
- var run2 = (cwd, args) => {
25851
- try {
25852
- const stdout = execFileSync("git", args, {
25853
- cwd,
25854
- encoding: "utf8",
25855
- stdio: ["pipe", "pipe", "pipe"]
25856
- });
25857
- return { stdout: stdout.trimEnd(), stderr: "", exitCode: 0 };
25858
- } catch (error) {
25859
- const e = error;
25860
- return {
25861
- stdout: (e.stdout ?? "").trimEnd(),
25862
- stderr: (e.stderr ?? "").trimEnd(),
25863
- exitCode: e.status ?? 1
25989
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
25990
+ class Server extends Protocol {
25991
+ constructor(_serverInfo, options) {
25992
+ super(options);
25993
+ this._serverInfo = _serverInfo;
25994
+ this._loggingLevels = new Map;
25995
+ this.LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index) => [level, index]));
25996
+ this.isMessageIgnored = (level, sessionId) => {
25997
+ const currentLevel = this._loggingLevels.get(sessionId);
25998
+ return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
25864
25999
  };
25865
- }
25866
- };
25867
- var gitContext = (workspaceRoot, paths = []) => {
25868
- const cwd = workspaceRoot;
25869
- let failure;
25870
- const runHere = (args) => {
25871
- const result = run2(cwd, args);
25872
- if (!failure && result.exitCode !== 0) {
25873
- failure = { stderr: result.stderr, exitCode: result.exitCode };
25874
- }
25875
- return result.stdout;
25876
- };
25877
- const branch = runHere(["rev-parse", "--abbrev-ref", "HEAD"]) || "unknown";
25878
- const status_short = runHere(["status", "--porcelain"]);
25879
- const staged = [];
25880
- const unstaged = [];
25881
- const untracked = [];
25882
- for (const line of status_short.split(`
25883
- `).filter(Boolean)) {
25884
- const code = line.slice(0, 2);
25885
- const file = line.slice(3);
25886
- if (code === "??") {
25887
- untracked.push(file);
25888
- } else {
25889
- if (code[0] !== " " && code[0] !== "?")
25890
- staged.push(file);
25891
- if (code[1] !== " " && code[1] !== "?")
25892
- unstaged.push(file);
26000
+ this._capabilities = options?.capabilities ?? {};
26001
+ this._instructions = options?.instructions;
26002
+ this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new AjvJsonSchemaValidator;
26003
+ this.setRequestHandler(InitializeRequestSchema, (request) => this._oninitialize(request));
26004
+ this.setNotificationHandler(InitializedNotificationSchema, () => this.oninitialized?.());
26005
+ if (this._capabilities.logging) {
26006
+ this.setRequestHandler(SetLevelRequestSchema, async (request, extra) => {
26007
+ const transportSessionId = extra.sessionId || extra.requestInfo?.headers["mcp-session-id"] || undefined;
26008
+ const { level } = request.params;
26009
+ const parseResult = LoggingLevelSchema.safeParse(level);
26010
+ if (parseResult.success) {
26011
+ this._loggingLevels.set(transportSessionId, parseResult.data);
26012
+ }
26013
+ return {};
26014
+ });
25893
26015
  }
25894
26016
  }
25895
- const pathArgs = paths.length ? ["--", ...paths] : [];
25896
- const diff_stat = runHere(["diff", "--stat", ...pathArgs]);
25897
- const cached_stat = runHere(["diff", "--cached", "--stat", ...pathArgs]);
25898
- const stagedSet = new Set(staged);
25899
- const unstagedSet = new Set(unstaged);
25900
- const partial_staged = [...stagedSet].filter((f) => unstagedSet.has(f));
25901
- return {
25902
- workspace_root: cwd,
25903
- branch,
25904
- status_short,
25905
- staged,
25906
- unstaged,
25907
- untracked,
25908
- diff_stat: [diff_stat, cached_stat].filter(Boolean).join(`
25909
- `),
25910
- partial_staged: partial_staged.length > 0,
25911
- partial_staged_files: partial_staged,
25912
- ...failure ? { stderr: failure.stderr, exitCode: failure.exitCode } : {}
25913
- };
25914
- };
25915
-
25916
- // packages/workit-core/src/core/config.ts
25917
- import {
25918
- copyFileSync,
25919
- cpSync,
25920
- existsSync as existsSync5,
25921
- mkdirSync as mkdirSync2,
25922
- readFileSync as readFileSync4,
25923
- readdirSync as readdirSync3,
25924
- writeFileSync as writeFileSync2
25925
- } from "node:fs";
25926
- import os2 from "node:os";
25927
- import path8 from "node:path";
25928
- var diagnosticLogger;
25929
- var PRESETS = {
25930
- gitflow: {
25931
- allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
25932
- protected: ["main", "develop", "master", "prod", "production"]
25933
- },
25934
- "github-flow": { allowed: ["*"], protected: ["main"] },
25935
- "trunk-based": { allowed: ["*"], protected: ["main"] },
25936
- custom: { allowed: [], protected: [] }
25937
- };
25938
- var mergePreset = (preset, input = {}, current = {
25939
- branchPolicy: { preset, allowed: [], protected: [] }
25940
- }) => {
25941
- const defs = PRESETS[preset];
25942
- return {
25943
- preset,
25944
- allowed: preset === "custom" ? input.allowed ?? current.branchPolicy.allowed : [...defs.allowed],
25945
- protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
25946
- };
25947
- };
25948
- var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path8.join(process.env.XDG_CONFIG_HOME || path8.join(os2.homedir(), ".config"), "workit");
25949
- var migratedDir = null;
25950
- var migrationFailed = false;
25951
- var ensureConfigDir = (dir = resolveConfigDir()) => {
25952
- if (migratedDir === dir)
25953
- return dir;
25954
- if (process.env.WORKFLOW_TOOLKIT_CONFIG || process.env.WORKFLOW_TOOLKIT_CONFIG_DIR) {
25955
- migratedDir = dir;
25956
- return dir;
26017
+ get experimental() {
26018
+ if (!this._experimental) {
26019
+ this._experimental = {
26020
+ tasks: new ExperimentalServerTasks(this)
26021
+ };
26022
+ }
26023
+ return this._experimental;
25957
26024
  }
25958
- const legacy = path8.join(process.env.XDG_CONFIG_HOME || path8.join(os2.homedir(), ".config"), "workflow-toolkit");
25959
- if (!existsSync5(legacy)) {
25960
- migratedDir = dir;
25961
- return dir;
26025
+ registerCapabilities(capabilities) {
26026
+ if (this.transport) {
26027
+ throw new Error("Cannot register capabilities after connecting to transport");
26028
+ }
26029
+ this._capabilities = mergeCapabilities(this._capabilities, capabilities);
25962
26030
  }
25963
- if (!migrationFailed && existsSync5(dir)) {
25964
- migratedDir = dir;
25965
- return dir;
26031
+ setRequestHandler(requestSchema, handler) {
26032
+ const shape = getObjectShape(requestSchema);
26033
+ const methodSchema = shape?.method;
26034
+ if (!methodSchema) {
26035
+ throw new Error("Schema is missing a method literal");
26036
+ }
26037
+ const methodValue = getLiteralValue(methodSchema);
26038
+ if (typeof methodValue !== "string") {
26039
+ throw new Error("Schema method literal must be a string");
26040
+ }
26041
+ const method = methodValue;
26042
+ if (method === "tools/call") {
26043
+ const wrappedHandler = async (request, extra) => {
26044
+ const validatedRequest = safeParse4(CallToolRequestSchema, request);
26045
+ if (!validatedRequest.success) {
26046
+ const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
26047
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage}`);
26048
+ }
26049
+ const { params } = validatedRequest.data;
26050
+ const result = await Promise.resolve(handler(request, extra));
26051
+ if (params.task) {
26052
+ const taskValidationResult = safeParse4(CreateTaskResultSchema, result);
26053
+ if (!taskValidationResult.success) {
26054
+ const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
26055
+ throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
26056
+ }
26057
+ return taskValidationResult.data;
26058
+ }
26059
+ const validationResult = safeParse4(CallToolResultSchema, result);
26060
+ if (!validationResult.success) {
26061
+ const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
26062
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage}`);
26063
+ }
26064
+ return validationResult.data;
26065
+ };
26066
+ return super.setRequestHandler(requestSchema, wrappedHandler);
26067
+ }
26068
+ return super.setRequestHandler(requestSchema, handler);
25966
26069
  }
25967
- migrationFailed = false;
25968
- mkdirSync2(dir, { recursive: true });
25969
- diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
25970
- for (const entry of readdirSync3(legacy, { withFileTypes: true })) {
25971
- const src = path8.join(legacy, entry.name);
25972
- const dest = path8.join(dir, entry.name);
25973
- if (existsSync5(dest))
25974
- continue;
25975
- try {
25976
- if (entry.isDirectory())
25977
- cpSync(src, dest, { recursive: true });
25978
- else if (entry.isFile())
25979
- copyFileSync(src, dest);
25980
- } catch (err) {
25981
- migrationFailed = true;
25982
- diagnosticLogger?.warn(EVENT.migration, { from: src, ok: false, ...errorDetail(err) });
25983
- console.warn(`[workit] config migration: a file could not be copied to ${dir}; it will be retried on the next run`);
26070
+ assertCapabilityForMethod(method) {
26071
+ switch (method) {
26072
+ case "sampling/createMessage":
26073
+ if (!this._clientCapabilities?.sampling) {
26074
+ throw new Error(`Client does not support sampling (required for ${method})`);
26075
+ }
26076
+ break;
26077
+ case "elicitation/create":
26078
+ if (!this._clientCapabilities?.elicitation) {
26079
+ throw new Error(`Client does not support elicitation (required for ${method})`);
26080
+ }
26081
+ break;
26082
+ case "roots/list":
26083
+ if (!this._clientCapabilities?.roots) {
26084
+ throw new Error(`Client does not support listing roots (required for ${method})`);
26085
+ }
26086
+ break;
26087
+ case "ping":
26088
+ break;
25984
26089
  }
25985
26090
  }
25986
- if (!migrationFailed)
25987
- migratedDir = dir;
25988
- return dir;
25989
- };
25990
- var configDir = () => ensureConfigDir();
25991
- var LOCALE_RE = /^[a-z]{2,3}(-(?:[A-Z]{2}|[0-9]{3}))?$/;
25992
- var DEFAULTS = {
25993
- locale: "en",
25994
- localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
25995
- timezone: "America/Santiago",
25996
- branchPolicy: {
25997
- preset: "gitflow",
25998
- allowed: [...PRESETS.gitflow.allowed],
25999
- protected: [...PRESETS.gitflow.protected]
26000
- },
26001
- commitPolicy: { preset: "conventional" }
26002
- };
26003
- var readSafe = (p) => {
26004
- try {
26005
- return readFileSync4(p, "utf8");
26006
- } catch {
26007
- return null;
26091
+ assertNotificationCapability(method) {
26092
+ switch (method) {
26093
+ case "notifications/message":
26094
+ if (!this._capabilities.logging) {
26095
+ throw new Error(`Server does not support logging (required for ${method})`);
26096
+ }
26097
+ break;
26098
+ case "notifications/resources/updated":
26099
+ case "notifications/resources/list_changed":
26100
+ if (!this._capabilities.resources) {
26101
+ throw new Error(`Server does not support notifying about resources (required for ${method})`);
26102
+ }
26103
+ break;
26104
+ case "notifications/tools/list_changed":
26105
+ if (!this._capabilities.tools) {
26106
+ throw new Error(`Server does not support notifying of tool list changes (required for ${method})`);
26107
+ }
26108
+ break;
26109
+ case "notifications/prompts/list_changed":
26110
+ if (!this._capabilities.prompts) {
26111
+ throw new Error(`Server does not support notifying of prompt list changes (required for ${method})`);
26112
+ }
26113
+ break;
26114
+ case "notifications/elicitation/complete":
26115
+ if (!this._clientCapabilities?.elicitation?.url) {
26116
+ throw new Error(`Client does not support URL elicitation (required for ${method})`);
26117
+ }
26118
+ break;
26119
+ case "notifications/cancelled":
26120
+ break;
26121
+ case "notifications/progress":
26122
+ break;
26123
+ }
26008
26124
  }
26009
- };
26010
- var isConfigObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
26011
- var parseConfigResult = (raw, file) => {
26012
- if (raw === null)
26013
- return { status: "missing", path: file };
26014
- let parsed;
26015
- try {
26016
- parsed = JSON.parse(raw);
26017
- } catch {
26018
- return { status: "malformed", path: file, error: `${file} is not valid JSON` };
26125
+ assertRequestHandlerCapability(method) {
26126
+ if (!this._capabilities) {
26127
+ return;
26128
+ }
26129
+ switch (method) {
26130
+ case "completion/complete":
26131
+ if (!this._capabilities.completions) {
26132
+ throw new Error(`Server does not support completions (required for ${method})`);
26133
+ }
26134
+ break;
26135
+ case "logging/setLevel":
26136
+ if (!this._capabilities.logging) {
26137
+ throw new Error(`Server does not support logging (required for ${method})`);
26138
+ }
26139
+ break;
26140
+ case "prompts/get":
26141
+ case "prompts/list":
26142
+ if (!this._capabilities.prompts) {
26143
+ throw new Error(`Server does not support prompts (required for ${method})`);
26144
+ }
26145
+ break;
26146
+ case "resources/list":
26147
+ case "resources/templates/list":
26148
+ case "resources/read":
26149
+ if (!this._capabilities.resources) {
26150
+ throw new Error(`Server does not support resources (required for ${method})`);
26151
+ }
26152
+ break;
26153
+ case "tools/call":
26154
+ case "tools/list":
26155
+ if (!this._capabilities.tools) {
26156
+ throw new Error(`Server does not support tools (required for ${method})`);
26157
+ }
26158
+ break;
26159
+ case "tasks/get":
26160
+ case "tasks/list":
26161
+ case "tasks/result":
26162
+ case "tasks/cancel":
26163
+ if (!this._capabilities.tasks) {
26164
+ throw new Error(`Server does not support tasks capability (required for ${method})`);
26165
+ }
26166
+ break;
26167
+ case "ping":
26168
+ case "initialize":
26169
+ break;
26170
+ }
26019
26171
  }
26020
- if (!isConfigObject(parsed)) {
26021
- return { status: "malformed", path: file, error: `${file} is not a JSON object` };
26172
+ assertTaskCapability(method) {
26173
+ assertClientRequestTaskCapability(this._clientCapabilities?.tasks?.requests, method, "Client");
26022
26174
  }
26023
- const input = parsed;
26024
- const locale = LOCALE_RE.test(String(input.locale ?? "")) ? input.locale : DEFAULTS.locale;
26025
- const preset = Object.hasOwn(PRESETS, input.branchPolicy?.preset) ? input.branchPolicy?.preset : "gitflow";
26026
- const commitPreset = COMMIT_PRESETS.includes(String(input.commitPolicy?.preset ?? "")) ? input.commitPolicy?.preset : "conventional";
26027
- return {
26028
- status: "valid",
26029
- path: file,
26030
- config: {
26031
- locale,
26032
- localeOptions: Array.isArray(input.localeOptions) ? input.localeOptions : DEFAULTS.localeOptions,
26033
- timezone: input.timezone ?? DEFAULTS.timezone,
26034
- branchPolicy: mergePreset(preset, {
26035
- allowed: Array.isArray(input.branchPolicy?.allowed) ? input.branchPolicy.allowed : undefined,
26036
- protectedNames: Array.isArray(input.branchPolicy?.protected) ? input.branchPolicy.protected : undefined
26037
- }, DEFAULTS),
26038
- commitPolicy: {
26039
- preset: commitPreset,
26040
- ...typeof input.commitPolicy?.pattern === "string" ? { pattern: input.commitPolicy.pattern } : {}
26041
- }
26042
- }
26043
- };
26044
- };
26045
- var readConfigTyped = (dir) => {
26046
- const file = path8.join(dir ?? configDir(), "config.json");
26047
- return parseConfigResult(readSafe(file), file);
26048
- };
26049
- var readConfig = () => {
26050
- const result = readConfigTyped();
26051
- if (result.status === "malformed")
26052
- throw new Error(result.error);
26053
- return result.config ?? DEFAULTS;
26054
- };
26055
- var resolveBranchPolicy = (config, workspace) => {
26056
- const wp = workspace?.branchPolicy ?? {};
26057
- const preset = Object.hasOwn(PRESETS, wp.preset) ? wp.preset : config.branchPolicy?.preset ?? "gitflow";
26058
- const merged = mergePreset(preset, {
26059
- allowed: wp.allowed,
26060
- protectedNames: wp.protected
26061
- }, config);
26062
- const allowed = merged.allowed.map((p) => new RegExp(`^${p.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i"));
26063
- return {
26064
- preset,
26065
- allowed,
26066
- protected: new Set(merged.protected.map((p) => p.toLowerCase())),
26067
- integration: wp.integration === "merge" ? "merge" : "pr",
26068
- defaultTargetBranch: preset === "github-flow" ? "main" : preset === "trunk-based" ? "master" : "develop"
26069
- };
26070
- };
26071
- var COMMIT_PRESETS = [
26072
- "conventional",
26073
- "gitmoji",
26074
- "ticket-prefix",
26075
- "freeform",
26076
- "custom",
26077
- "auto"
26078
- ];
26079
-
26080
- // packages/workit-core/src/core/workspaces.ts
26081
- import { readFileSync as readFileSync5, realpathSync as realpathSync6 } from "node:fs";
26082
- import path9 from "node:path";
26083
- var readWorkspacesResult = (dir = configDir()) => {
26084
- const file = path9.join(dir, "workspaces.json");
26085
- let raw;
26086
- try {
26087
- raw = readFileSync5(file, "utf8");
26088
- } catch {
26089
- return { status: "missing", path: file, entries: [] };
26175
+ assertTaskHandlerCapability(method) {
26176
+ if (!this._capabilities) {
26177
+ return;
26178
+ }
26179
+ assertToolsCallTaskCapability(this._capabilities.tasks?.requests, method, "Server");
26090
26180
  }
26091
- let parsed;
26092
- try {
26093
- parsed = JSON.parse(raw);
26094
- } catch {
26095
- return { status: "malformed", path: file, entries: [], error: `${file} is not valid JSON` };
26181
+ async _oninitialize(request) {
26182
+ const requestedVersion = request.params.protocolVersion;
26183
+ this._clientCapabilities = request.params.capabilities;
26184
+ this._clientVersion = request.params.clientInfo;
26185
+ const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : LATEST_PROTOCOL_VERSION;
26186
+ return {
26187
+ protocolVersion,
26188
+ capabilities: this.getCapabilities(),
26189
+ serverInfo: this._serverInfo,
26190
+ ...this._instructions && { instructions: this._instructions }
26191
+ };
26096
26192
  }
26097
- if (!isConfigObject(parsed)) {
26098
- return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
26193
+ getClientCapabilities() {
26194
+ return this._clientCapabilities;
26099
26195
  }
26100
- const list = parsed.workspaces;
26101
- return {
26102
- status: "valid",
26103
- path: file,
26104
- entries: Array.isArray(list) ? list : []
26105
- };
26106
- };
26107
- var loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries;
26108
- var globToRegExp = (glob) => {
26109
- let out = "";
26110
- for (let i = 0;i < glob.length; i++) {
26111
- const c = glob[i];
26112
- if (c === "*") {
26113
- if (glob[i + 1] === "*") {
26114
- if (glob[i + 2] === "/") {
26115
- if (out === "")
26116
- out += "/?";
26117
- out += "(?:[^/]+/)*";
26118
- i += 2;
26119
- } else {
26120
- if (i + 2 >= glob.length) {
26121
- if (out.endsWith("/")) {
26122
- out = out.slice(0, -1);
26123
- out += "(?:/.*)?";
26124
- } else {
26125
- out += ".*";
26196
+ getClientVersion() {
26197
+ return this._clientVersion;
26198
+ }
26199
+ getCapabilities() {
26200
+ return this._capabilities;
26201
+ }
26202
+ async ping() {
26203
+ return this.request({ method: "ping" }, EmptyResultSchema);
26204
+ }
26205
+ async createMessage(params, options) {
26206
+ if (params.tools || params.toolChoice) {
26207
+ if (!this._clientCapabilities?.sampling?.tools) {
26208
+ throw new Error("Client does not support sampling tools capability.");
26209
+ }
26210
+ }
26211
+ if (params.messages.length > 0) {
26212
+ const lastMessage = params.messages[params.messages.length - 1];
26213
+ const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
26214
+ const hasToolResults = lastContent.some((c) => c.type === "tool_result");
26215
+ const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : undefined;
26216
+ const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
26217
+ const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
26218
+ if (hasToolResults) {
26219
+ if (lastContent.some((c) => c.type !== "tool_result")) {
26220
+ throw new Error("The last message must contain only tool_result content if any is present");
26221
+ }
26222
+ if (!hasPreviousToolUse) {
26223
+ throw new Error("tool_result blocks are not matching any tool_use from the previous message");
26224
+ }
26225
+ }
26226
+ if (hasPreviousToolUse) {
26227
+ const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
26228
+ const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
26229
+ if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
26230
+ throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
26231
+ }
26232
+ }
26233
+ }
26234
+ if (params.tools) {
26235
+ return this.request({ method: "sampling/createMessage", params }, CreateMessageResultWithToolsSchema, options);
26236
+ }
26237
+ return this.request({ method: "sampling/createMessage", params }, CreateMessageResultSchema, options);
26238
+ }
26239
+ async elicitInput(params, options) {
26240
+ const mode = params.mode ?? "form";
26241
+ switch (mode) {
26242
+ case "url": {
26243
+ if (!this._clientCapabilities?.elicitation?.url) {
26244
+ throw new Error("Client does not support url elicitation.");
26245
+ }
26246
+ const urlParams = params;
26247
+ return this.request({ method: "elicitation/create", params: urlParams }, ElicitResultSchema, options);
26248
+ }
26249
+ case "form": {
26250
+ if (!this._clientCapabilities?.elicitation?.form) {
26251
+ throw new Error("Client does not support form elicitation.");
26252
+ }
26253
+ const formParams = params.mode === "form" ? params : { ...params, mode: "form" };
26254
+ const result = await this.request({ method: "elicitation/create", params: formParams }, ElicitResultSchema, options);
26255
+ if (result.action === "accept" && result.content && formParams.requestedSchema) {
26256
+ try {
26257
+ const validator = this._jsonSchemaValidator.getValidator(formParams.requestedSchema);
26258
+ const validationResult = validator(result.content);
26259
+ if (!validationResult.valid) {
26260
+ throw new McpError(ErrorCode.InvalidParams, `Elicitation response content does not match requested schema: ${validationResult.errorMessage}`);
26126
26261
  }
26127
- } else {
26128
- out += ".*";
26262
+ } catch (error) {
26263
+ if (error instanceof McpError) {
26264
+ throw error;
26265
+ }
26266
+ throw new McpError(ErrorCode.InternalError, `Error validating elicitation response: ${error instanceof Error ? error.message : String(error)}`);
26129
26267
  }
26130
- i++;
26131
26268
  }
26132
- } else {
26133
- out += "[^/]*";
26269
+ return result;
26134
26270
  }
26135
- } else {
26136
- out += c.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
26137
26271
  }
26138
26272
  }
26139
- return new RegExp(`^${out}$`);
26140
- };
26141
- var matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/"));
26142
- var realpathOf = (p) => {
26143
- const real = realpathSync6.native ?? realpathSync6;
26144
- try {
26145
- return real(p);
26146
- } catch {
26147
- return p;
26148
- }
26149
- };
26150
- var canonicalGlob = (glob) => {
26151
- const m = /[*?[\]{]/.exec(glob);
26152
- const prefix = m ? glob.slice(0, m.index) : glob;
26153
- const rest = m ? glob.slice(m.index) : "";
26154
- if (!prefix)
26155
- return glob;
26156
- const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
26157
- return real + (prefix.endsWith("/") ? "/" : "") + rest;
26158
- };
26159
- var resolveWorkspaceFrom = (cwd, dir) => {
26160
- const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
26161
- for (const entry of loadWorkspacesFrom(dir)) {
26162
- if (!entry || typeof entry !== "object")
26163
- continue;
26164
- const ws = entry;
26165
- if (typeof ws.glob !== "string" || !ws.glob)
26166
- continue;
26167
- const glob = ws.glob.replaceAll("\\", "/");
26168
- const canonical = canonicalGlob(glob);
26169
- for (const target of targets) {
26170
- if (matchWorkspace(glob, target))
26171
- return ws;
26172
- if (canonical !== glob && matchWorkspace(canonical, target))
26173
- return ws;
26273
+ createElicitationCompletionNotifier(elicitationId, options) {
26274
+ if (!this._clientCapabilities?.elicitation?.url) {
26275
+ throw new Error("Client does not support URL elicitation (required for notifications/elicitation/complete)");
26174
26276
  }
26277
+ return () => this.notification({
26278
+ method: "notifications/elicitation/complete",
26279
+ params: {
26280
+ elicitationId
26281
+ }
26282
+ }, options);
26175
26283
  }
26176
- return null;
26177
- };
26178
- var resolveWorkspace = (cwd) => resolveWorkspaceFrom(cwd, configDir());
26179
-
26180
- // packages/workit-core/src/core/vcs-config.ts
26181
- import fs7 from "node:fs";
26182
- import path10 from "node:path";
26183
- import { spawnSync as spawnSync3 } from "node:child_process";
26184
- var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
26185
- var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path10.join(configDir(), "vcs.json");
26186
- var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
26187
- var remoteProvider = (cwd) => {
26188
- const r = spawnSync3("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
26189
- if (r.status !== 0)
26190
- return null;
26191
- const url = (r.stdout ?? "").trim();
26192
- if (/github\.com[:/]/.test(url))
26193
- return "github";
26194
- if (/gitlab\.com[:/]/.test(url))
26195
- return "gitlab";
26196
- return null;
26197
- };
26198
- var readVcsConfig = () => {
26199
- const file = vcsConfigPath();
26200
- let raw;
26201
- try {
26202
- raw = fs7.readFileSync(file, "utf8");
26203
- } catch {
26204
- return { status: "missing", path: file, config: {} };
26284
+ async listRoots(params, options) {
26285
+ return this.request({ method: "roots/list", params }, ListRootsResultSchema, options);
26205
26286
  }
26206
- let parsed;
26207
- try {
26208
- parsed = JSON.parse(raw);
26209
- } catch {
26210
- return { status: "malformed", path: file, config: {}, error: `${file} is not valid JSON` };
26287
+ async sendLoggingMessage(params, sessionId) {
26288
+ if (this._capabilities.logging) {
26289
+ if (!this.isMessageIgnored(params.level, sessionId)) {
26290
+ return this.notification({ method: "notifications/message", params });
26291
+ }
26292
+ }
26211
26293
  }
26212
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
26213
- return { status: "valid", path: file, config: parsed };
26294
+ async sendResourceUpdated(params) {
26295
+ return this.notification({
26296
+ method: "notifications/resources/updated",
26297
+ params
26298
+ });
26214
26299
  }
26215
- return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
26216
- };
26217
- function vcsConfig(mode, cwd) {
26218
- const ws = resolveWorkspace(vcsCwd(cwd));
26219
- const wsVcs = ws?.vcs ?? {};
26220
- const wsYt = ws?.youtrack ?? {};
26221
- const wsIssues = ws?.issues ?? {};
26222
- const { status: cfgStatus, path: cfgPath, config: cfg, error: cfgError } = readVcsConfig();
26223
- const root = vcsCwd(cwd);
26224
- const determinateRoot = cwd !== undefined || process.env.WORKFLOW_WORKSPACE_ROOT !== undefined;
26225
- const rawProvider = wsVcs.provider ?? (determinateRoot ? remoteProvider(root) : null) ?? cfg.provider ?? null;
26226
- const provider = rawProvider === null || rawProvider === undefined ? null : String(rawProvider).toLowerCase() || null;
26227
- const wp = ws?.branchPolicy ?? {};
26228
- const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
26229
- const policyDefault = resolveBranchPolicyFor(root).defaultTargetBranch;
26230
- const defaultTarget = String(wsVcs.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : cfg.defaultTargetBranch ?? policyDefault) ?? "develop");
26231
- const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
26232
- const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
26233
- let issuesProvider = null;
26234
- let linkOnPr = null;
26235
- if (provider === "github" && typeof wsIssues.provider === "string" && wsIssues.provider.toLowerCase() === "github") {
26236
- issuesProvider = "github";
26237
- linkOnPr = typeof wsIssues.link_on_pr === "boolean" ? wsIssues.link_on_pr : null;
26300
+ async sendResourceListChanged() {
26301
+ return this.notification({
26302
+ method: "notifications/resources/list_changed"
26303
+ });
26238
26304
  }
26239
- if (mode === "resolve") {
26240
- if (cfgStatus === "malformed")
26241
- return { ok: false, error: cfgError, configPath: cfgPath };
26242
- return {
26243
- ok: true,
26244
- workspace_name: ws?.name ?? null,
26245
- provider,
26246
- defaultTargetBranch: defaultTarget,
26247
- link_issues: linkIssues,
26248
- youtrack_base_url: youtrackBaseUrl,
26249
- issues_provider: issuesProvider,
26250
- link_on_pr: linkOnPr
26251
- };
26305
+ async sendToolListChanged() {
26306
+ return this.notification({ method: "notifications/tools/list_changed" });
26252
26307
  }
26253
- if (cfgStatus === "malformed") {
26254
- return { ok: false, error: cfgError, configPath: cfgPath };
26308
+ async sendPromptListChanged() {
26309
+ return this.notification({ method: "notifications/prompts/list_changed" });
26255
26310
  }
26256
- if (cfgStatus === "missing") {
26257
- return { ok: false, error: `vcs.json is missing: ${cfgPath}`, configPath: cfgPath };
26311
+ }
26312
+
26313
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
26314
+ import process3 from "node:process";
26315
+
26316
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
26317
+ var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
26318
+
26319
+ class ReadBuffer {
26320
+ constructor(options) {
26321
+ this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
26258
26322
  }
26259
- if (provider === null) {
26260
- return {
26261
- ok: false,
26262
- error: `no vcs provider configured (set vcs.provider in ${cfgPath}, match a workspace entry, or run inside a checkout with a recognized origin remote)`,
26263
- configPath: cfgPath
26264
- };
26323
+ append(chunk) {
26324
+ const newSize = (this._buffer?.length ?? 0) + chunk.length;
26325
+ if (newSize > this._maxBufferSize) {
26326
+ this.clear();
26327
+ throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
26328
+ }
26329
+ this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
26265
26330
  }
26266
- const prov = cfg[provider] ?? {};
26267
- const wsTokenFile = typeof wsVcs.tokenFile === "string" && wsVcs.tokenFile.trim() !== "" ? wsVcs.tokenFile : null;
26268
- const tokenFile = String(wsTokenFile ?? prov.tokenFile ?? path10.join(configDir(), `${provider}.token`));
26269
- const tokenPath = path10.resolve(tokenFile);
26270
- let tokenOk = false;
26271
- if (fs7.existsSync(tokenPath)) {
26272
- const token = fs7.readFileSync(tokenPath, "utf8").trim();
26273
- const placeholder = !token || token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER);
26274
- tokenOk = !placeholder;
26331
+ readMessage() {
26332
+ if (!this._buffer) {
26333
+ return null;
26334
+ }
26335
+ const index = this._buffer.indexOf(`
26336
+ `);
26337
+ if (index === -1) {
26338
+ return null;
26339
+ }
26340
+ const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
26341
+ this._buffer = this._buffer.subarray(index + 1);
26342
+ return deserializeMessage(line);
26275
26343
  }
26276
- const out = {
26277
- ok: true,
26278
- configPath: path10.resolve(cfgPath),
26279
- provider,
26280
- defaultTargetBranch: defaultTarget,
26281
- pr: cfg.pr ?? {},
26282
- tokenPath,
26283
- tokenPresent: fs7.existsSync(tokenPath),
26284
- tokenReady: tokenOk,
26285
- workspace_name: ws?.name ?? null,
26286
- link_issues: linkIssues,
26287
- youtrack_base_url: youtrackBaseUrl,
26288
- issues_provider: issuesProvider,
26289
- link_on_pr: linkOnPr
26290
- };
26291
- if (provider === "gitlab") {
26292
- out.gitlab = {
26293
- host: prov.host ?? "gitlab.com",
26294
- apiUrl: prov.apiUrl ?? "https://gitlab.com/api/v4"
26295
- };
26296
- } else if (provider === "github") {
26297
- out.github = { host: prov.host ?? "github.com" };
26344
+ clear() {
26345
+ this._buffer = undefined;
26298
26346
  }
26299
- if (mode === "summary")
26300
- delete out.tokenPath;
26301
- return out;
26302
26347
  }
26303
- function mergedPrStyle(limit = 6, cwd) {
26304
- const cfg = vcsConfig("load", cwd);
26305
- if (!cfg.ok || !cfg.tokenReady)
26306
- return { ok: false, error: "vcs not configured" };
26307
- const provider = cfg.provider;
26308
- const token = fs7.readFileSync(cfg.tokenPath, "utf8").trim();
26309
- const examples = [];
26310
- const descInfo = (desc, caseInsensitiveNotes = false) => ({
26311
- hasNotesSection: caseInsensitiveNotes ? /##\s*notes/i.test(desc) : /##\s*Notes/.test(desc),
26312
- sections: desc.split(`
26313
- `).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
26314
- descriptionPreview: desc.slice(0, 600)
26315
- });
26316
- if (provider === "gitlab") {
26317
- const remote = spawnSync3("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
26318
- if (remote.status !== 0)
26319
- return { ok: false, error: "no origin remote" };
26320
- const url = (remote.stdout ?? "").trim();
26321
- const m = /gitlab\.com[:/](.+?)(?:\.git)?$/.exec(url);
26322
- if (!m)
26323
- return { ok: false, error: "not a gitlab.com origin" };
26324
- const project = m[1];
26325
- const env = { ...process.env, GITLAB_TOKEN: token };
26326
- const run = (args) => spawnSync3("glab", ["api", ...args], { cwd, encoding: "utf8", env });
26327
- let r = run([
26328
- `projects/${project.replaceAll("/", "%2F")}/merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`
26329
- ]);
26330
- if (r.status !== 0) {
26331
- r = run([`merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`]);
26348
+ function deserializeMessage(line) {
26349
+ return JSONRPCMessageSchema.parse(JSON.parse(line));
26350
+ }
26351
+ function serializeMessage(message) {
26352
+ return JSON.stringify(message) + `
26353
+ `;
26354
+ }
26355
+
26356
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
26357
+ class StdioServerTransport {
26358
+ constructor(_stdin = process3.stdin, _stdout = process3.stdout, options) {
26359
+ this._stdin = _stdin;
26360
+ this._stdout = _stdout;
26361
+ this._started = false;
26362
+ this._ondata = (chunk) => {
26363
+ try {
26364
+ this._readBuffer.append(chunk);
26365
+ this.processReadBuffer();
26366
+ } catch (error) {
26367
+ this.onerror?.(error);
26368
+ this.close().catch(() => {});
26369
+ }
26370
+ };
26371
+ this._onerror = (error) => {
26372
+ this.onerror?.(error);
26373
+ };
26374
+ this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
26375
+ }
26376
+ async start() {
26377
+ if (this._started) {
26378
+ throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
26332
26379
  }
26333
- if (r.status !== 0)
26334
- return { ok: false, error: "could not list merge requests" };
26335
- for (const mr of JSON.parse(r.stdout ?? "[]")) {
26336
- const desc = String(mr.description ?? "").trim();
26337
- examples.push({
26338
- title: mr.title,
26339
- url: mr.web_url,
26340
- squash: mr.squash,
26341
- ...descInfo(desc, true)
26342
- });
26380
+ this._started = true;
26381
+ this._stdin.on("data", this._ondata);
26382
+ this._stdin.on("error", this._onerror);
26383
+ }
26384
+ processReadBuffer() {
26385
+ while (true) {
26386
+ try {
26387
+ const message = this._readBuffer.readMessage();
26388
+ if (message === null) {
26389
+ break;
26390
+ }
26391
+ this.onmessage?.(message);
26392
+ } catch (error) {
26393
+ this.onerror?.(error);
26394
+ }
26343
26395
  }
26344
- } else if (provider === "github") {
26345
- const r = spawnSync3("gh", ["pr", "list", "--state", "merged", "--limit", String(limit), "--json", "title,url,body"], { cwd, encoding: "utf8", env: { ...process.env, GH_TOKEN: token } });
26346
- if (r.status !== 0)
26347
- return { ok: false, error: "could not list pull requests" };
26348
- for (const pr of JSON.parse(r.stdout ?? "[]")) {
26349
- const desc = String(pr.body ?? "").trim();
26350
- examples.push({ title: pr.title, url: pr.url, ...descInfo(desc) });
26396
+ }
26397
+ async close() {
26398
+ this._stdin.off("data", this._ondata);
26399
+ this._stdin.off("error", this._onerror);
26400
+ const remainingDataListeners = this._stdin.listenerCount("data");
26401
+ if (remainingDataListeners === 0) {
26402
+ this._stdin.pause();
26351
26403
  }
26404
+ this._readBuffer.clear();
26405
+ this.onclose?.();
26406
+ }
26407
+ send(message) {
26408
+ return new Promise((resolve) => {
26409
+ const json = serializeMessage(message);
26410
+ if (this._stdout.write(json)) {
26411
+ resolve();
26412
+ } else {
26413
+ this._stdout.once("drain", resolve);
26414
+ }
26415
+ });
26352
26416
  }
26353
- return {
26354
- ok: true,
26355
- provider,
26356
- count: examples.length,
26357
- styleHints: [
26358
- "Prefer ## Summary bullets + ## Validation or ## Test plan only",
26359
- "Do not add ## Notes with branch names, commit counts, or diff stats",
26360
- "Do not paste commit log or diff stat into the body"
26361
- ],
26362
- examples
26363
- };
26364
26417
  }
26365
26418
 
26366
- // packages/workit-core/src/core/branch.ts
26367
- var resolveBranchPolicyFor = (workspaceRoot) => resolveBranchPolicy(readConfig(), resolveWorkspace(workspaceRoot));
26419
+ // packages/workit-mcp/src/server.ts
26420
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
26421
+
26422
+ // packages/workit-core/src/core/logger.ts
26423
+ import os2 from "node:os";
26424
+ var REDACTED = "[REDACTED]";
26425
+ var SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
26426
+ var KEY_EQ_VALUE = /\b([A-Za-z0-9_-]*(?:token|secret|password|passwd|apikey|api[_-]?key|authorization|credential|bearer)[A-Za-z0-9_-]*)([:=]\s*).+/gi;
26427
+ var URL_QUERY = /(https?:\/\/[^?#\s]+)\?[^#\s]*/g;
26428
+ var SENSITIVE_WORDS = new Set([
26429
+ "token",
26430
+ "secret",
26431
+ "password",
26432
+ "passwd",
26433
+ "authorization",
26434
+ "credential",
26435
+ "credentials",
26436
+ "cookie",
26437
+ "api",
26438
+ "apikey",
26439
+ "apitoken",
26440
+ "accesstoken",
26441
+ "clientsecret",
26442
+ "refreshtoken",
26443
+ "key",
26444
+ "bearer",
26445
+ "prompt",
26446
+ "message",
26447
+ "messages",
26448
+ "content",
26449
+ "body",
26450
+ "args",
26451
+ "argument",
26452
+ "result",
26453
+ "results",
26454
+ "output",
26455
+ "issue",
26456
+ "description",
26457
+ "summary",
26458
+ "payload",
26459
+ "script",
26460
+ "command",
26461
+ "text",
26462
+ "query"
26463
+ ]);
26464
+ var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
26465
+ var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26466
+ var homePattern = new RegExp(`${escapeRegExp(os2.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
26467
+ var applyPatterns = (value) => {
26468
+ let out = value;
26469
+ out = out.replace(SECRET_VALUE, REDACTED);
26470
+ out = out.replace(KEY_EQ_VALUE, "$1$2[REDACTED]");
26471
+ out = out.replace(URL_QUERY, "$1?[REDACTED]");
26472
+ out = out.replace(homePattern, "~");
26473
+ if (process.platform === "win32")
26474
+ out = out.replace(/\\/g, "/");
26475
+ return out;
26476
+ };
26477
+ var redactSecrets = (value) => applyPatterns(value);
26368
26478
 
26369
26479
  // packages/workit-core/src/core/pr-create.ts
26370
26480
  import fs8 from "node:fs";