@brainervirus/workit-codex 1.0.11 → 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,
@@ -15912,11 +15995,11 @@ class TaskStore {
15912
15995
  return workspace;
15913
15996
  if (!workspace.data)
15914
15997
  return failure("not_found", "workspace not found");
15915
- const expectedTaskRevision = input.expectedTaskRevision ?? input.expectedRevision;
15916
- if (!expectedTaskRevision)
15998
+ const expected = input.expectedRevision;
15999
+ if (!expected)
15917
16000
  return failure("invalid_input", "task revision is required");
15918
- if (task.data.revision !== expectedTaskRevision)
15919
- return this.conflict(expectedTaskRevision, task.data.revision);
16001
+ if (task.data.revision !== expected)
16002
+ return this.conflict(expected, task.data.revision);
15920
16003
  if (workspace.data.revision !== input.expectedWorkspaceRevision)
15921
16004
  return this.conflict(input.expectedWorkspaceRevision, workspace.data.revision);
15922
16005
  const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
@@ -16450,6 +16533,7 @@ var same = (left, right) => {
16450
16533
  };
16451
16534
  var UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
16452
16535
  var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
16536
+ var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
16453
16537
  var callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor };
16454
16538
  var validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
16455
16539
  const code = char.charCodeAt(0);
@@ -16511,6 +16595,36 @@ var trustedAuthority = (kind, provenance, expected, binding, caller) => {
16511
16595
  verifiedAuthorities.set(token, { kind, provenance, ...expected, ...binding, caller });
16512
16596
  return token;
16513
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
+ };
16514
16628
  var scopeEqual = (left, right) => canonicalJson(left) === canonicalJson(right);
16515
16629
  var parseSteps = (content) => {
16516
16630
  let value;
@@ -16522,7 +16636,10 @@ var parseSteps = (content) => {
16522
16636
  if (typeof value !== "object" || value === null || !("steps" in value))
16523
16637
  return [];
16524
16638
  const steps = value.steps;
16525
- 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;
16526
16643
  };
16527
16644
  var decisionMatches = (entry, purpose, binding) => {
16528
16645
  const decision = entry.data;
@@ -16713,14 +16830,12 @@ var validateAction = (store, task, workspaceId, input, authority) => {
16713
16830
  return content;
16714
16831
  const requestedSteps = parseSteps(decision.binding.approvedContent);
16715
16832
  if (requestedSteps === null)
16716
- return failure("invalid_input", "bounded action steps must be a string array");
16717
- 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)))
16718
16835
  return failure("permission_denied", "bounded action steps do not match approved content");
16719
- if (requestedSteps.some((step) => !step || typeof step !== "string") || new Set(requestedSteps).size !== requestedSteps.length)
16720
- return failure("invalid_input", "bounded action steps must be unique and non-empty");
16721
16836
  const stored = task.actionProgress?.find((item) => item.decisionId === input.decisionId);
16722
- const workflow = stored ? { steps: stored.steps, completed: stored.completedSteps } : { steps: requestedSteps, completed: [] };
16723
- 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)))
16724
16839
  return failure("permission_denied", "bounded action scope changed");
16725
16840
  return success(null, null, { entry, workflow });
16726
16841
  };
@@ -17275,13 +17390,28 @@ function evaluateEvidence(task, candidate) {
17275
17390
  });
17276
17391
  }
17277
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);
17278
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")));
17279
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 }));
17280
- var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
17410
+ var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
17281
17411
  if (kind !== "passed")
17282
17412
  return false;
17283
17413
  if (dimension === "testing" || dimension === "verification")
17284
- return evidenceKind === "check";
17414
+ return evidenceKind === "check" || ruleId === "pre-pr-cleanup" && evidenceKind === "artifact";
17285
17415
  if (dimension === "review")
17286
17416
  return evidenceKind === "review";
17287
17417
  if (dimension === "investigation" || dimension === "challenge")
@@ -17290,6 +17420,21 @@ var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
17290
17420
  return evidenceKind === "artifact";
17291
17421
  return false;
17292
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
+ };
17293
17438
  function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller) {
17294
17439
  const evidence = evaluateEvidence(task, candidate);
17295
17440
  if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
@@ -17303,7 +17448,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
17303
17448
  return (task.policy?.requirements ?? []).map((requirement) => {
17304
17449
  const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
17305
17450
  const passed = related.filter(({ entry, evaluation }) => {
17306
- if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension))
17451
+ if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
17307
17452
  return false;
17308
17453
  if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
17309
17454
  return false;
@@ -17311,11 +17456,12 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
17311
17456
  return true;
17312
17457
  const reviewSession = sessionFromRef(entry.data.reviewContext);
17313
17458
  const sameImplementation = sameSession(reviewSession, task.intent.provenance.session);
17314
- 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));
17315
17460
  return entry.data.kind === "review" && reviewSession !== null && sameSession(reviewSession, entry.provenance.session) && (requirement.ruleId === "self-review" || !sameImplementation && !sameEvidenceSession);
17316
17461
  });
17317
17462
  if (passed.length) {
17318
- 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;
17319
17465
  if (withRed.length)
17320
17466
  return {
17321
17467
  requirementId: requirement.id,
@@ -17364,6 +17510,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
17364
17510
  reason: "an applicable approved limitation permits the missing evidence"
17365
17511
  };
17366
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
+ };
17367
17521
  return {
17368
17522
  requirementId: requirement.id,
17369
17523
  status: unavailable ? "unavailable" : "unsatisfied",
@@ -17388,7 +17542,13 @@ function evaluateClosure(requestedOutcome, view) {
17388
17542
  if (entry.data.disposition === "fixed") {
17389
17543
  const verified = entry.data.resolution?.evidenceIds.some((id) => {
17390
17544
  const evidence = view.task.evidence.find((item) => item.id === id);
17391
- 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
+ });
17392
17552
  });
17393
17553
  if (!verified)
17394
17554
  return failure("requirements_unsatisfied", "fixed findings require current verification");
@@ -17404,7 +17564,12 @@ function evaluateClosure(requestedOutcome, view) {
17404
17564
  }
17405
17565
  }
17406
17566
  }
17407
- 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
+ });
17408
17573
  if (requestedOutcome !== "stopped" && blocking.length)
17409
17574
  return failure("requirements_unsatisfied", "applicable requirements are unsatisfied", {
17410
17575
  requirementIds: blocking.map((item) => item.requirementId),
@@ -17473,197 +17638,755 @@ var exportDigest = (bundle) => sha256({
17473
17638
  sourceWorkspaceId: bundle.sourceWorkspaceId,
17474
17639
  task: bundle.task
17475
17640
  });
17476
- // packages/workit-core/src/core/external-action.ts
17477
- var externalActionSchema = discriminatedUnion("operation", [
17478
- object({
17479
- operation: literal("git.branch_setup"),
17480
- payload: object({
17481
- action: _enum(["setup", "reapply_stash"]).optional(),
17482
- sdd_dir: string2().optional(),
17483
- target_branch: string2().optional(),
17484
- stash: _enum(["yes", "no"]).optional()
17485
- }).strict()
17486
- }).strict(),
17487
- object({
17488
- operation: literal("git.commit"),
17489
- payload: object({
17490
- message: string2().min(1).optional(),
17491
- plan_steps: array(string2().min(1)).optional(),
17492
- plan_branch: string2().min(1).optional()
17493
- }).strict()
17494
- }).strict(),
17495
- object({
17496
- operation: literal("git.push"),
17497
- payload: object({ branch: string2().min(1).optional() }).strict()
17498
- }).strict(),
17499
- object({
17500
- operation: literal("hosting.pull_request"),
17501
- payload: object({
17502
- title: string2().min(1),
17503
- body: string2().optional(),
17504
- draft: boolean2().optional(),
17505
- target_branch: string2().optional(),
17506
- babysit: boolean2().optional()
17507
- }).strict()
17508
- }).strict(),
17509
- object({
17510
- operation: literal("youtrack.update"),
17511
- payload: object({
17512
- issueId: string2().min(1),
17513
- markdown: string2().min(1),
17514
- minutes: number2().positive().optional()
17515
- }).strict()
17516
- }).strict(),
17517
- object({
17518
- operation: literal("youtrack.time"),
17519
- payload: object({
17520
- issueId: string2().min(1),
17521
- minutes: number2().positive(),
17522
- text: string2().optional(),
17523
- dateMs: number2().finite().optional()
17524
- }).strict()
17525
- }).strict(),
17526
- object({
17527
- operation: literal("youtrack.meeting"),
17528
- payload: object({
17529
- issueId: string2().min(1),
17530
- minutes: number2().positive(),
17531
- text: string2().min(1)
17532
- }).strict()
17533
- }).strict(),
17534
- object({
17535
- operation: literal("changelog.apply"),
17536
- payload: object({
17537
- entries: union([
17538
- record(string2(), array(string2())),
17539
- array(object({ category: string2().min(1), text: string2().min(1) }).strict())
17540
- ]).optional(),
17541
- path: string2().optional(),
17542
- normalize_only: boolean2().optional()
17543
- }).strict()
17544
- }).strict(),
17545
- object({
17546
- operation: literal("context.read"),
17547
- payload: object({
17548
- kind: _enum([
17549
- "git",
17550
- "pr",
17551
- "youtrack",
17552
- "github_issue",
17553
- "gitlab_issue",
17554
- "changelog",
17555
- "release",
17556
- "affected"
17557
- ]),
17558
- range: string2().optional(),
17559
- issueId: string2().optional(),
17560
- issueUrl: string2().optional(),
17561
- issueRef: string2().optional(),
17562
- mode: string2().optional(),
17563
- specPath: string2().optional(),
17564
- planPath: string2().optional()
17565
- }).strict()
17566
- }).strict()
17567
- ]);
17568
- // packages/workit-core/src/core/task-engine.ts
17569
- import { realpathSync as realpathSync4 } from "node:fs";
17570
-
17571
- // packages/workit-core/src/core/policy-resolver.ts
17572
- var resolverInputSchema = object({
17573
- intent: intentSchema,
17574
- assessment: assessmentSchema,
17575
- constraints: array(constraintSchema),
17576
- prior: object({
17577
- decisions: array(decisionSchema),
17578
- findings: array(findingSchema),
17579
- requirements: array(requirementSchema)
17580
- }).strict(),
17581
- preferences: object({ fast: boolean2().optional(), thorough: boolean2().optional() }).strict().optional()
17582
- }).strict();
17583
- var compareCodeUnits3 = (left, right) => {
17584
- const length = Math.min(left.length, right.length);
17585
- for (let index = 0;index < length; index += 1) {
17586
- const difference = left.charCodeAt(index) - right.charCodeAt(index);
17587
- if (difference !== 0)
17588
- 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
+ };
17589
17658
  }
17590
- return left.length - right.length;
17591
17659
  };
17592
- var stableUnique = (values) => [...new Set(values)].sort(compareCodeUnits3);
17593
- var stableList = (values) => [...values].sort((left, right) => compareCodeUnits3(canonicalJson(left), canonicalJson(right)));
17594
- var normalizedScope = (scope) => ({
17595
- description: scope.description,
17596
- paths: stableUnique(scope.paths),
17597
- exclusions: stableUnique(scope.exclusions)
17598
- });
17599
- var normalizedRefList = (values) => stableList(values);
17600
- var normalizeAssessment = (assessment) => ({
17601
- facts: stableList(assessment.facts.map((fact) => ({ ...fact, refs: normalizedRefList(fact.refs) }))),
17602
- signals: Object.fromEntries(Object.entries(assessment.signals).map(([name, signal]) => [
17603
- name,
17604
- { ...signal, refs: normalizedRefList(signal.refs) }
17605
- ])),
17606
- consequences: stableList(assessment.consequences.map((consequence) => ({
17607
- ...consequence,
17608
- fact: { ...consequence.fact, refs: normalizedRefList(consequence.fact.refs) }
17609
- }))),
17610
- verification: stableList(assessment.verification.map((verification) => ({
17611
- ...verification,
17612
- scope: normalizedScope(verification.scope),
17613
- availableChecks: normalizedRefList(verification.availableChecks),
17614
- gaps: stableUnique(verification.gaps)
17615
- })))
17616
- });
17617
- var normalizeConstraint = (constraint) => ({
17618
- ...constraint,
17619
- source: constraint.source,
17620
- requires: stableList(constraint.requires.map((requirement) => ({
17621
- ...requirement,
17622
- scope: normalizedScope(requirement.scope),
17623
- refs: normalizedRefList(requirement.refs)
17624
- })))
17625
- });
17626
- var normalizeDecision = (decision) => ({
17627
- ...decision,
17628
- binding: {
17629
- ...decision.binding,
17630
- scope: normalizedScope(decision.binding.scope),
17631
- contentRefs: normalizedRefList(decision.binding.contentRefs)
17632
- },
17633
- requirementIds: stableUnique(decision.requirementIds)
17634
- });
17635
- var normalizeIntent = (intent) => ({
17636
- ...intent,
17637
- scope: normalizedScope(intent.scope),
17638
- authorityRefs: normalizedRefList(intent.authorityRefs)
17639
- });
17640
- var normalizePrior = (prior) => ({
17641
- decisions: stableList(prior.decisions.map(normalizeDecision)),
17642
- findings: stableList(prior.findings.map((finding) => ({
17643
- ...finding,
17644
- scope: normalizedScope(finding.scope),
17645
- refs: normalizedRefList(finding.refs),
17646
- resolution: finding.resolution ? {
17647
- ...finding.resolution,
17648
- evidenceIds: stableUnique(finding.resolution.evidenceIds),
17649
- decisionIds: stableUnique(finding.resolution.decisionIds)
17650
- } : null
17651
- }))),
17652
- requirements: stableList(prior.requirements.map((requirement) => ({
17653
- ...requirement,
17654
- scope: normalizedScope(requirement.scope)
17655
- })))
17656
- });
17657
- var invalidFields = (error) => ({
17658
- fields: error.issues.map((issue) => ({
17659
- path: issue.code === "unrecognized_keys" ? issue.keys.join(".") : issue.path.join("."),
17660
- reason: issue.message
17661
- }))
17662
- });
17663
- function normalizeResolverInput(input) {
17664
- const parsed = resolverInputSchema.safeParse(input);
17665
- if (!parsed.success)
17666
- 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));
17667
18390
  const value = parsed.data;
17668
18391
  const signals = value.assessment.signals;
17669
18392
  if (signals.behaviorChange.value === true && signals.mechanicalLowRisk.value === true)
@@ -18010,6 +18733,8 @@ var applyWorkerLifecycle = (input) => {
18010
18733
  if (task.data.status === "paused" && (input.state === "running" || input.state === "cancelling"))
18011
18734
  return failure("invalid_transition", "paused task cannot run a worker");
18012
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);
18013
18738
  if (notStarted && (input.state !== "stopped" || input.session !== null))
18014
18739
  return failure("invalid_input", "a never-dispatched worker stops with no session");
18015
18740
  if (notStarted && entry.data.session !== null)
@@ -18041,7 +18766,7 @@ var applyWorkerLifecycle = (input) => {
18041
18766
  return success(task.data.revision, workspace.data.revision, entry);
18042
18767
  const changed = input.store.mutateTaskAndWorkspace({
18043
18768
  taskId: task.data.id,
18044
- expectedTaskRevision: input.expectedRevision,
18769
+ expectedRevision: input.expectedRevision,
18045
18770
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
18046
18771
  now: input.now,
18047
18772
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
@@ -18089,90 +18814,22 @@ var importedProvenance = (context) => ({
18089
18814
  receipts: []
18090
18815
  });
18091
18816
  var portableTask = (task) => {
18092
- const clone = structuredClone(task);
18093
- const portableProvenance = (value) => ({
18094
- ...value,
18095
- session: null,
18096
- receipts: []
18097
- });
18098
- const entries = [
18099
- clone.intent,
18100
- ...clone.assessments,
18101
- ...clone.evidence,
18102
- ...clone.decisions,
18103
- ...clone.findings,
18104
- ...clone.workers
18105
- ];
18106
- for (const entry of entries)
18107
- entry.provenance = portableProvenance(entry.provenance);
18108
- for (const worker of clone.workers)
18109
- worker.data.session = null;
18110
- clone.intent = {
18111
- ...clone.intent,
18112
- data: { ...clone.intent.data, authorityRefs: portableRefs(clone.intent.data.authorityRefs) }
18113
- };
18114
- clone.constraints = clone.constraints.flatMap((constraint) => {
18115
- const source = portableRef(constraint.source);
18116
- return source ? [
18117
- {
18118
- ...constraint,
18119
- source,
18120
- requires: constraint.requires.map((requirement) => ({
18121
- ...requirement,
18122
- refs: portableRefs(requirement.refs)
18123
- }))
18124
- }
18125
- ] : [];
18126
- });
18127
- clone.assessments = clone.assessments.map((entry) => ({
18128
- ...entry,
18129
- data: {
18130
- ...entry.data,
18131
- facts: entry.data.facts.map(portableFact),
18132
- signals: Object.fromEntries(Object.entries(entry.data.signals).map(([name, signal]) => [name, portableSignal(signal)])),
18133
- consequences: entry.data.consequences.map((consequence) => ({
18134
- ...consequence,
18135
- fact: portableFact(consequence.fact)
18136
- })),
18137
- verification: entry.data.verification.map((verification) => ({
18138
- ...verification,
18139
- availableChecks: portableRefs(verification.availableChecks)
18140
- }))
18141
- }
18142
- }));
18143
- clone.progress = {
18144
- ...clone.progress,
18145
- blockers: clone.progress.blockers.map((blocker) => ({
18146
- ...blocker,
18147
- refs: portableRefs(blocker.refs)
18148
- }))
18149
- };
18150
- clone.evidence = clone.evidence.map((entry) => ({
18151
- ...entry,
18152
- data: {
18153
- ...entry.data,
18154
- refs: portableRefs(entry.data.refs),
18155
- reviewContext: portableRef(entry.data.reviewContext)
18156
- }
18157
- }));
18158
- clone.decisions = clone.decisions.map((entry) => ({
18159
- ...entry,
18160
- data: {
18161
- ...entry.data,
18162
- consumption: entry.data.consumption ? portableRef(entry.data.consumption.actionRef) ? {
18163
- ...entry.data.consumption,
18164
- actionRef: portableRef(entry.data.consumption.actionRef)
18165
- } : null : null,
18166
- binding: {
18167
- ...entry.data.binding,
18168
- contentRefs: portableRefs(entry.data.binding.contentRefs)
18169
- }
18170
- }
18171
- }));
18172
- clone.findings = clone.findings.map((entry) => ({
18173
- ...entry,
18174
- data: { ...entry.data, refs: portableRefs(entry.data.refs) }
18175
- }));
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);
18176
18833
  clone.candidates = [];
18177
18834
  return clone;
18178
18835
  };
@@ -18197,15 +18854,13 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
18197
18854
  allocate(entry.id);
18198
18855
  const fresh = (id) => allocate(id);
18199
18856
  const provenance = importedProvenance(context);
18857
+ const remap = (ref) => mapRef(ref, ids);
18200
18858
  const intent = {
18201
18859
  ...source.intent,
18202
18860
  id: fresh(source.intent.id),
18203
18861
  recordedAt: timestamp,
18204
18862
  provenance,
18205
- data: {
18206
- ...source.intent.data,
18207
- authorityRefs: source.intent.data.authorityRefs.map((ref) => mapRef(ref, ids))
18208
- }
18863
+ data: rewriteRecordRefs(source.intent.data, intentSchema, remap)
18209
18864
  };
18210
18865
  const assessments = source.assessments.map((entry) => {
18211
18866
  const data = entry.data;
@@ -18214,62 +18869,39 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
18214
18869
  id: fresh(entry.id),
18215
18870
  recordedAt: timestamp,
18216
18871
  provenance,
18217
- data: {
18218
- ...data,
18219
- facts: data.facts.map((fact) => ({
18220
- ...fact,
18221
- refs: fact.refs.map((ref) => mapRef(ref, ids))
18222
- })),
18223
- signals: Object.fromEntries(Object.entries(data.signals).map(([name, signal]) => [
18224
- name,
18225
- { ...signal, refs: signal.refs.map((ref) => mapRef(ref, ids)) }
18226
- ])),
18227
- consequences: data.consequences.map((consequence) => ({
18228
- ...consequence,
18229
- fact: {
18230
- ...consequence.fact,
18231
- refs: consequence.fact.refs.map((ref) => mapRef(ref, ids))
18232
- }
18233
- })),
18234
- verification: data.verification.map((verification) => ({
18235
- ...verification,
18236
- availableChecks: verification.availableChecks.map((ref) => mapRef(ref, ids))
18237
- }))
18238
- }
18872
+ data: rewriteRecordRefs(data, assessmentSchema, remap)
18239
18873
  };
18240
18874
  });
18241
18875
  const evidence = source.evidence.map((entry) => ({
18242
18876
  ...entry,
18243
18877
  id: fresh(entry.id),
18244
18878
  recordedAt: timestamp,
18245
- provenance,
18246
- data: {
18247
- ...entry.data,
18248
- refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
18249
- reviewContext: entry.data.reviewContext ? mapRef(entry.data.reviewContext, ids) : null
18250
- }
18251
- }));
18252
- const decisions = source.decisions.map((entry) => ({
18253
- ...entry,
18254
- id: fresh(entry.id),
18255
- recordedAt: timestamp,
18256
- provenance
18879
+ provenance,
18880
+ data: rewriteRecordRefs(entry.data, evidenceSchema, remap)
18257
18881
  }));
18258
- const findings = source.findings.map((entry) => ({
18882
+ const decisions = source.decisions.map((entry) => ({
18259
18883
  ...entry,
18260
18884
  id: fresh(entry.id),
18261
18885
  recordedAt: timestamp,
18262
- provenance,
18263
- data: {
18264
- ...entry.data,
18265
- refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
18266
- resolution: entry.data.resolution ? {
18267
- ...entry.data.resolution,
18268
- evidenceIds: entry.data.resolution.evidenceIds.map((id) => ids.get(id) ?? id),
18269
- decisionIds: entry.data.resolution.decisionIds.map((id) => ids.get(id) ?? id)
18270
- } : null
18271
- }
18886
+ provenance
18272
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
+ });
18273
18905
  const workers = source.workers.map((entry) => ({
18274
18906
  ...entry,
18275
18907
  id: fresh(entry.id),
@@ -18312,19 +18944,21 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
18312
18944
  closure: null,
18313
18945
  assessments,
18314
18946
  evidence,
18315
- decisions: decisions.map((entry) => ({
18316
- ...entry,
18317
- data: {
18318
- ...entry.data,
18319
- binding: {
18320
- ...entry.data.binding,
18321
- taskId: "",
18322
- workspaceId: destinationWorkspaceId,
18323
- contentRefs: entry.data.binding.contentRefs.map((ref) => mapRef(ref, ids))
18324
- },
18325
- consumption: null
18326
- }
18327
- })),
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
+ }),
18328
18962
  ...actionProgress ? { actionProgress } : {},
18329
18963
  findings,
18330
18964
  workers,
@@ -18362,7 +18996,7 @@ class WorkitCore {
18362
18996
  }
18363
18997
  contextRootError() {
18364
18998
  try {
18365
- if (realpathSync4(this.context.root) !== this.store.root)
18999
+ if (realpathSync5(this.context.root) !== this.store.root)
18366
19000
  return failure("invalid_input", "operation context root does not match the task store root");
18367
19001
  } catch {
18368
19002
  return failure("invalid_input", "operation context root cannot be resolved");
@@ -18407,7 +19041,7 @@ class WorkitCore {
18407
19041
  activeWorkerBlocker(task, workspace) {
18408
19042
  if (workspace.writer)
18409
19043
  return failure("recovery_required", "writer ownership must be released first");
18410
- if (task.workers.some((entry) => isUncertainWorker(entry.data.state)))
19044
+ if (task.workers.some((entry) => workerBlocksTransition("close", entry.data.state)))
18411
19045
  return failure("recovery_required", "worker state requires reconciliation");
18412
19046
  return null;
18413
19047
  }
@@ -18653,37 +19287,33 @@ class WorkitCore {
18653
19287
  const currentCandidate = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
18654
19288
  if (!currentCandidate.ok)
18655
19289
  return currentCandidate;
18656
- if (evidence.kind === "check" || evidence.kind === "review") {
18657
- evidence.beforeCandidateId ??= currentCandidate.data.id;
18658
- evidence.candidateId ??= currentCandidate.data.id;
18659
- } else {
18660
- evidence.beforeCandidateId ??= null;
18661
- evidence.candidateId ??= null;
18662
- }
19290
+ const pin = helper && helper.ok ? helper.data.data.assignment.candidateId : null;
19291
+ const bound = resolveBinding(evidence, pin, currentCandidate.data.id);
18663
19292
  if (helper?.ok) {
18664
19293
  const assignment = helper.data.data.assignment;
18665
- 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))
18666
19295
  return failure("permission_denied", "evidence is outside the worker assignment");
18667
19296
  }
18668
19297
  const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
18669
- for (const candidateId of [evidence.beforeCandidateId, evidence.candidateId]) {
19298
+ for (const candidateId of [bound.beforeCandidateId, bound.candidateId]) {
18670
19299
  if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
18671
19300
  return failure("invalid_input", "evidence candidate is not a captured candidate");
18672
19301
  }
19302
+ const boundEvidence = { ...evidence, ...bound };
18673
19303
  const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
18674
19304
  const candidates = current.candidates.some((item) => item.id === currentCandidate.data.id) ? current.candidates : [...current.candidates, currentCandidate.data];
18675
19305
  const entry = {
18676
19306
  id: newId(),
18677
19307
  recordedAt: mutation.now,
18678
19308
  provenance: provenance(this.context, "agent_reported"),
18679
- data: evidence
19309
+ data: boundEvidence
18680
19310
  };
18681
19311
  return success(mutation.revision, null, {
18682
19312
  ...current,
18683
19313
  candidates,
18684
19314
  evidence: [...current.evidence, entry],
18685
19315
  findings: current.findings.map((finding) => {
18686
- if (finding.data.disposition === "open" || evidence.result === "skipped")
19316
+ if (finding.data.disposition === "open" || boundEvidence.result === "skipped")
18687
19317
  return finding;
18688
19318
  if (finding.data.disposition !== "fixed")
18689
19319
  return finding;
@@ -18692,8 +19322,12 @@ class WorkitCore {
18692
19322
  item.evidenceId,
18693
19323
  item.status
18694
19324
  ]));
18695
- 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));
18696
- 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)));
18697
19331
  return verified && !contradicted ? finding : { ...finding, data: { ...finding.data, disposition: "open", resolution: null } };
18698
19332
  })
18699
19333
  });
@@ -18851,7 +19485,7 @@ class WorkitCore {
18851
19485
  if (input.action === "record") {
18852
19486
  if (helper?.ok) {
18853
19487
  const assignment = helper.data.data.assignment;
18854
- 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))
18855
19489
  return failure("permission_denied", "finding is outside the worker assignment");
18856
19490
  }
18857
19491
  if (input.candidateId && !task.data.candidates.some((candidate) => candidate.id === input.candidateId))
@@ -18898,7 +19532,11 @@ class WorkitCore {
18898
19532
  const evaluations = evaluateEvidence(task.data, current.data);
18899
19533
  const verified = evidence.some((entry) => {
18900
19534
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
18901
- 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
+ });
18902
19540
  });
18903
19541
  if (!verified)
18904
19542
  return failure("permission_denied", "fixed findings require passing verification evidence");
@@ -18910,7 +19548,7 @@ class WorkitCore {
18910
19548
  if (entry.data.result === "missing" || entry.data.result === "skipped")
18911
19549
  return false;
18912
19550
  const candidateMatches = finding.data.candidateId === null ? entry.data.candidateId === null : entry.data.candidateId === finding.data.candidateId || entry.data.beforeCandidateId === finding.data.candidateId;
18913
- 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)));
18914
19552
  return candidateMatches && (referenceMatches || entry.data.claim === finding.data.claim);
18915
19553
  });
18916
19554
  if (!relevant)
@@ -18988,7 +19626,7 @@ class WorkitCore {
18988
19626
  const workerId = newId();
18989
19627
  const changed = this.store.mutateTaskAndWorkspace({
18990
19628
  taskId: task.data.id,
18991
- expectedTaskRevision: input.expectedRevision,
19629
+ expectedRevision: input.expectedRevision,
18992
19630
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
18993
19631
  now: trustedNow(this.context),
18994
19632
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -19026,7 +19664,7 @@ class WorkitCore {
19026
19664
  return failure("permission_denied", "worker report is outside the worker assignment");
19027
19665
  const changed = this.store.mutateTaskAndWorkspace({
19028
19666
  taskId: task.data.id,
19029
- expectedTaskRevision: input.expectedRevision,
19667
+ expectedRevision: input.expectedRevision,
19030
19668
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19031
19669
  now: trustedNow(this.context),
19032
19670
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -19050,24 +19688,25 @@ class WorkitCore {
19050
19688
  return failure("invalid_transition", "closed task cannot cancel workers");
19051
19689
  if (entry.data.state === "unknown")
19052
19690
  return failure("recovery_required", "worker state requires recovery");
19053
- if (entry.data.state === "stopped")
19054
- return failure("invalid_transition", "stopped worker cannot be cancelled");
19055
- 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
+ }
19056
19695
  const changed = this.store.mutateTaskAndWorkspace({
19057
19696
  taskId: task.data.id,
19058
- expectedTaskRevision: input.expectedRevision,
19697
+ expectedRevision: input.expectedRevision,
19059
19698
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19060
19699
  now: trustedNow(this.context),
19061
- 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
+ }),
19062
19704
  task: (current, mutation) => success(mutation.revision, null, {
19063
19705
  ...current,
19064
19706
  workers: current.workers.map((candidate) => candidate.id === input.workerId ? {
19065
19707
  ...candidate,
19066
19708
  recordedAt: mutation.now,
19067
- data: {
19068
- ...candidate.data,
19069
- state: settles ? "stopped" : "cancelling"
19070
- }
19709
+ data: { ...candidate.data, state: "stopped" }
19071
19710
  } : candidate)
19072
19711
  })
19073
19712
  });
@@ -19164,7 +19803,7 @@ class WorkitCore {
19164
19803
  return attested;
19165
19804
  const changed = this.store.mutateTaskAndWorkspace({
19166
19805
  taskId: task.data.id,
19167
- expectedTaskRevision: input.expectedRevision,
19806
+ expectedRevision: input.expectedRevision,
19168
19807
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19169
19808
  now: trustedNow(this.context),
19170
19809
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -19330,6 +19969,12 @@ class WorkitCore {
19330
19969
  if (!sameSession2(helper.data.data.session, this.context))
19331
19970
  return failure("permission_denied", "worker session does not match the caller");
19332
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
+ }
19333
19978
  const owner = {
19334
19979
  taskId: task.data.id,
19335
19980
  workerId: helperId,
@@ -19341,7 +19986,7 @@ class WorkitCore {
19341
19986
  };
19342
19987
  const changed = this.store.mutateTaskAndWorkspace({
19343
19988
  taskId: task.data.id,
19344
- expectedTaskRevision: input.expectedRevision,
19989
+ expectedRevision: input.expectedRevision,
19345
19990
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19346
19991
  now: trustedNow(this.context),
19347
19992
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
@@ -19372,7 +20017,7 @@ class WorkitCore {
19372
20017
  }
19373
20018
  const changed = this.store.mutateTaskAndWorkspace({
19374
20019
  taskId: task.data.id,
19375
- expectedTaskRevision: input.expectedRevision,
20020
+ expectedRevision: input.expectedRevision,
19376
20021
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19377
20022
  now: trustedNow(this.context),
19378
20023
  workspace: (currentWorkspace, mutation) => success(mutation.revision, mutation.revision, { ...currentWorkspace, writer: null }),
@@ -19604,7 +20249,7 @@ class WorkitCore {
19604
20249
  if (view.task.workspaceId !== view.workspace.id)
19605
20250
  return failure("invalid_input", "task and workspace bindings are invalid");
19606
20251
  try {
19607
- if (realpathSync4(view.workspace.root) !== this.store.root)
20252
+ if (realpathSync5(view.workspace.root) !== this.store.root)
19608
20253
  return failure("invalid_input", "task view root does not match the task store");
19609
20254
  } catch {
19610
20255
  return failure("invalid_input", "task view root cannot be resolved");
@@ -19621,7 +20266,15 @@ class WorkitCore {
19621
20266
  if (seen.has(observation.workerId))
19622
20267
  return failure("invalid_input", "worker observations must be unique");
19623
20268
  seen.add(observation.workerId);
19624
- 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)
19625
20278
  return failure("revision_conflict", "worker observation is stale");
19626
20279
  if (!view.task.workers.some((entry) => entry.id === observation.workerId))
19627
20280
  return failure("permission_denied", "worker observation is not assigned");
@@ -19707,19 +20360,26 @@ class WorkitCore {
19707
20360
  return failure("permission_denied", "imported resume requires native destination approval");
19708
20361
  resumeCandidate = current.data.candidate;
19709
20362
  }
19710
- 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);
19711
20364
  if (blocker)
19712
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
+ }
19713
20373
  const changed = this.store.mutateTaskAndWorkspace({
19714
20374
  taskId: task.data.id,
19715
- expectedTaskRevision: input.expectedRevision,
20375
+ expectedRevision: input.expectedRevision,
19716
20376
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19717
20377
  now: trustedNow(this.context),
19718
20378
  workspace: (workspace, context) => success(context.revision, context.revision, workspace),
19719
20379
  task: (current, context) => success(context.revision, null, {
19720
20380
  ...current,
19721
20381
  status,
19722
- 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,
19723
20383
  progress: current.progress,
19724
20384
  pauseReason: status === "paused" ? input.reason ?? null : null
19725
20385
  })
@@ -19757,12 +20417,13 @@ class WorkitCore {
19757
20417
  return workspace;
19758
20418
  if (!workspace.data)
19759
20419
  return failure("not_found", "workspace not found");
19760
- if (workspace.data.writer || task.data.workers.some((entry) => isUncertainWorker(entry.data.state)))
19761
- 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;
19762
20423
  this.fillRevisions(input, task.data, workspace.data);
19763
20424
  const changed = this.store.mutateTaskAndWorkspace({
19764
20425
  taskId: task.data.id,
19765
- expectedTaskRevision: input.expectedRevision,
20426
+ expectedRevision: input.expectedRevision,
19766
20427
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19767
20428
  now: trustedNow(this.context),
19768
20429
  workspace: (workspace, context) => success(context.revision, context.revision, workspace),
@@ -19818,7 +20479,7 @@ class WorkitCore {
19818
20479
  return closure;
19819
20480
  const changed = this.store.mutateTaskAndWorkspace({
19820
20481
  taskId: task.data.id,
19821
- expectedTaskRevision: input.expectedRevision,
20482
+ expectedRevision: input.expectedRevision,
19822
20483
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
19823
20484
  now: trustedNow(this.context),
19824
20485
  workspace: (value, context) => success(context.revision, context.revision, value),
@@ -19855,7 +20516,7 @@ function gitRevisionParts(value) {
19855
20516
  }
19856
20517
  function resolveGitRevision(root, value) {
19857
20518
  for (const part of gitRevisionParts(value)) {
19858
- const result = run(root, "git", [
20519
+ const result = run2(root, "git", [
19859
20520
  "rev-parse",
19860
20521
  "--verify",
19861
20522
  "--quiet",
@@ -19867,23 +20528,23 @@ function resolveGitRevision(root, value) {
19867
20528
  }
19868
20529
  }
19869
20530
  function resolveInside(root, candidate) {
19870
- const base = realpathSync5(root);
19871
- const target = path7.resolve(base, candidate);
19872
- 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)) {
19873
20534
  throw new Error("path must stay inside repository root");
19874
20535
  }
19875
20536
  let ancestor = target;
19876
- while (!existsSync4(ancestor))
19877
- ancestor = path7.dirname(ancestor);
19878
- const canonicalAncestor = realpathSync5(ancestor);
19879
- 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)) {
19880
20541
  throw new Error("path must stay inside repository root");
19881
20542
  }
19882
20543
  return target;
19883
20544
  }
19884
- function run(root, executable, args, env = {}) {
19885
- const cwd = realpathSync5(root);
19886
- const result = spawnSync2(executable, args, {
20545
+ function run2(root, executable, args, env = {}) {
20546
+ const cwd = realpathSync6(root);
20547
+ const result = spawnSync3(executable, args, {
19887
20548
  cwd,
19888
20549
  encoding: "utf8",
19889
20550
  env: { ...process.env, ...env }
@@ -24523,1029 +25184,707 @@ class Protocol {
24523
25184
  this._onrequest(message, extra);
24524
25185
  } else if (isJSONRPCNotification(message)) {
24525
25186
  this._onnotification(message);
24526
- } else {
24527
- this._onerror(new Error(`Unknown message type: ${JSON.stringify(message)}`));
24528
- }
24529
- };
24530
- await this._transport.start();
24531
- }
24532
- _onclose() {
24533
- const responseHandlers = this._responseHandlers;
24534
- this._responseHandlers = new Map;
24535
- this._progressHandlers.clear();
24536
- this._taskProgressTokens.clear();
24537
- this._pendingDebouncedNotifications.clear();
24538
- for (const info of this._timeoutInfo.values()) {
24539
- clearTimeout(info.timeoutId);
24540
- }
24541
- this._timeoutInfo.clear();
24542
- for (const controller of this._requestHandlerAbortControllers.values()) {
24543
- controller.abort();
24544
- }
24545
- this._requestHandlerAbortControllers.clear();
24546
- const error = McpError.fromError(ErrorCode.ConnectionClosed, "Connection closed");
24547
- this._transport = undefined;
24548
- this.onclose?.();
24549
- for (const handler of responseHandlers.values()) {
24550
- handler(error);
24551
- }
24552
- }
24553
- _onerror(error) {
24554
- this.onerror?.(error);
24555
- }
24556
- _onnotification(notification) {
24557
- const handler = this._notificationHandlers.get(notification.method) ?? this.fallbackNotificationHandler;
24558
- if (handler === undefined) {
24559
- return;
24560
- }
24561
- Promise.resolve().then(() => handler(notification)).catch((error) => this._onerror(new Error(`Uncaught error in notification handler: ${error}`)));
24562
- }
24563
- _onrequest(request, extra) {
24564
- const handler = this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
24565
- const capturedTransport = this._transport;
24566
- const relatedTaskId = request.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
24567
- if (handler === undefined) {
24568
- const errorResponse = {
24569
- jsonrpc: "2.0",
24570
- id: request.id,
24571
- error: {
24572
- code: ErrorCode.MethodNotFound,
24573
- message: "Method not found"
24574
- }
24575
- };
24576
- if (relatedTaskId && this._taskMessageQueue) {
24577
- this._enqueueTaskMessage(relatedTaskId, {
24578
- type: "error",
24579
- message: errorResponse,
24580
- timestamp: Date.now()
24581
- }, capturedTransport?.sessionId).catch((error) => this._onerror(new Error(`Failed to enqueue error response: ${error}`)));
24582
- } else {
24583
- capturedTransport?.send(errorResponse).catch((error) => this._onerror(new Error(`Failed to send an error response: ${error}`)));
24584
- }
24585
- return;
24586
- }
24587
- const abortController = new AbortController;
24588
- this._requestHandlerAbortControllers.set(request.id, abortController);
24589
- const taskCreationParams = isTaskAugmentedRequestParams(request.params) ? request.params.task : undefined;
24590
- const taskStore = this._taskStore ? this.requestTaskStore(request, capturedTransport?.sessionId) : undefined;
24591
- const fullExtra = {
24592
- signal: abortController.signal,
24593
- sessionId: capturedTransport?.sessionId,
24594
- _meta: request.params?._meta,
24595
- sendNotification: async (notification) => {
24596
- if (abortController.signal.aborted)
24597
- return;
24598
- const notificationOptions = { relatedRequestId: request.id };
24599
- if (relatedTaskId) {
24600
- notificationOptions.relatedTask = { taskId: relatedTaskId };
24601
- }
24602
- await this.notification(notification, notificationOptions);
24603
- },
24604
- sendRequest: async (r, resultSchema, options) => {
24605
- if (abortController.signal.aborted) {
24606
- throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
24607
- }
24608
- const requestOptions = { ...options, relatedRequestId: request.id };
24609
- if (relatedTaskId && !requestOptions.relatedTask) {
24610
- requestOptions.relatedTask = { taskId: relatedTaskId };
24611
- }
24612
- const effectiveTaskId = requestOptions.relatedTask?.taskId ?? relatedTaskId;
24613
- if (effectiveTaskId && taskStore) {
24614
- await taskStore.updateTaskStatus(effectiveTaskId, "input_required");
24615
- }
24616
- return await this.request(r, resultSchema, requestOptions);
24617
- },
24618
- authInfo: extra?.authInfo,
24619
- requestId: request.id,
24620
- requestInfo: extra?.requestInfo,
24621
- taskId: relatedTaskId,
24622
- taskStore,
24623
- taskRequestedTtl: taskCreationParams?.ttl,
24624
- closeSSEStream: extra?.closeSSEStream,
24625
- closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
24626
- };
24627
- Promise.resolve().then(() => {
24628
- if (taskCreationParams) {
24629
- this.assertTaskHandlerCapability(request.method);
24630
- }
24631
- }).then(() => handler(request, fullExtra)).then(async (result) => {
24632
- if (abortController.signal.aborted) {
24633
- return;
24634
- }
24635
- const response = {
24636
- result,
24637
- jsonrpc: "2.0",
24638
- id: request.id
24639
- };
24640
- if (relatedTaskId && this._taskMessageQueue) {
24641
- await this._enqueueTaskMessage(relatedTaskId, {
24642
- type: "response",
24643
- message: response,
24644
- timestamp: Date.now()
24645
- }, capturedTransport?.sessionId);
24646
- } else {
24647
- await capturedTransport?.send(response);
24648
- }
24649
- }, async (error) => {
24650
- if (abortController.signal.aborted) {
24651
- return;
24652
- }
24653
- const errorResponse = {
24654
- jsonrpc: "2.0",
24655
- id: request.id,
24656
- error: {
24657
- code: Number.isSafeInteger(error["code"]) ? error["code"] : ErrorCode.InternalError,
24658
- message: error.message ?? "Internal error",
24659
- ...error["data"] !== undefined && { data: error["data"] }
24660
- }
24661
- };
24662
- if (relatedTaskId && this._taskMessageQueue) {
24663
- await this._enqueueTaskMessage(relatedTaskId, {
24664
- type: "error",
24665
- message: errorResponse,
24666
- timestamp: Date.now()
24667
- }, capturedTransport?.sessionId);
24668
- } else {
24669
- await capturedTransport?.send(errorResponse);
24670
- }
24671
- }).catch((error) => this._onerror(new Error(`Failed to send response: ${error}`))).finally(() => {
24672
- if (this._requestHandlerAbortControllers.get(request.id) === abortController) {
24673
- this._requestHandlerAbortControllers.delete(request.id);
24674
- }
24675
- });
24676
- }
24677
- _onprogress(notification) {
24678
- const { progressToken, ...params } = notification.params;
24679
- const messageId = Number(progressToken);
24680
- const handler = this._progressHandlers.get(messageId);
24681
- if (!handler) {
24682
- this._onerror(new Error(`Received a progress notification for an unknown token: ${JSON.stringify(notification)}`));
24683
- return;
24684
- }
24685
- const responseHandler = this._responseHandlers.get(messageId);
24686
- const timeoutInfo = this._timeoutInfo.get(messageId);
24687
- if (timeoutInfo && responseHandler && timeoutInfo.resetTimeoutOnProgress) {
24688
- try {
24689
- this._resetTimeout(messageId);
24690
- } catch (error) {
24691
- this._responseHandlers.delete(messageId);
24692
- this._progressHandlers.delete(messageId);
24693
- this._cleanupTimeout(messageId);
24694
- responseHandler(error);
24695
- return;
24696
- }
24697
- }
24698
- handler(params);
24699
- }
24700
- _onresponse(response) {
24701
- const messageId = Number(response.id);
24702
- const resolver = this._requestResolvers.get(messageId);
24703
- if (resolver) {
24704
- this._requestResolvers.delete(messageId);
24705
- if (isJSONRPCResultResponse(response)) {
24706
- resolver(response);
24707
- } else {
24708
- const error = new McpError(response.error.code, response.error.message, response.error.data);
24709
- resolver(error);
24710
- }
24711
- return;
24712
- }
24713
- const handler = this._responseHandlers.get(messageId);
24714
- if (handler === undefined) {
24715
- this._onerror(new Error(`Received a response for an unknown message ID: ${JSON.stringify(response)}`));
24716
- return;
24717
- }
24718
- this._responseHandlers.delete(messageId);
24719
- this._cleanupTimeout(messageId);
24720
- let isTaskResponse = false;
24721
- if (isJSONRPCResultResponse(response) && response.result && typeof response.result === "object") {
24722
- const result = response.result;
24723
- if (result.task && typeof result.task === "object") {
24724
- const task = result.task;
24725
- if (typeof task.taskId === "string") {
24726
- isTaskResponse = true;
24727
- this._taskProgressTokens.set(task.taskId, messageId);
24728
- }
25187
+ } else {
25188
+ this._onerror(new Error(`Unknown message type: ${JSON.stringify(message)}`));
24729
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);
24730
25201
  }
24731
- if (!isTaskResponse) {
24732
- this._progressHandlers.delete(messageId);
25202
+ this._timeoutInfo.clear();
25203
+ for (const controller of this._requestHandlerAbortControllers.values()) {
25204
+ controller.abort();
24733
25205
  }
24734
- if (isJSONRPCResultResponse(response)) {
24735
- handler(response);
24736
- } else {
24737
- 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()) {
24738
25211
  handler(error);
24739
25212
  }
24740
25213
  }
24741
- get transport() {
24742
- return this._transport;
24743
- }
24744
- async close() {
24745
- await this._transport?.close();
25214
+ _onerror(error) {
25215
+ this.onerror?.(error);
24746
25216
  }
24747
- async* requestStream(request, resultSchema, options) {
24748
- const { task } = options ?? {};
24749
- if (!task) {
24750
- try {
24751
- const result = await this.request(request, resultSchema, options);
24752
- yield { type: "result", result };
24753
- } catch (error) {
24754
- yield {
24755
- type: "error",
24756
- error: error instanceof McpError ? error : new McpError(ErrorCode.InternalError, String(error))
24757
- };
24758
- }
25217
+ _onnotification(notification) {
25218
+ const handler = this._notificationHandlers.get(notification.method) ?? this.fallbackNotificationHandler;
25219
+ if (handler === undefined) {
24759
25220
  return;
24760
25221
  }
24761
- let taskId;
24762
- try {
24763
- const createResult = await this.request(request, CreateTaskResultSchema, options);
24764
- if (createResult.task) {
24765
- taskId = createResult.task.taskId;
24766
- yield { type: "taskCreated", task: createResult.task };
24767
- } else {
24768
- throw new McpError(ErrorCode.InternalError, "Task creation did not return a task");
24769
- }
24770
- while (true) {
24771
- const task = await this.getTask({ taskId }, options);
24772
- yield { type: "taskStatus", task };
24773
- if (isTerminal(task.status)) {
24774
- if (task.status === "completed") {
24775
- const result = await this.getTaskResult({ taskId }, resultSchema, options);
24776
- yield { type: "result", result };
24777
- } else if (task.status === "failed") {
24778
- yield {
24779
- type: "error",
24780
- error: new McpError(ErrorCode.InternalError, `Task ${taskId} failed`)
24781
- };
24782
- } else if (task.status === "cancelled") {
24783
- yield {
24784
- type: "error",
24785
- error: new McpError(ErrorCode.InternalError, `Task ${taskId} was cancelled`)
24786
- };
24787
- }
24788
- return;
24789
- }
24790
- if (task.status === "input_required") {
24791
- const result = await this.getTaskResult({ taskId }, resultSchema, options);
24792
- yield { type: "result", result };
24793
- return;
24794
- }
24795
- const pollInterval = task.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
24796
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
24797
- options?.signal?.throwIfAborted();
24798
- }
24799
- } catch (error) {
24800
- yield {
24801
- type: "error",
24802
- error: error instanceof McpError ? error : new McpError(ErrorCode.InternalError, String(error))
24803
- };
24804
- }
25222
+ Promise.resolve().then(() => handler(notification)).catch((error) => this._onerror(new Error(`Uncaught error in notification handler: ${error}`)));
24805
25223
  }
24806
- request(request, resultSchema, options) {
24807
- const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
24808
- return new Promise((resolve, reject) => {
24809
- const earlyReject = (error) => {
24810
- reject(error);
24811
- };
24812
- if (!this._transport) {
24813
- earlyReject(new Error("Not connected"));
24814
- return;
24815
- }
24816
- if (this._options?.enforceStrictCapabilities === true) {
24817
- try {
24818
- this.assertCapabilityForMethod(request.method);
24819
- if (task) {
24820
- this.assertTaskCapability(request.method);
24821
- }
24822
- } catch (e) {
24823
- earlyReject(e);
24824
- return;
24825
- }
24826
- }
24827
- options?.signal?.throwIfAborted();
24828
- const messageId = this._requestMessageId++;
24829
- const jsonrpcRequest = {
24830
- ...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 = {
24831
25230
  jsonrpc: "2.0",
24832
- id: messageId
25231
+ id: request.id,
25232
+ error: {
25233
+ code: ErrorCode.MethodNotFound,
25234
+ message: "Method not found"
25235
+ }
24833
25236
  };
24834
- if (options?.onprogress) {
24835
- this._progressHandlers.set(messageId, options.onprogress);
24836
- jsonrpcRequest.params = {
24837
- ...request.params,
24838
- _meta: {
24839
- ...request.params?._meta || {},
24840
- progressToken: messageId
24841
- }
24842
- };
24843
- }
24844
- if (task) {
24845
- jsonrpcRequest.params = {
24846
- ...jsonrpcRequest.params,
24847
- task
24848
- };
24849
- }
24850
- if (relatedTask) {
24851
- jsonrpcRequest.params = {
24852
- ...jsonrpcRequest.params,
24853
- _meta: {
24854
- ...jsonrpcRequest.params?._meta || {},
24855
- [RELATED_TASK_META_KEY]: relatedTask
24856
- }
24857
- };
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}`)));
24858
25245
  }
24859
- const cancel = (reason) => {
24860
- this._responseHandlers.delete(messageId);
24861
- this._progressHandlers.delete(messageId);
24862
- this._cleanupTimeout(messageId);
24863
- this._transport?.send({
24864
- jsonrpc: "2.0",
24865
- method: "notifications/cancelled",
24866
- params: {
24867
- requestId: messageId,
24868
- reason: String(reason)
24869
- }
24870
- }, { relatedRequestId, resumptionToken, onresumptiontoken }).catch((error) => this._onerror(new Error(`Failed to send cancellation: ${error}`)));
24871
- const error = reason instanceof McpError ? reason : new McpError(ErrorCode.RequestTimeout, String(reason));
24872
- reject(error);
24873
- };
24874
- this._responseHandlers.set(messageId, (response) => {
24875
- 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)
24876
25258
  return;
25259
+ const notificationOptions = { relatedRequestId: request.id };
25260
+ if (relatedTaskId) {
25261
+ notificationOptions.relatedTask = { taskId: relatedTaskId };
24877
25262
  }
24878
- if (response instanceof Error) {
24879
- 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");
24880
25268
  }
24881
- try {
24882
- const parseResult = safeParse4(resultSchema, response.result);
24883
- if (!parseResult.success) {
24884
- reject(parseResult.error);
24885
- } else {
24886
- resolve(parseResult.data);
24887
- }
24888
- } catch (error) {
24889
- reject(error);
25269
+ const requestOptions = { ...options, relatedRequestId: request.id };
25270
+ if (relatedTaskId && !requestOptions.relatedTask) {
25271
+ requestOptions.relatedTask = { taskId: relatedTaskId };
24890
25272
  }
24891
- });
24892
- options?.signal?.addEventListener("abort", () => {
24893
- cancel(options?.signal?.reason);
24894
- });
24895
- const timeout = options?.timeout ?? DEFAULT_REQUEST_TIMEOUT_MSEC;
24896
- const timeoutHandler = () => cancel(McpError.fromError(ErrorCode.RequestTimeout, "Request timed out", { timeout }));
24897
- this._setupTimeout(messageId, timeout, options?.maxTotalTimeout, timeoutHandler, options?.resetTimeoutOnProgress ?? false);
24898
- const relatedTaskId = relatedTask?.taskId;
24899
- if (relatedTaskId) {
24900
- const responseResolver = (response) => {
24901
- const handler = this._responseHandlers.get(messageId);
24902
- if (handler) {
24903
- handler(response);
24904
- } else {
24905
- this._onerror(new Error(`Response handler missing for side-channeled request ${messageId}`));
24906
- }
24907
- };
24908
- this._requestResolvers.set(messageId, responseResolver);
24909
- this._enqueueTaskMessage(relatedTaskId, {
24910
- type: "request",
24911
- 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,
24912
25305
  timestamp: Date.now()
24913
- }).catch((error) => {
24914
- this._cleanupTimeout(messageId);
24915
- reject(error);
24916
- });
25306
+ }, capturedTransport?.sessionId);
24917
25307
  } else {
24918
- this._transport.send(jsonrpcRequest, { relatedRequestId, resumptionToken, onresumptiontoken }).catch((error) => {
24919
- this._cleanupTimeout(messageId);
24920
- reject(error);
24921
- });
25308
+ await capturedTransport?.send(response);
24922
25309
  }
24923
- });
24924
- }
24925
- async getTask(params, options) {
24926
- return this.request({ method: "tasks/get", params }, GetTaskResultSchema, options);
24927
- }
24928
- async getTaskResult(params, resultSchema, options) {
24929
- return this.request({ method: "tasks/result", params }, resultSchema, options);
24930
- }
24931
- async listTasks(params, options) {
24932
- return this.request({ method: "tasks/list", params }, ListTasksResultSchema, options);
24933
- }
24934
- async cancelTask(params, options) {
24935
- return this.request({ method: "tasks/cancel", params }, CancelTaskResultSchema, options);
24936
- }
24937
- async notification(notification, options) {
24938
- if (!this._transport) {
24939
- throw new Error("Not connected");
24940
- }
24941
- this.assertNotificationCapability(notification.method);
24942
- const relatedTaskId = options?.relatedTask?.taskId;
24943
- if (relatedTaskId) {
24944
- const jsonrpcNotification = {
24945
- ...notification,
25310
+ }, async (error) => {
25311
+ if (abortController.signal.aborted) {
25312
+ return;
25313
+ }
25314
+ const errorResponse = {
24946
25315
  jsonrpc: "2.0",
24947
- params: {
24948
- ...notification.params,
24949
- _meta: {
24950
- ...notification.params?._meta || {},
24951
- [RELATED_TASK_META_KEY]: options.relatedTask
24952
- }
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"] }
24953
25321
  }
24954
25322
  };
24955
- await this._enqueueTaskMessage(relatedTaskId, {
24956
- type: "notification",
24957
- message: jsonrpcNotification,
24958
- timestamp: Date.now()
24959
- });
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)}`));
24960
25344
  return;
24961
25345
  }
24962
- const debouncedMethods = this._options?.debouncedNotificationMethods ?? [];
24963
- const canDebounce = debouncedMethods.includes(notification.method) && !notification.params && !options?.relatedRequestId && !options?.relatedTask;
24964
- if (canDebounce) {
24965
- 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);
24966
25356
  return;
24967
25357
  }
24968
- this._pendingDebouncedNotifications.add(notification.method);
24969
- Promise.resolve().then(() => {
24970
- this._pendingDebouncedNotifications.delete(notification.method);
24971
- if (!this._transport) {
24972
- return;
24973
- }
24974
- let jsonrpcNotification = {
24975
- ...notification,
24976
- jsonrpc: "2.0"
24977
- };
24978
- if (options?.relatedTask) {
24979
- jsonrpcNotification = {
24980
- ...jsonrpcNotification,
24981
- params: {
24982
- ...jsonrpcNotification.params,
24983
- _meta: {
24984
- ...jsonrpcNotification.params?._meta || {},
24985
- [RELATED_TASK_META_KEY]: options.relatedTask
24986
- }
24987
- }
24988
- };
24989
- }
24990
- this._transport?.send(jsonrpcNotification, options).catch((error) => this._onerror(error));
24991
- });
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
+ }
24992
25372
  return;
24993
25373
  }
24994
- let jsonrpcNotification = {
24995
- ...notification,
24996
- jsonrpc: "2.0"
24997
- };
24998
- if (options?.relatedTask) {
24999
- jsonrpcNotification = {
25000
- ...jsonrpcNotification,
25001
- params: {
25002
- ...jsonrpcNotification.params,
25003
- _meta: {
25004
- ...jsonrpcNotification.params?._meta || {},
25005
- [RELATED_TASK_META_KEY]: options.relatedTask
25006
- }
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);
25007
25389
  }
25008
- };
25390
+ }
25009
25391
  }
25010
- await this._transport.send(jsonrpcNotification, options);
25011
- }
25012
- setRequestHandler(requestSchema, handler) {
25013
- const method = getMethodLiteral(requestSchema);
25014
- this.assertRequestHandlerCapability(method);
25015
- this._requestHandlers.set(method, (request, extra) => {
25016
- const parsed = parseWithCompat(requestSchema, request);
25017
- return Promise.resolve(handler(parsed, extra));
25018
- });
25019
- }
25020
- removeRequestHandler(method) {
25021
- this._requestHandlers.delete(method);
25022
- }
25023
- assertCanSetRequestHandler(method) {
25024
- if (this._requestHandlers.has(method)) {
25025
- 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);
25026
25400
  }
25027
25401
  }
25028
- setNotificationHandler(notificationSchema, handler) {
25029
- const method = getMethodLiteral(notificationSchema);
25030
- this._notificationHandlers.set(method, (notification) => {
25031
- const parsed = parseWithCompat(notificationSchema, notification);
25032
- return Promise.resolve(handler(parsed));
25033
- });
25034
- }
25035
- removeNotificationHandler(method) {
25036
- this._notificationHandlers.delete(method);
25402
+ get transport() {
25403
+ return this._transport;
25037
25404
  }
25038
- _cleanupTaskProgressHandler(taskId) {
25039
- const progressToken = this._taskProgressTokens.get(taskId);
25040
- if (progressToken !== undefined) {
25041
- this._progressHandlers.delete(progressToken);
25042
- this._taskProgressTokens.delete(taskId);
25043
- }
25405
+ async close() {
25406
+ await this._transport?.close();
25044
25407
  }
25045
- async _enqueueTaskMessage(taskId, message, sessionId) {
25046
- if (!this._taskStore || !this._taskMessageQueue) {
25047
- 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;
25048
25421
  }
25049
- const maxQueueSize = this._options?.maxTaskQueueSize;
25050
- await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
25051
- }
25052
- async _clearTaskQueue(taskId, sessionId) {
25053
- if (this._taskMessageQueue) {
25054
- const messages = await this._taskMessageQueue.dequeueAll(taskId, sessionId);
25055
- for (const message of messages) {
25056
- if (message.type === "request" && isJSONRPCRequest(message.message)) {
25057
- const requestId = message.message.id;
25058
- const resolver = this._requestResolvers.get(requestId);
25059
- if (resolver) {
25060
- resolver(new McpError(ErrorCode.InternalError, "Task cancelled or completed"));
25061
- this._requestResolvers.delete(requestId);
25062
- } else {
25063
- 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
+ };
25064
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;
25065
25455
  }
25456
+ const pollInterval = task.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
25457
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
25458
+ options?.signal?.throwIfAborted();
25066
25459
  }
25460
+ } catch (error) {
25461
+ yield {
25462
+ type: "error",
25463
+ error: error instanceof McpError ? error : new McpError(ErrorCode.InternalError, String(error))
25464
+ };
25067
25465
  }
25068
25466
  }
25069
- async _waitForTaskUpdate(taskId, signal) {
25070
- let interval = this._options?.defaultTaskPollInterval ?? 1000;
25071
- try {
25072
- const task = await this._taskStore?.getTask(taskId);
25073
- if (task?.pollInterval) {
25074
- interval = task.pollInterval;
25075
- }
25076
- } catch {}
25467
+ request(request, resultSchema, options) {
25468
+ const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
25077
25469
  return new Promise((resolve, reject) => {
25078
- if (signal.aborted) {
25079
- 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"));
25080
25475
  return;
25081
25476
  }
25082
- const timeoutId = setTimeout(resolve, interval);
25083
- signal.addEventListener("abort", () => {
25084
- clearTimeout(timeoutId);
25085
- reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
25086
- }, { once: true });
25087
- });
25088
- }
25089
- requestTaskStore(request, sessionId) {
25090
- const taskStore = this._taskStore;
25091
- if (!taskStore) {
25092
- throw new Error("No task store configured");
25093
- }
25094
- return {
25095
- createTask: async (taskParams) => {
25096
- if (!request) {
25097
- throw new Error("No request provided");
25098
- }
25099
- return await taskStore.createTask(taskParams, request.id, {
25100
- method: request.method,
25101
- params: request.params
25102
- }, sessionId);
25103
- },
25104
- getTask: async (taskId) => {
25105
- const task = await taskStore.getTask(taskId, sessionId);
25106
- if (!task) {
25107
- throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
25108
- }
25109
- return task;
25110
- },
25111
- storeTaskResult: async (taskId, status, result) => {
25112
- await taskStore.storeTaskResult(taskId, status, result, sessionId);
25113
- const task = await taskStore.getTask(taskId, sessionId);
25114
- if (task) {
25115
- const notification = TaskStatusNotificationSchema.parse({
25116
- method: "notifications/tasks/status",
25117
- params: task
25118
- });
25119
- await this.notification(notification);
25120
- if (isTerminal(task.status)) {
25121
- this._cleanupTaskProgressHandler(taskId);
25122
- }
25123
- }
25124
- },
25125
- getTaskResult: (taskId) => {
25126
- return taskStore.getTaskResult(taskId, sessionId);
25127
- },
25128
- updateTaskStatus: async (taskId, status, statusMessage) => {
25129
- const task = await taskStore.getTask(taskId, sessionId);
25130
- if (!task) {
25131
- throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
25132
- }
25133
- if (isTerminal(task.status)) {
25134
- 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.`);
25135
- }
25136
- await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId);
25137
- const updatedTask = await taskStore.getTask(taskId, sessionId);
25138
- if (updatedTask) {
25139
- const notification = TaskStatusNotificationSchema.parse({
25140
- method: "notifications/tasks/status",
25141
- params: updatedTask
25142
- });
25143
- await this.notification(notification);
25144
- if (isTerminal(updatedTask.status)) {
25145
- this._cleanupTaskProgressHandler(taskId);
25477
+ if (this._options?.enforceStrictCapabilities === true) {
25478
+ try {
25479
+ this.assertCapabilityForMethod(request.method);
25480
+ if (task) {
25481
+ this.assertTaskCapability(request.method);
25146
25482
  }
25483
+ } catch (e) {
25484
+ earlyReject(e);
25485
+ return;
25147
25486
  }
25148
- },
25149
- listTasks: (cursor) => {
25150
- return taskStore.listTasks(cursor, sessionId);
25151
25487
  }
25152
- };
25153
- }
25154
- }
25155
- function isPlainObject3(value) {
25156
- return value !== null && typeof value === "object" && !Array.isArray(value);
25157
- }
25158
- function mergeCapabilities(base, additional) {
25159
- const result = { ...base };
25160
- for (const key in additional) {
25161
- const k = key;
25162
- const addValue = additional[k];
25163
- if (addValue === undefined)
25164
- continue;
25165
- const baseValue = result[k];
25166
- if (isPlainObject3(baseValue) && isPlainObject3(addValue)) {
25167
- result[k] = { ...baseValue, ...addValue };
25168
- } else {
25169
- result[k] = addValue;
25170
- }
25171
- }
25172
- return result;
25173
- }
25174
-
25175
- // node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
25176
- var import_ajv = __toESM(require_ajv(), 1);
25177
- var import_ajv_formats = __toESM(require_dist(), 1);
25178
- function createDefaultAjvInstance() {
25179
- const ajv = new import_ajv.default({
25180
- strict: false,
25181
- validateFormats: true,
25182
- validateSchema: false,
25183
- allErrors: true
25184
- });
25185
- const addFormats = import_ajv_formats.default;
25186
- addFormats(ajv);
25187
- return ajv;
25188
- }
25189
-
25190
- class AjvJsonSchemaValidator {
25191
- constructor(ajv) {
25192
- this._ajv = ajv ?? createDefaultAjvInstance();
25193
- }
25194
- getValidator(schema) {
25195
- const ajvValidator = "$id" in schema && typeof schema.$id === "string" ? this._ajv.getSchema(schema.$id) ?? this._ajv.compile(schema) : this._ajv.compile(schema);
25196
- return (input) => {
25197
- const valid = ajvValidator(input);
25198
- if (valid) {
25199
- return {
25200
- valid: true,
25201
- data: input,
25202
- 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
+ }
25203
25503
  };
25204
- } else {
25205
- return {
25206
- valid: false,
25207
- data: undefined,
25208
- errorMessage: this._ajv.errorsText(ajvValidator.errors)
25504
+ }
25505
+ if (task) {
25506
+ jsonrpcRequest.params = {
25507
+ ...jsonrpcRequest.params,
25508
+ task
25209
25509
  };
25210
25510
  }
25211
- };
25212
- }
25213
- }
25214
-
25215
- // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
25216
- class ExperimentalServerTasks {
25217
- constructor(_server) {
25218
- this._server = _server;
25219
- }
25220
- requestStream(request, resultSchema, options) {
25221
- return this._server.requestStream(request, resultSchema, options);
25222
- }
25223
- createMessageStream(params, options) {
25224
- const clientCapabilities = this._server.getClientCapabilities();
25225
- if ((params.tools || params.toolChoice) && !clientCapabilities?.sampling?.tools) {
25226
- throw new Error("Client does not support sampling tools capability.");
25227
- }
25228
- if (params.messages.length > 0) {
25229
- const lastMessage = params.messages[params.messages.length - 1];
25230
- const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
25231
- const hasToolResults = lastContent.some((c) => c.type === "tool_result");
25232
- const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : undefined;
25233
- const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
25234
- const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
25235
- if (hasToolResults) {
25236
- if (lastContent.some((c) => c.type !== "tool_result")) {
25237
- throw new Error("The last message must contain only tool_result content if any is present");
25238
- }
25239
- if (!hasPreviousToolUse) {
25240
- throw new Error("tool_result blocks are not matching any tool_use from the previous message");
25241
- }
25511
+ if (relatedTask) {
25512
+ jsonrpcRequest.params = {
25513
+ ...jsonrpcRequest.params,
25514
+ _meta: {
25515
+ ...jsonrpcRequest.params?._meta || {},
25516
+ [RELATED_TASK_META_KEY]: relatedTask
25517
+ }
25518
+ };
25242
25519
  }
25243
- if (hasPreviousToolUse) {
25244
- const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
25245
- const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
25246
- if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
25247
- 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;
25248
25538
  }
25249
- }
25250
- }
25251
- return this.requestStream({
25252
- method: "sampling/createMessage",
25253
- params
25254
- }, CreateMessageResultSchema, options);
25255
- }
25256
- elicitInputStream(params, options) {
25257
- const clientCapabilities = this._server.getClientCapabilities();
25258
- const mode = params.mode ?? "form";
25259
- switch (mode) {
25260
- case "url": {
25261
- if (!clientCapabilities?.elicitation?.url) {
25262
- throw new Error("Client does not support url elicitation.");
25539
+ if (response instanceof Error) {
25540
+ return reject(response);
25263
25541
  }
25264
- break;
25265
- }
25266
- case "form": {
25267
- if (!clientCapabilities?.elicitation?.form) {
25268
- 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);
25269
25551
  }
25270
- 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
+ });
25271
25583
  }
25272
- }
25273
- const normalizedParams = mode === "form" && params.mode === undefined ? { ...params, mode: "form" } : params;
25274
- return this.requestStream({
25275
- method: "elicitation/create",
25276
- params: normalizedParams
25277
- }, ElicitResultSchema, options);
25278
- }
25279
- async getTask(taskId, options) {
25280
- return this._server.getTask({ taskId }, options);
25281
- }
25282
- async getTaskResult(taskId, resultSchema, options) {
25283
- return this._server.getTaskResult({ taskId }, resultSchema, options);
25284
- }
25285
- async listTasks(cursor, options) {
25286
- return this._server.listTasks(cursor ? { cursor } : undefined, options);
25287
- }
25288
- async cancelTask(taskId, options) {
25289
- return this._server.cancelTask({ taskId }, options);
25584
+ });
25290
25585
  }
25291
- }
25292
-
25293
- // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
25294
- function assertToolsCallTaskCapability(requests, method, entityName) {
25295
- if (!requests) {
25296
- 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);
25297
25588
  }
25298
- switch (method) {
25299
- case "tools/call":
25300
- if (!requests.tools?.call) {
25301
- throw new Error(`${entityName} does not support task creation for tools/call (required for ${method})`);
25302
- }
25303
- break;
25304
- default:
25305
- break;
25589
+ async getTaskResult(params, resultSchema, options) {
25590
+ return this.request({ method: "tasks/result", params }, resultSchema, options);
25306
25591
  }
25307
- }
25308
- function assertClientRequestTaskCapability(requests, method, entityName) {
25309
- if (!requests) {
25310
- 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);
25311
25594
  }
25312
- switch (method) {
25313
- case "sampling/createMessage":
25314
- if (!requests.sampling?.createMessage) {
25315
- throw new Error(`${entityName} does not support task creation for sampling/createMessage (required for ${method})`);
25316
- }
25317
- break;
25318
- case "elicitation/create":
25319
- if (!requests.elicitation?.create) {
25320
- throw new Error(`${entityName} does not support task creation for elicitation/create (required for ${method})`);
25321
- }
25322
- break;
25323
- default:
25324
- break;
25595
+ async cancelTask(params, options) {
25596
+ return this.request({ method: "tasks/cancel", params }, CancelTaskResultSchema, options);
25325
25597
  }
25326
- }
25327
-
25328
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
25329
- class Server extends Protocol {
25330
- constructor(_serverInfo, options) {
25331
- super(options);
25332
- this._serverInfo = _serverInfo;
25333
- this._loggingLevels = new Map;
25334
- this.LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index) => [level, index]));
25335
- this.isMessageIgnored = (level, sessionId) => {
25336
- const currentLevel = this._loggingLevels.get(sessionId);
25337
- return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
25338
- };
25339
- this._capabilities = options?.capabilities ?? {};
25340
- this._instructions = options?.instructions;
25341
- this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new AjvJsonSchemaValidator;
25342
- this.setRequestHandler(InitializeRequestSchema, (request) => this._oninitialize(request));
25343
- this.setNotificationHandler(InitializedNotificationSchema, () => this.oninitialized?.());
25344
- if (this._capabilities.logging) {
25345
- this.setRequestHandler(SetLevelRequestSchema, async (request, extra) => {
25346
- const transportSessionId = extra.sessionId || extra.requestInfo?.headers["mcp-session-id"] || undefined;
25347
- const { level } = request.params;
25348
- const parseResult = LoggingLevelSchema.safeParse(level);
25349
- if (parseResult.success) {
25350
- this._loggingLevels.set(transportSessionId, parseResult.data);
25351
- }
25352
- return {};
25353
- });
25598
+ async notification(notification, options) {
25599
+ if (!this._transport) {
25600
+ throw new Error("Not connected");
25354
25601
  }
25355
- }
25356
- get experimental() {
25357
- if (!this._experimental) {
25358
- this._experimental = {
25359
- 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
+ }
25360
25615
  };
25616
+ await this._enqueueTaskMessage(relatedTaskId, {
25617
+ type: "notification",
25618
+ message: jsonrpcNotification,
25619
+ timestamp: Date.now()
25620
+ });
25621
+ return;
25361
25622
  }
25362
- return this._experimental;
25363
- }
25364
- registerCapabilities(capabilities) {
25365
- if (this.transport) {
25366
- throw new Error("Cannot register capabilities after connecting to transport");
25367
- }
25368
- this._capabilities = mergeCapabilities(this._capabilities, capabilities);
25369
- }
25370
- setRequestHandler(requestSchema, handler) {
25371
- const shape = getObjectShape(requestSchema);
25372
- const methodSchema = shape?.method;
25373
- if (!methodSchema) {
25374
- throw new Error("Schema is missing a method literal");
25375
- }
25376
- const methodValue = getLiteralValue(methodSchema);
25377
- if (typeof methodValue !== "string") {
25378
- throw new Error("Schema method literal must be a string");
25379
- }
25380
- const method = methodValue;
25381
- if (method === "tools/call") {
25382
- const wrappedHandler = async (request, extra) => {
25383
- const validatedRequest = safeParse4(CallToolRequestSchema, request);
25384
- if (!validatedRequest.success) {
25385
- const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
25386
- 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;
25387
25634
  }
25388
- const { params } = validatedRequest.data;
25389
- const result = await Promise.resolve(handler(request, extra));
25390
- if (params.task) {
25391
- const taskValidationResult = safeParse4(CreateTaskResultSchema, result);
25392
- if (!taskValidationResult.success) {
25393
- const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
25394
- throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
25395
- }
25396
- 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
+ };
25397
25650
  }
25398
- const validationResult = safeParse4(CallToolResultSchema, result);
25399
- if (!validationResult.success) {
25400
- const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
25401
- 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
+ }
25402
25668
  }
25403
- return validationResult.data;
25404
25669
  };
25405
- return super.setRequestHandler(requestSchema, wrappedHandler);
25406
25670
  }
25407
- return super.setRequestHandler(requestSchema, handler);
25671
+ await this._transport.send(jsonrpcNotification, options);
25408
25672
  }
25409
- assertCapabilityForMethod(method) {
25410
- switch (method) {
25411
- case "sampling/createMessage":
25412
- if (!this._clientCapabilities?.sampling) {
25413
- throw new Error(`Client does not support sampling (required for ${method})`);
25414
- }
25415
- break;
25416
- case "elicitation/create":
25417
- if (!this._clientCapabilities?.elicitation) {
25418
- throw new Error(`Client does not support elicitation (required for ${method})`);
25419
- }
25420
- break;
25421
- case "roots/list":
25422
- if (!this._clientCapabilities?.roots) {
25423
- throw new Error(`Client does not support listing roots (required for ${method})`);
25424
- }
25425
- break;
25426
- case "ping":
25427
- 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`);
25428
25687
  }
25429
25688
  }
25430
- assertNotificationCapability(method) {
25431
- switch (method) {
25432
- case "notifications/message":
25433
- if (!this._capabilities.logging) {
25434
- throw new Error(`Server does not support logging (required for ${method})`);
25435
- }
25436
- break;
25437
- case "notifications/resources/updated":
25438
- case "notifications/resources/list_changed":
25439
- if (!this._capabilities.resources) {
25440
- throw new Error(`Server does not support notifying about resources (required for ${method})`);
25441
- }
25442
- break;
25443
- case "notifications/tools/list_changed":
25444
- if (!this._capabilities.tools) {
25445
- throw new Error(`Server does not support notifying of tool list changes (required for ${method})`);
25446
- }
25447
- break;
25448
- case "notifications/prompts/list_changed":
25449
- if (!this._capabilities.prompts) {
25450
- throw new Error(`Server does not support notifying of prompt list changes (required for ${method})`);
25451
- }
25452
- break;
25453
- case "notifications/elicitation/complete":
25454
- if (!this._clientCapabilities?.elicitation?.url) {
25455
- 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
+ }
25456
25726
  }
25457
- break;
25458
- case "notifications/cancelled":
25459
- break;
25460
- case "notifications/progress":
25461
- break;
25727
+ }
25462
25728
  }
25463
25729
  }
25464
- assertRequestHandlerCapability(method) {
25465
- if (!this._capabilities) {
25466
- 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");
25467
25754
  }
25468
- switch (method) {
25469
- case "completion/complete":
25470
- if (!this._capabilities.completions) {
25471
- 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");
25472
25759
  }
25473
- break;
25474
- case "logging/setLevel":
25475
- if (!this._capabilities.logging) {
25476
- 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");
25477
25769
  }
25478
- break;
25479
- case "prompts/get":
25480
- case "prompts/list":
25481
- if (!this._capabilities.prompts) {
25482
- 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
+ }
25483
25784
  }
25484
- break;
25485
- case "resources/list":
25486
- case "resources/templates/list":
25487
- case "resources/read":
25488
- if (!this._capabilities.resources) {
25489
- 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`);
25490
25793
  }
25491
- break;
25492
- case "tools/call":
25493
- case "tools/list":
25494
- if (!this._capabilities.tools) {
25495
- 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.`);
25496
25796
  }
25497
- break;
25498
- case "tasks/get":
25499
- case "tasks/list":
25500
- case "tasks/result":
25501
- case "tasks/cancel":
25502
- if (!this._capabilities.tasks) {
25503
- 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
+ }
25504
25808
  }
25505
- break;
25506
- case "ping":
25507
- case "initialize":
25508
- break;
25509
- }
25510
- }
25511
- assertTaskCapability(method) {
25512
- assertClientRequestTaskCapability(this._clientCapabilities?.tasks?.requests, method, "Client");
25513
- }
25514
- assertTaskHandlerCapability(method) {
25515
- if (!this._capabilities) {
25516
- return;
25517
- }
25518
- assertToolsCallTaskCapability(this._capabilities.tasks?.requests, method, "Server");
25519
- }
25520
- async _oninitialize(request) {
25521
- const requestedVersion = request.params.protocolVersion;
25522
- this._clientCapabilities = request.params.capabilities;
25523
- this._clientVersion = request.params.clientInfo;
25524
- const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : LATEST_PROTOCOL_VERSION;
25525
- return {
25526
- protocolVersion,
25527
- capabilities: this.getCapabilities(),
25528
- serverInfo: this._serverInfo,
25529
- ...this._instructions && { instructions: this._instructions }
25809
+ },
25810
+ listTasks: (cursor) => {
25811
+ return taskStore.listTasks(cursor, sessionId);
25812
+ }
25530
25813
  };
25531
25814
  }
25532
- getClientCapabilities() {
25533
- 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();
25534
25854
  }
25535
- getClientVersion() {
25536
- 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
+ };
25537
25873
  }
25538
- getCapabilities() {
25539
- 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;
25540
25880
  }
25541
- async ping() {
25542
- return this.request({ method: "ping" }, EmptyResultSchema);
25881
+ requestStream(request, resultSchema, options) {
25882
+ return this._server.requestStream(request, resultSchema, options);
25543
25883
  }
25544
- async createMessage(params, options) {
25545
- if (params.tools || params.toolChoice) {
25546
- if (!this._clientCapabilities?.sampling?.tools) {
25547
- throw new Error("Client does not support sampling tools capability.");
25548
- }
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.");
25549
25888
  }
25550
25889
  if (params.messages.length > 0) {
25551
25890
  const lastMessage = params.messages[params.messages.length - 1];
@@ -25570,807 +25909,572 @@ class Server extends Protocol {
25570
25909
  }
25571
25910
  }
25572
25911
  }
25573
- if (params.tools) {
25574
- return this.request({ method: "sampling/createMessage", params }, CreateMessageResultWithToolsSchema, options);
25575
- }
25576
- return this.request({ method: "sampling/createMessage", params }, CreateMessageResultSchema, options);
25912
+ return this.requestStream({
25913
+ method: "sampling/createMessage",
25914
+ params
25915
+ }, CreateMessageResultSchema, options);
25577
25916
  }
25578
- async elicitInput(params, options) {
25917
+ elicitInputStream(params, options) {
25918
+ const clientCapabilities = this._server.getClientCapabilities();
25579
25919
  const mode = params.mode ?? "form";
25580
25920
  switch (mode) {
25581
25921
  case "url": {
25582
- if (!this._clientCapabilities?.elicitation?.url) {
25922
+ if (!clientCapabilities?.elicitation?.url) {
25583
25923
  throw new Error("Client does not support url elicitation.");
25584
25924
  }
25585
- const urlParams = params;
25586
- return this.request({ method: "elicitation/create", params: urlParams }, ElicitResultSchema, options);
25925
+ break;
25587
25926
  }
25588
25927
  case "form": {
25589
- if (!this._clientCapabilities?.elicitation?.form) {
25928
+ if (!clientCapabilities?.elicitation?.form) {
25590
25929
  throw new Error("Client does not support form elicitation.");
25591
25930
  }
25592
- const formParams = params.mode === "form" ? params : { ...params, mode: "form" };
25593
- const result = await this.request({ method: "elicitation/create", params: formParams }, ElicitResultSchema, options);
25594
- if (result.action === "accept" && result.content && formParams.requestedSchema) {
25595
- try {
25596
- const validator = this._jsonSchemaValidator.getValidator(formParams.requestedSchema);
25597
- const validationResult = validator(result.content);
25598
- if (!validationResult.valid) {
25599
- throw new McpError(ErrorCode.InvalidParams, `Elicitation response content does not match requested schema: ${validationResult.errorMessage}`);
25600
- }
25601
- } catch (error) {
25602
- if (error instanceof McpError) {
25603
- throw error;
25604
- }
25605
- throw new McpError(ErrorCode.InternalError, `Error validating elicitation response: ${error instanceof Error ? error.message : String(error)}`);
25606
- }
25607
- }
25608
- return result;
25609
- }
25610
- }
25611
- }
25612
- createElicitationCompletionNotifier(elicitationId, options) {
25613
- if (!this._clientCapabilities?.elicitation?.url) {
25614
- throw new Error("Client does not support URL elicitation (required for notifications/elicitation/complete)");
25615
- }
25616
- return () => this.notification({
25617
- method: "notifications/elicitation/complete",
25618
- params: {
25619
- elicitationId
25620
- }
25621
- }, options);
25622
- }
25623
- async listRoots(params, options) {
25624
- return this.request({ method: "roots/list", params }, ListRootsResultSchema, options);
25625
- }
25626
- async sendLoggingMessage(params, sessionId) {
25627
- if (this._capabilities.logging) {
25628
- if (!this.isMessageIgnored(params.level, sessionId)) {
25629
- return this.notification({ method: "notifications/message", params });
25931
+ break;
25630
25932
  }
25631
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);
25632
25939
  }
25633
- async sendResourceUpdated(params) {
25634
- return this.notification({
25635
- method: "notifications/resources/updated",
25636
- params
25637
- });
25638
- }
25639
- async sendResourceListChanged() {
25640
- return this.notification({
25641
- method: "notifications/resources/list_changed"
25642
- });
25643
- }
25644
- async sendToolListChanged() {
25645
- return this.notification({ method: "notifications/tools/list_changed" });
25646
- }
25647
- async sendPromptListChanged() {
25648
- return this.notification({ method: "notifications/prompts/list_changed" });
25649
- }
25650
- }
25651
-
25652
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
25653
- import process3 from "node:process";
25654
-
25655
- // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
25656
- var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
25657
-
25658
- class ReadBuffer {
25659
- constructor(options) {
25660
- this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
25940
+ async getTask(taskId, options) {
25941
+ return this._server.getTask({ taskId }, options);
25661
25942
  }
25662
- append(chunk) {
25663
- const newSize = (this._buffer?.length ?? 0) + chunk.length;
25664
- if (newSize > this._maxBufferSize) {
25665
- this.clear();
25666
- throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
25667
- }
25668
- this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
25943
+ async getTaskResult(taskId, resultSchema, options) {
25944
+ return this._server.getTaskResult({ taskId }, resultSchema, options);
25669
25945
  }
25670
- readMessage() {
25671
- if (!this._buffer) {
25672
- return null;
25673
- }
25674
- const index = this._buffer.indexOf(`
25675
- `);
25676
- if (index === -1) {
25677
- return null;
25678
- }
25679
- const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
25680
- this._buffer = this._buffer.subarray(index + 1);
25681
- return deserializeMessage(line);
25946
+ async listTasks(cursor, options) {
25947
+ return this._server.listTasks(cursor ? { cursor } : undefined, options);
25682
25948
  }
25683
- clear() {
25684
- this._buffer = undefined;
25949
+ async cancelTask(taskId, options) {
25950
+ return this._server.cancelTask({ taskId }, options);
25685
25951
  }
25686
25952
  }
25687
- function deserializeMessage(line) {
25688
- return JSONRPCMessageSchema.parse(JSON.parse(line));
25689
- }
25690
- function serializeMessage(message) {
25691
- return JSON.stringify(message) + `
25692
- `;
25693
- }
25694
25953
 
25695
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
25696
- class StdioServerTransport {
25697
- constructor(_stdin = process3.stdin, _stdout = process3.stdout, options) {
25698
- this._stdin = _stdin;
25699
- this._stdout = _stdout;
25700
- this._started = false;
25701
- this._ondata = (chunk) => {
25702
- try {
25703
- this._readBuffer.append(chunk);
25704
- this.processReadBuffer();
25705
- } catch (error) {
25706
- this.onerror?.(error);
25707
- this.close().catch(() => {});
25708
- }
25709
- };
25710
- this._onerror = (error) => {
25711
- this.onerror?.(error);
25712
- };
25713
- this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
25714
- }
25715
- async start() {
25716
- if (this._started) {
25717
- throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
25718
- }
25719
- this._started = true;
25720
- this._stdin.on("data", this._ondata);
25721
- this._stdin.on("error", this._onerror);
25722
- }
25723
- processReadBuffer() {
25724
- while (true) {
25725
- try {
25726
- const message = this._readBuffer.readMessage();
25727
- if (message === null) {
25728
- break;
25729
- }
25730
- this.onmessage?.(message);
25731
- } catch (error) {
25732
- this.onerror?.(error);
25733
- }
25734
- }
25735
- }
25736
- async close() {
25737
- this._stdin.off("data", this._ondata);
25738
- this._stdin.off("error", this._onerror);
25739
- const remainingDataListeners = this._stdin.listenerCount("data");
25740
- if (remainingDataListeners === 0) {
25741
- this._stdin.pause();
25742
- }
25743
- this._readBuffer.clear();
25744
- 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})`);
25745
25958
  }
25746
- send(message) {
25747
- return new Promise((resolve) => {
25748
- const json = serializeMessage(message);
25749
- if (this._stdout.write(json)) {
25750
- resolve();
25751
- } else {
25752
- 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})`);
25753
25963
  }
25754
- });
25964
+ break;
25965
+ default:
25966
+ break;
25755
25967
  }
25756
25968
  }
25757
-
25758
- // packages/workit-mcp/src/server.ts
25759
- import { fileURLToPath as fileURLToPath2 } from "node:url";
25760
-
25761
- // packages/workit-core/src/core/boundary.ts
25762
- import { statSync } from "node:fs";
25763
- var EVENT = {
25764
- initialization: "initialization",
25765
- provenance: "provenance",
25766
- assets: "assets",
25767
- configurationSource: "configuration_source",
25768
- hooks: "hooks",
25769
- mcpConnection: "mcp_connection",
25770
- migration: "migration",
25771
- installSteps: "install_steps",
25772
- uncaughtFailure: "uncaught_failure",
25773
- toolsFailed: "tools_failed",
25774
- doctor: "doctor"
25775
- };
25776
- var errorDetail = (err) => {
25777
- if (err instanceof Error) {
25778
- 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})`);
25779
25972
  }
25780
- return { error_name: "unknown", error: String(err) };
25781
- };
25782
- var markSourcesLoaded = (files, loadedAt = Date.now()) => ({
25783
- loadedAt,
25784
- files: [...files]
25785
- });
25786
- var changedSourcesSinceLoad = (marker) => {
25787
- const changed = [];
25788
- for (const file of marker.files) {
25789
- try {
25790
- if (statSync(file).mtimeMs > marker.loadedAt)
25791
- changed.push(file);
25792
- } 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;
25793
25986
  }
25794
- return changed;
25795
- };
25796
-
25797
- // packages/workit-core/src/core/logger.ts
25798
- import os from "node:os";
25799
- var REDACTED = "[REDACTED]";
25800
- var SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
25801
- 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;
25802
- var URL_QUERY = /(https?:\/\/[^?#\s]+)\?[^#\s]*/g;
25803
- var SENSITIVE_WORDS = new Set([
25804
- "token",
25805
- "secret",
25806
- "password",
25807
- "passwd",
25808
- "authorization",
25809
- "credential",
25810
- "credentials",
25811
- "cookie",
25812
- "api",
25813
- "apikey",
25814
- "apitoken",
25815
- "accesstoken",
25816
- "clientsecret",
25817
- "refreshtoken",
25818
- "key",
25819
- "bearer",
25820
- "prompt",
25821
- "message",
25822
- "messages",
25823
- "content",
25824
- "body",
25825
- "args",
25826
- "argument",
25827
- "result",
25828
- "results",
25829
- "output",
25830
- "issue",
25831
- "description",
25832
- "summary",
25833
- "payload",
25834
- "script",
25835
- "command",
25836
- "text",
25837
- "query"
25838
- ]);
25839
- var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
25840
- var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25841
- var homePattern = new RegExp(`${escapeRegExp(os.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
25842
- var applyPatterns = (value) => {
25843
- let out = value;
25844
- out = out.replace(SECRET_VALUE, REDACTED);
25845
- out = out.replace(KEY_EQ_VALUE, "$1$2[REDACTED]");
25846
- out = out.replace(URL_QUERY, "$1?[REDACTED]");
25847
- out = out.replace(homePattern, "~");
25848
- if (process.platform === "win32")
25849
- out = out.replace(/\\/g, "/");
25850
- return out;
25851
- };
25852
- var redactSecrets = (value) => applyPatterns(value);
25987
+ }
25853
25988
 
25854
- // packages/workit-core/src/core/git.ts
25855
- import { execFileSync } from "node:child_process";
25856
- var run2 = (cwd, args) => {
25857
- try {
25858
- const stdout = execFileSync("git", args, {
25859
- cwd,
25860
- encoding: "utf8",
25861
- stdio: ["pipe", "pipe", "pipe"]
25862
- });
25863
- return { stdout: stdout.trimEnd(), stderr: "", exitCode: 0 };
25864
- } catch (error) {
25865
- const e = error;
25866
- return {
25867
- stdout: (e.stdout ?? "").trimEnd(),
25868
- stderr: (e.stderr ?? "").trimEnd(),
25869
- 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;
25870
25999
  };
25871
- }
25872
- };
25873
- var gitContext = (workspaceRoot, paths = []) => {
25874
- const cwd = workspaceRoot;
25875
- let failure;
25876
- const runHere = (args) => {
25877
- const result = run2(cwd, args);
25878
- if (!failure && result.exitCode !== 0) {
25879
- failure = { stderr: result.stderr, exitCode: result.exitCode };
25880
- }
25881
- return result.stdout;
25882
- };
25883
- const branch = runHere(["rev-parse", "--abbrev-ref", "HEAD"]) || "unknown";
25884
- const status_short = runHere(["status", "--porcelain"]);
25885
- const staged = [];
25886
- const unstaged = [];
25887
- const untracked = [];
25888
- for (const line of status_short.split(`
25889
- `).filter(Boolean)) {
25890
- const code = line.slice(0, 2);
25891
- const file = line.slice(3);
25892
- if (code === "??") {
25893
- untracked.push(file);
25894
- } else {
25895
- if (code[0] !== " " && code[0] !== "?")
25896
- staged.push(file);
25897
- if (code[1] !== " " && code[1] !== "?")
25898
- 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
+ });
25899
26015
  }
25900
26016
  }
25901
- const pathArgs = paths.length ? ["--", ...paths] : [];
25902
- const diff_stat = runHere(["diff", "--stat", ...pathArgs]);
25903
- const cached_stat = runHere(["diff", "--cached", "--stat", ...pathArgs]);
25904
- const stagedSet = new Set(staged);
25905
- const unstagedSet = new Set(unstaged);
25906
- const partial_staged = [...stagedSet].filter((f) => unstagedSet.has(f));
25907
- return {
25908
- workspace_root: cwd,
25909
- branch,
25910
- status_short,
25911
- staged,
25912
- unstaged,
25913
- untracked,
25914
- diff_stat: [diff_stat, cached_stat].filter(Boolean).join(`
25915
- `),
25916
- partial_staged: partial_staged.length > 0,
25917
- partial_staged_files: partial_staged,
25918
- ...failure ? { stderr: failure.stderr, exitCode: failure.exitCode } : {}
25919
- };
25920
- };
25921
-
25922
- // packages/workit-core/src/core/config.ts
25923
- import {
25924
- copyFileSync,
25925
- cpSync,
25926
- existsSync as existsSync5,
25927
- mkdirSync as mkdirSync2,
25928
- readFileSync as readFileSync4,
25929
- readdirSync as readdirSync3,
25930
- writeFileSync as writeFileSync2
25931
- } from "node:fs";
25932
- import os2 from "node:os";
25933
- import path8 from "node:path";
25934
- var diagnosticLogger;
25935
- var PRESETS = {
25936
- gitflow: {
25937
- allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
25938
- protected: ["main", "develop", "master", "prod", "production"]
25939
- },
25940
- "github-flow": { allowed: ["*"], protected: ["main"] },
25941
- "trunk-based": { allowed: ["*"], protected: ["main"] },
25942
- custom: { allowed: [], protected: [] }
25943
- };
25944
- var mergePreset = (preset, input = {}, current = {
25945
- branchPolicy: { preset, allowed: [], protected: [] }
25946
- }) => {
25947
- const defs = PRESETS[preset];
25948
- return {
25949
- preset,
25950
- allowed: preset === "custom" ? input.allowed ?? current.branchPolicy.allowed : [...defs.allowed],
25951
- protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
25952
- };
25953
- };
25954
- 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");
25955
- var migratedDir = null;
25956
- var migrationFailed = false;
25957
- var ensureConfigDir = (dir = resolveConfigDir()) => {
25958
- if (migratedDir === dir)
25959
- return dir;
25960
- if (process.env.WORKFLOW_TOOLKIT_CONFIG || process.env.WORKFLOW_TOOLKIT_CONFIG_DIR) {
25961
- migratedDir = dir;
25962
- return dir;
26017
+ get experimental() {
26018
+ if (!this._experimental) {
26019
+ this._experimental = {
26020
+ tasks: new ExperimentalServerTasks(this)
26021
+ };
26022
+ }
26023
+ return this._experimental;
25963
26024
  }
25964
- const legacy = path8.join(process.env.XDG_CONFIG_HOME || path8.join(os2.homedir(), ".config"), "workflow-toolkit");
25965
- if (!existsSync5(legacy)) {
25966
- migratedDir = dir;
25967
- 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);
25968
26030
  }
25969
- if (!migrationFailed && existsSync5(dir)) {
25970
- migratedDir = dir;
25971
- 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);
25972
26069
  }
25973
- migrationFailed = false;
25974
- mkdirSync2(dir, { recursive: true });
25975
- diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
25976
- for (const entry of readdirSync3(legacy, { withFileTypes: true })) {
25977
- const src = path8.join(legacy, entry.name);
25978
- const dest = path8.join(dir, entry.name);
25979
- if (existsSync5(dest))
25980
- continue;
25981
- try {
25982
- if (entry.isDirectory())
25983
- cpSync(src, dest, { recursive: true });
25984
- else if (entry.isFile())
25985
- copyFileSync(src, dest);
25986
- } catch (err) {
25987
- migrationFailed = true;
25988
- diagnosticLogger?.warn(EVENT.migration, { from: src, ok: false, ...errorDetail(err) });
25989
- 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;
25990
26089
  }
25991
26090
  }
25992
- if (!migrationFailed)
25993
- migratedDir = dir;
25994
- return dir;
25995
- };
25996
- var configDir = () => ensureConfigDir();
25997
- var LOCALE_RE = /^[a-z]{2,3}(-(?:[A-Z]{2}|[0-9]{3}))?$/;
25998
- var DEFAULTS = {
25999
- locale: "en",
26000
- localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
26001
- timezone: "America/Santiago",
26002
- branchPolicy: {
26003
- preset: "gitflow",
26004
- allowed: [...PRESETS.gitflow.allowed],
26005
- protected: [...PRESETS.gitflow.protected]
26006
- },
26007
- commitPolicy: { preset: "conventional" }
26008
- };
26009
- var readSafe = (p) => {
26010
- try {
26011
- return readFileSync4(p, "utf8");
26012
- } catch {
26013
- 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
+ }
26014
26124
  }
26015
- };
26016
- var isConfigObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
26017
- var parseConfigResult = (raw, file) => {
26018
- if (raw === null)
26019
- return { status: "missing", path: file };
26020
- let parsed;
26021
- try {
26022
- parsed = JSON.parse(raw);
26023
- } catch {
26024
- 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
+ }
26025
26171
  }
26026
- if (!isConfigObject(parsed)) {
26027
- return { status: "malformed", path: file, error: `${file} is not a JSON object` };
26172
+ assertTaskCapability(method) {
26173
+ assertClientRequestTaskCapability(this._clientCapabilities?.tasks?.requests, method, "Client");
26028
26174
  }
26029
- const input = parsed;
26030
- const locale = LOCALE_RE.test(String(input.locale ?? "")) ? input.locale : DEFAULTS.locale;
26031
- const preset = Object.hasOwn(PRESETS, input.branchPolicy?.preset) ? input.branchPolicy?.preset : "gitflow";
26032
- const commitPreset = COMMIT_PRESETS.includes(String(input.commitPolicy?.preset ?? "")) ? input.commitPolicy?.preset : "conventional";
26033
- return {
26034
- status: "valid",
26035
- path: file,
26036
- config: {
26037
- locale,
26038
- localeOptions: Array.isArray(input.localeOptions) ? input.localeOptions : DEFAULTS.localeOptions,
26039
- timezone: input.timezone ?? DEFAULTS.timezone,
26040
- branchPolicy: mergePreset(preset, {
26041
- allowed: Array.isArray(input.branchPolicy?.allowed) ? input.branchPolicy.allowed : undefined,
26042
- protectedNames: Array.isArray(input.branchPolicy?.protected) ? input.branchPolicy.protected : undefined
26043
- }, DEFAULTS),
26044
- commitPolicy: {
26045
- preset: commitPreset,
26046
- ...typeof input.commitPolicy?.pattern === "string" ? { pattern: input.commitPolicy.pattern } : {}
26047
- }
26048
- }
26049
- };
26050
- };
26051
- var readConfigTyped = (dir) => {
26052
- const file = path8.join(dir ?? configDir(), "config.json");
26053
- return parseConfigResult(readSafe(file), file);
26054
- };
26055
- var readConfig = () => {
26056
- const result = readConfigTyped();
26057
- if (result.status === "malformed")
26058
- throw new Error(result.error);
26059
- return result.config ?? DEFAULTS;
26060
- };
26061
- var resolveBranchPolicy = (config, workspace) => {
26062
- const wp = workspace?.branchPolicy ?? {};
26063
- const preset = Object.hasOwn(PRESETS, wp.preset) ? wp.preset : config.branchPolicy?.preset ?? "gitflow";
26064
- const merged = mergePreset(preset, {
26065
- allowed: wp.allowed,
26066
- protectedNames: wp.protected
26067
- }, config);
26068
- const allowed = merged.allowed.map((p) => new RegExp(`^${p.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i"));
26069
- return {
26070
- preset,
26071
- allowed,
26072
- protected: new Set(merged.protected.map((p) => p.toLowerCase())),
26073
- integration: wp.integration === "merge" ? "merge" : "pr",
26074
- defaultTargetBranch: preset === "github-flow" ? "main" : preset === "trunk-based" ? "master" : "develop"
26075
- };
26076
- };
26077
- var COMMIT_PRESETS = [
26078
- "conventional",
26079
- "gitmoji",
26080
- "ticket-prefix",
26081
- "freeform",
26082
- "custom",
26083
- "auto"
26084
- ];
26085
-
26086
- // packages/workit-core/src/core/workspaces.ts
26087
- import { readFileSync as readFileSync5, realpathSync as realpathSync6 } from "node:fs";
26088
- import path9 from "node:path";
26089
- var readWorkspacesResult = (dir = configDir()) => {
26090
- const file = path9.join(dir, "workspaces.json");
26091
- let raw;
26092
- try {
26093
- raw = readFileSync5(file, "utf8");
26094
- } catch {
26095
- return { status: "missing", path: file, entries: [] };
26175
+ assertTaskHandlerCapability(method) {
26176
+ if (!this._capabilities) {
26177
+ return;
26178
+ }
26179
+ assertToolsCallTaskCapability(this._capabilities.tasks?.requests, method, "Server");
26096
26180
  }
26097
- let parsed;
26098
- try {
26099
- parsed = JSON.parse(raw);
26100
- } catch {
26101
- 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
+ };
26102
26192
  }
26103
- if (!isConfigObject(parsed)) {
26104
- return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
26193
+ getClientCapabilities() {
26194
+ return this._clientCapabilities;
26105
26195
  }
26106
- const list = parsed.workspaces;
26107
- return {
26108
- status: "valid",
26109
- path: file,
26110
- entries: Array.isArray(list) ? list : []
26111
- };
26112
- };
26113
- var loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries;
26114
- var globToRegExp = (glob) => {
26115
- let out = "";
26116
- for (let i = 0;i < glob.length; i++) {
26117
- const c = glob[i];
26118
- if (c === "*") {
26119
- if (glob[i + 1] === "*") {
26120
- if (glob[i + 2] === "/") {
26121
- if (out === "")
26122
- out += "/?";
26123
- out += "(?:[^/]+/)*";
26124
- i += 2;
26125
- } else {
26126
- if (i + 2 >= glob.length) {
26127
- if (out.endsWith("/")) {
26128
- out = out.slice(0, -1);
26129
- out += "(?:/.*)?";
26130
- } else {
26131
- 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}`);
26132
26261
  }
26133
- } else {
26134
- 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)}`);
26135
26267
  }
26136
- i++;
26137
26268
  }
26138
- } else {
26139
- out += "[^/]*";
26269
+ return result;
26140
26270
  }
26141
- } else {
26142
- out += c.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
26143
26271
  }
26144
26272
  }
26145
- return new RegExp(`^${out}$`);
26146
- };
26147
- var matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/"));
26148
- var realpathOf = (p) => {
26149
- const real = realpathSync6.native ?? realpathSync6;
26150
- try {
26151
- return real(p);
26152
- } catch {
26153
- return p;
26154
- }
26155
- };
26156
- var canonicalGlob = (glob) => {
26157
- const m = /[*?[\]{]/.exec(glob);
26158
- const prefix = m ? glob.slice(0, m.index) : glob;
26159
- const rest = m ? glob.slice(m.index) : "";
26160
- if (!prefix)
26161
- return glob;
26162
- const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
26163
- return real + (prefix.endsWith("/") ? "/" : "") + rest;
26164
- };
26165
- var resolveWorkspaceFrom = (cwd, dir) => {
26166
- const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
26167
- for (const entry of loadWorkspacesFrom(dir)) {
26168
- if (!entry || typeof entry !== "object")
26169
- continue;
26170
- const ws = entry;
26171
- if (typeof ws.glob !== "string" || !ws.glob)
26172
- continue;
26173
- const glob = ws.glob.replaceAll("\\", "/");
26174
- const canonical = canonicalGlob(glob);
26175
- for (const target of targets) {
26176
- if (matchWorkspace(glob, target))
26177
- return ws;
26178
- if (canonical !== glob && matchWorkspace(canonical, target))
26179
- 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)");
26180
26276
  }
26277
+ return () => this.notification({
26278
+ method: "notifications/elicitation/complete",
26279
+ params: {
26280
+ elicitationId
26281
+ }
26282
+ }, options);
26181
26283
  }
26182
- return null;
26183
- };
26184
- var resolveWorkspace = (cwd) => resolveWorkspaceFrom(cwd, configDir());
26185
-
26186
- // packages/workit-core/src/core/vcs-config.ts
26187
- import fs7 from "node:fs";
26188
- import path10 from "node:path";
26189
- import { spawnSync as spawnSync3 } from "node:child_process";
26190
- var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
26191
- var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path10.join(configDir(), "vcs.json");
26192
- var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
26193
- var remoteProvider = (cwd) => {
26194
- const r = spawnSync3("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
26195
- if (r.status !== 0)
26196
- return null;
26197
- const url = (r.stdout ?? "").trim();
26198
- if (/github\.com[:/]/.test(url))
26199
- return "github";
26200
- if (/gitlab\.com[:/]/.test(url))
26201
- return "gitlab";
26202
- return null;
26203
- };
26204
- var readVcsConfig = () => {
26205
- const file = vcsConfigPath();
26206
- let raw;
26207
- try {
26208
- raw = fs7.readFileSync(file, "utf8");
26209
- } catch {
26210
- return { status: "missing", path: file, config: {} };
26284
+ async listRoots(params, options) {
26285
+ return this.request({ method: "roots/list", params }, ListRootsResultSchema, options);
26211
26286
  }
26212
- let parsed;
26213
- try {
26214
- parsed = JSON.parse(raw);
26215
- } catch {
26216
- 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
+ }
26217
26293
  }
26218
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
26219
- return { status: "valid", path: file, config: parsed };
26294
+ async sendResourceUpdated(params) {
26295
+ return this.notification({
26296
+ method: "notifications/resources/updated",
26297
+ params
26298
+ });
26220
26299
  }
26221
- return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
26222
- };
26223
- function vcsConfig(mode, cwd) {
26224
- const ws = resolveWorkspace(vcsCwd(cwd));
26225
- const wsVcs = ws?.vcs ?? {};
26226
- const wsYt = ws?.youtrack ?? {};
26227
- const wsIssues = ws?.issues ?? {};
26228
- const { status: cfgStatus, path: cfgPath, config: cfg, error: cfgError } = readVcsConfig();
26229
- const root = vcsCwd(cwd);
26230
- const determinateRoot = cwd !== undefined || process.env.WORKFLOW_WORKSPACE_ROOT !== undefined;
26231
- const rawProvider = wsVcs.provider ?? (determinateRoot ? remoteProvider(root) : null) ?? cfg.provider ?? null;
26232
- const provider = rawProvider === null || rawProvider === undefined ? null : String(rawProvider).toLowerCase() || null;
26233
- const wp = ws?.branchPolicy ?? {};
26234
- const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
26235
- const policyDefault = resolveBranchPolicyFor(root).defaultTargetBranch;
26236
- const defaultTarget = String(wsVcs.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : cfg.defaultTargetBranch ?? policyDefault) ?? "develop");
26237
- const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
26238
- const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
26239
- let issuesProvider = null;
26240
- let linkOnPr = null;
26241
- if (provider === "github" && typeof wsIssues.provider === "string" && wsIssues.provider.toLowerCase() === "github") {
26242
- issuesProvider = "github";
26243
- 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
+ });
26244
26304
  }
26245
- if (mode === "resolve") {
26246
- if (cfgStatus === "malformed")
26247
- return { ok: false, error: cfgError, configPath: cfgPath };
26248
- return {
26249
- ok: true,
26250
- workspace_name: ws?.name ?? null,
26251
- provider,
26252
- defaultTargetBranch: defaultTarget,
26253
- link_issues: linkIssues,
26254
- youtrack_base_url: youtrackBaseUrl,
26255
- issues_provider: issuesProvider,
26256
- link_on_pr: linkOnPr
26257
- };
26305
+ async sendToolListChanged() {
26306
+ return this.notification({ method: "notifications/tools/list_changed" });
26258
26307
  }
26259
- if (cfgStatus === "malformed") {
26260
- return { ok: false, error: cfgError, configPath: cfgPath };
26308
+ async sendPromptListChanged() {
26309
+ return this.notification({ method: "notifications/prompts/list_changed" });
26261
26310
  }
26262
- if (cfgStatus === "missing") {
26263
- 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;
26264
26322
  }
26265
- if (provider === null) {
26266
- return {
26267
- ok: false,
26268
- error: `no vcs provider configured (set vcs.provider in ${cfgPath}, match a workspace entry, or run inside a checkout with a recognized origin remote)`,
26269
- configPath: cfgPath
26270
- };
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;
26271
26330
  }
26272
- const prov = cfg[provider] ?? {};
26273
- const wsTokenFile = typeof wsVcs.tokenFile === "string" && wsVcs.tokenFile.trim() !== "" ? wsVcs.tokenFile : null;
26274
- const tokenFile = String(wsTokenFile ?? prov.tokenFile ?? path10.join(configDir(), `${provider}.token`));
26275
- const tokenPath = path10.resolve(tokenFile);
26276
- let tokenOk = false;
26277
- if (fs7.existsSync(tokenPath)) {
26278
- const token = fs7.readFileSync(tokenPath, "utf8").trim();
26279
- const placeholder = !token || token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER);
26280
- 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);
26281
26343
  }
26282
- const out = {
26283
- ok: true,
26284
- configPath: path10.resolve(cfgPath),
26285
- provider,
26286
- defaultTargetBranch: defaultTarget,
26287
- pr: cfg.pr ?? {},
26288
- tokenPath,
26289
- tokenPresent: fs7.existsSync(tokenPath),
26290
- tokenReady: tokenOk,
26291
- workspace_name: ws?.name ?? null,
26292
- link_issues: linkIssues,
26293
- youtrack_base_url: youtrackBaseUrl,
26294
- issues_provider: issuesProvider,
26295
- link_on_pr: linkOnPr
26296
- };
26297
- if (provider === "gitlab") {
26298
- out.gitlab = {
26299
- host: prov.host ?? "gitlab.com",
26300
- apiUrl: prov.apiUrl ?? "https://gitlab.com/api/v4"
26301
- };
26302
- } else if (provider === "github") {
26303
- out.github = { host: prov.host ?? "github.com" };
26344
+ clear() {
26345
+ this._buffer = undefined;
26304
26346
  }
26305
- if (mode === "summary")
26306
- delete out.tokenPath;
26307
- return out;
26308
26347
  }
26309
- function mergedPrStyle(limit = 6, cwd) {
26310
- const cfg = vcsConfig("load", cwd);
26311
- if (!cfg.ok || !cfg.tokenReady)
26312
- return { ok: false, error: "vcs not configured" };
26313
- const provider = cfg.provider;
26314
- const token = fs7.readFileSync(cfg.tokenPath, "utf8").trim();
26315
- const examples = [];
26316
- const descInfo = (desc, caseInsensitiveNotes = false) => ({
26317
- hasNotesSection: caseInsensitiveNotes ? /##\s*notes/i.test(desc) : /##\s*Notes/.test(desc),
26318
- sections: desc.split(`
26319
- `).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
26320
- descriptionPreview: desc.slice(0, 600)
26321
- });
26322
- if (provider === "gitlab") {
26323
- const remote = spawnSync3("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
26324
- if (remote.status !== 0)
26325
- return { ok: false, error: "no origin remote" };
26326
- const url = (remote.stdout ?? "").trim();
26327
- const m = /gitlab\.com[:/](.+?)(?:\.git)?$/.exec(url);
26328
- if (!m)
26329
- return { ok: false, error: "not a gitlab.com origin" };
26330
- const project = m[1];
26331
- const env = { ...process.env, GITLAB_TOKEN: token };
26332
- const run = (args) => spawnSync3("glab", ["api", ...args], { cwd, encoding: "utf8", env });
26333
- let r = run([
26334
- `projects/${project.replaceAll("/", "%2F")}/merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`
26335
- ]);
26336
- if (r.status !== 0) {
26337
- 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.");
26338
26379
  }
26339
- if (r.status !== 0)
26340
- return { ok: false, error: "could not list merge requests" };
26341
- for (const mr of JSON.parse(r.stdout ?? "[]")) {
26342
- const desc = String(mr.description ?? "").trim();
26343
- examples.push({
26344
- title: mr.title,
26345
- url: mr.web_url,
26346
- squash: mr.squash,
26347
- ...descInfo(desc, true)
26348
- });
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
+ }
26349
26395
  }
26350
- } else if (provider === "github") {
26351
- const r = spawnSync3("gh", ["pr", "list", "--state", "merged", "--limit", String(limit), "--json", "title,url,body"], { cwd, encoding: "utf8", env: { ...process.env, GH_TOKEN: token } });
26352
- if (r.status !== 0)
26353
- return { ok: false, error: "could not list pull requests" };
26354
- for (const pr of JSON.parse(r.stdout ?? "[]")) {
26355
- const desc = String(pr.body ?? "").trim();
26356
- 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();
26357
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
+ });
26358
26416
  }
26359
- return {
26360
- ok: true,
26361
- provider,
26362
- count: examples.length,
26363
- styleHints: [
26364
- "Prefer ## Summary bullets + ## Validation or ## Test plan only",
26365
- "Do not add ## Notes with branch names, commit counts, or diff stats",
26366
- "Do not paste commit log or diff stat into the body"
26367
- ],
26368
- examples
26369
- };
26370
26417
  }
26371
26418
 
26372
- // packages/workit-core/src/core/branch.ts
26373
- 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);
26374
26478
 
26375
26479
  // packages/workit-core/src/core/pr-create.ts
26376
26480
  import fs8 from "node:fs";