@mastra/factory 0.2.1-alpha.2 → 0.2.1-alpha.4

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/factory.d.ts.map +1 -1
  3. package/dist/factory.js +3462 -3583
  4. package/dist/factory.js.map +1 -1
  5. package/dist/index.js +3462 -3583
  6. package/dist/index.js.map +1 -1
  7. package/dist/integrations/base.d.ts +13 -25
  8. package/dist/integrations/base.d.ts.map +1 -1
  9. package/dist/integrations/github/integration.d.ts.map +1 -1
  10. package/dist/integrations/github/integration.js +625 -217
  11. package/dist/integrations/github/integration.js.map +1 -1
  12. package/dist/integrations/github/routes.d.ts +1 -4
  13. package/dist/integrations/github/routes.d.ts.map +1 -1
  14. package/dist/integrations/github/routes.js +113 -203
  15. package/dist/integrations/github/routes.js.map +1 -1
  16. package/dist/integrations/github/rules.d.ts +30 -0
  17. package/dist/integrations/github/rules.d.ts.map +1 -0
  18. package/dist/{rules/github-service.js → integrations/github/rules.js} +27 -8
  19. package/dist/integrations/github/rules.js.map +1 -0
  20. package/dist/integrations/linear/agent-tools.js.map +1 -1
  21. package/dist/integrations/linear/integration.d.ts.map +1 -1
  22. package/dist/integrations/linear/integration.js +352 -3
  23. package/dist/integrations/linear/integration.js.map +1 -1
  24. package/dist/integrations/linear/routes.d.ts +2 -2
  25. package/dist/integrations/linear/routes.d.ts.map +1 -1
  26. package/dist/integrations/linear/routes.js +2 -2
  27. package/dist/integrations/linear/routes.js.map +1 -1
  28. package/dist/integrations/linear/rules.d.ts +42 -0
  29. package/dist/integrations/linear/rules.d.ts.map +1 -0
  30. package/dist/{rules/linear-service.js → integrations/linear/rules.js} +14 -4
  31. package/dist/integrations/linear/rules.js.map +1 -0
  32. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  33. package/dist/integrations/platform/github/integration.js +626 -218
  34. package/dist/integrations/platform/github/integration.js.map +1 -1
  35. package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
  36. package/dist/integrations/platform/linear/integration.js +353 -3
  37. package/dist/integrations/platform/linear/integration.js.map +1 -1
  38. package/dist/routes/oauth.js +6 -39
  39. package/dist/routes/oauth.js.map +1 -1
  40. package/dist/routes/surface.d.ts +3 -1
  41. package/dist/routes/surface.d.ts.map +1 -1
  42. package/dist/routes/surface.js +230 -619
  43. package/dist/routes/surface.js.map +1 -1
  44. package/dist/routes/work-items.js.map +1 -1
  45. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  46. package/dist/storage/domains/work-items/base.js +74 -75
  47. package/dist/storage/domains/work-items/base.js.map +1 -1
  48. package/dist/storage/domains/work-items/metrics.js.map +1 -1
  49. package/package.json +5 -5
  50. package/dist/integrations/github/project-lock.d.ts +0 -100
  51. package/dist/integrations/github/project-lock.d.ts.map +0 -1
  52. package/dist/integrations/github/project-lock.js +0 -103
  53. package/dist/integrations/github/project-lock.js.map +0 -1
  54. package/dist/rules/github-service.d.ts +0 -29
  55. package/dist/rules/github-service.d.ts.map +0 -1
  56. package/dist/rules/github-service.js.map +0 -1
  57. package/dist/rules/linear-service.d.ts +0 -27
  58. package/dist/rules/linear-service.d.ts.map +0 -1
  59. package/dist/rules/linear-service.js.map +0 -1
@@ -5797,7 +5797,7 @@ var init_errors = __esm({
5797
5797
  });
5798
5798
 
5799
5799
  // ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@libsql/hrana-client/0.10.0/56906f702e597dd372b3dd88d4aef2fcfb8f7d209d434be1f2455f1e7f8c6772/node_modules/@libsql/hrana-client/lib-esm/encoding/json/decode.js
5800
- function string2(value) {
5800
+ function string(value) {
5801
5801
  if (typeof value === "string") {
5802
5802
  return value;
5803
5803
  }
@@ -5811,13 +5811,13 @@ function stringOpt(value) {
5811
5811
  }
5812
5812
  throw typeError(value, "string or null");
5813
5813
  }
5814
- function number2(value) {
5814
+ function number(value) {
5815
5815
  if (typeof value === "number") {
5816
5816
  return value;
5817
5817
  }
5818
5818
  throw typeError(value, "number");
5819
5819
  }
5820
- function boolean2(value) {
5820
+ function boolean(value) {
5821
5821
  if (typeof value === "boolean") {
5822
5822
  return value;
5823
5823
  }
@@ -5829,14 +5829,14 @@ function array(value) {
5829
5829
  }
5830
5830
  throw typeError(value, "array");
5831
5831
  }
5832
- function object2(value) {
5832
+ function object(value) {
5833
5833
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
5834
5834
  return value;
5835
5835
  }
5836
5836
  throw typeError(value, "object");
5837
5837
  }
5838
5838
  function arrayObjectsMap(value, fun) {
5839
- return array(value).map((elemValue) => fun(object2(elemValue)));
5839
+ return array(value).map((elemValue) => fun(object(elemValue)));
5840
5840
  }
5841
5841
  function typeError(value, expected) {
5842
5842
  if (value === void 0) {
@@ -5851,7 +5851,7 @@ function typeError(value, expected) {
5851
5851
  return new ProtoError(`Expected ${expected}, received ${received}`);
5852
5852
  }
5853
5853
  function readJsonObject(value, fun) {
5854
- return fun(object2(value));
5854
+ return fun(object(value));
5855
5855
  }
5856
5856
  var init_decode = __esm({
5857
5857
  "../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@libsql/hrana-client/0.10.0/56906f702e597dd372b3dd88d4aef2fcfb8f7d209d434be1f2455f1e7f8c6772/node_modules/@libsql/hrana-client/lib-esm/encoding/json/decode.js"() {
@@ -7531,14 +7531,14 @@ var init_protobuf_encode2 = __esm({
7531
7531
 
7532
7532
  // ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@libsql/hrana-client/0.10.0/56906f702e597dd372b3dd88d4aef2fcfb8f7d209d434be1f2455f1e7f8c6772/node_modules/@libsql/hrana-client/lib-esm/shared/json_decode.js
7533
7533
  function Error2(obj) {
7534
- const message = string2(obj["message"]);
7534
+ const message = string(obj["message"]);
7535
7535
  const code = stringOpt(obj["code"]);
7536
7536
  return { message, code };
7537
7537
  }
7538
7538
  function StmtResult(obj) {
7539
7539
  const cols = arrayObjectsMap(obj["cols"], Col);
7540
7540
  const rows = array(obj["rows"]).map((rowObj) => arrayObjectsMap(rowObj, Value3));
7541
- const affectedRowCount = number2(obj["affected_row_count"]);
7541
+ const affectedRowCount = number(obj["affected_row_count"]);
7542
7542
  const lastInsertRowidStr = stringOpt(obj["last_insert_rowid"]);
7543
7543
  const lastInsertRowid = lastInsertRowidStr !== void 0 ? BigInt(lastInsertRowidStr) : void 0;
7544
7544
  return { cols, rows, affectedRowCount, lastInsertRowid };
@@ -7552,37 +7552,37 @@ function BatchResult(obj) {
7552
7552
  const stepResults = /* @__PURE__ */ new Map();
7553
7553
  array(obj["step_results"]).forEach((value, i) => {
7554
7554
  if (value !== null) {
7555
- stepResults.set(i, StmtResult(object2(value)));
7555
+ stepResults.set(i, StmtResult(object(value)));
7556
7556
  }
7557
7557
  });
7558
7558
  const stepErrors = /* @__PURE__ */ new Map();
7559
7559
  array(obj["step_errors"]).forEach((value, i) => {
7560
7560
  if (value !== null) {
7561
- stepErrors.set(i, Error2(object2(value)));
7561
+ stepErrors.set(i, Error2(object(value)));
7562
7562
  }
7563
7563
  });
7564
7564
  return { stepResults, stepErrors };
7565
7565
  }
7566
7566
  function CursorEntry(obj) {
7567
- const type = string2(obj["type"]);
7567
+ const type = string(obj["type"]);
7568
7568
  if (type === "step_begin") {
7569
- const step = number2(obj["step"]);
7569
+ const step = number(obj["step"]);
7570
7570
  const cols = arrayObjectsMap(obj["cols"], Col);
7571
7571
  return { type: "step_begin", step, cols };
7572
7572
  } else if (type === "step_end") {
7573
- const affectedRowCount = number2(obj["affected_row_count"]);
7573
+ const affectedRowCount = number(obj["affected_row_count"]);
7574
7574
  const lastInsertRowidStr = stringOpt(obj["last_insert_rowid"]);
7575
7575
  const lastInsertRowid = lastInsertRowidStr !== void 0 ? BigInt(lastInsertRowidStr) : void 0;
7576
7576
  return { type: "step_end", affectedRowCount, lastInsertRowid };
7577
7577
  } else if (type === "step_error") {
7578
- const step = number2(obj["step"]);
7579
- const error = Error2(object2(obj["error"]));
7578
+ const step = number(obj["step"]);
7579
+ const error = Error2(object(obj["error"]));
7580
7580
  return { type: "step_error", step, error };
7581
7581
  } else if (type === "row") {
7582
7582
  const row = arrayObjectsMap(obj["row"], Value3);
7583
7583
  return { type: "row", row };
7584
7584
  } else if (type === "error") {
7585
- const error = Error2(object2(obj["error"]));
7585
+ const error = Error2(object(obj["error"]));
7586
7586
  return { type: "error", error };
7587
7587
  } else {
7588
7588
  throw new ProtoError("Unexpected type of CursorEntry");
@@ -7591,8 +7591,8 @@ function CursorEntry(obj) {
7591
7591
  function DescribeResult(obj) {
7592
7592
  const params = arrayObjectsMap(obj["params"], DescribeParam);
7593
7593
  const cols = arrayObjectsMap(obj["cols"], DescribeCol);
7594
- const isExplain = boolean2(obj["is_explain"]);
7595
- const isReadonly = boolean2(obj["is_readonly"]);
7594
+ const isExplain = boolean(obj["is_explain"]);
7595
+ const isReadonly = boolean(obj["is_readonly"]);
7596
7596
  return { params, cols, isExplain, isReadonly };
7597
7597
  }
7598
7598
  function DescribeParam(obj) {
@@ -7600,23 +7600,23 @@ function DescribeParam(obj) {
7600
7600
  return { name };
7601
7601
  }
7602
7602
  function DescribeCol(obj) {
7603
- const name = string2(obj["name"]);
7603
+ const name = string(obj["name"]);
7604
7604
  const decltype = stringOpt(obj["decltype"]);
7605
7605
  return { name, decltype };
7606
7606
  }
7607
7607
  function Value3(obj) {
7608
- const type = string2(obj["type"]);
7608
+ const type = string(obj["type"]);
7609
7609
  if (type === "null") {
7610
7610
  return null;
7611
7611
  } else if (type === "integer") {
7612
- const value = string2(obj["value"]);
7612
+ const value = string(obj["value"]);
7613
7613
  return BigInt(value);
7614
7614
  } else if (type === "float") {
7615
- return number2(obj["value"]);
7615
+ return number(obj["value"]);
7616
7616
  } else if (type === "text") {
7617
- return string2(obj["value"]);
7617
+ return string(obj["value"]);
7618
7618
  } else if (type === "blob") {
7619
- return gBase64.toUint8Array(string2(obj["base64"]));
7619
+ return gBase64.toUint8Array(string(obj["base64"]));
7620
7620
  } else {
7621
7621
  throw new ProtoError("Unexpected type of Value");
7622
7622
  }
@@ -7632,35 +7632,35 @@ var init_json_decode = __esm({
7632
7632
 
7633
7633
  // ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@libsql/hrana-client/0.10.0/56906f702e597dd372b3dd88d4aef2fcfb8f7d209d434be1f2455f1e7f8c6772/node_modules/@libsql/hrana-client/lib-esm/ws/json_decode.js
7634
7634
  function ServerMsg(obj) {
7635
- const type = string2(obj["type"]);
7635
+ const type = string(obj["type"]);
7636
7636
  if (type === "hello_ok") {
7637
7637
  return { type: "hello_ok" };
7638
7638
  } else if (type === "hello_error") {
7639
- const error = Error2(object2(obj["error"]));
7639
+ const error = Error2(object(obj["error"]));
7640
7640
  return { type: "hello_error", error };
7641
7641
  } else if (type === "response_ok") {
7642
- const requestId = number2(obj["request_id"]);
7643
- const response = Response(object2(obj["response"]));
7642
+ const requestId = number(obj["request_id"]);
7643
+ const response = Response(object(obj["response"]));
7644
7644
  return { type: "response_ok", requestId, response };
7645
7645
  } else if (type === "response_error") {
7646
- const requestId = number2(obj["request_id"]);
7647
- const error = Error2(object2(obj["error"]));
7646
+ const requestId = number(obj["request_id"]);
7647
+ const error = Error2(object(obj["error"]));
7648
7648
  return { type: "response_error", requestId, error };
7649
7649
  } else {
7650
7650
  throw new ProtoError("Unexpected type of ServerMsg");
7651
7651
  }
7652
7652
  }
7653
7653
  function Response(obj) {
7654
- const type = string2(obj["type"]);
7654
+ const type = string(obj["type"]);
7655
7655
  if (type === "open_stream") {
7656
7656
  return { type: "open_stream" };
7657
7657
  } else if (type === "close_stream") {
7658
7658
  return { type: "close_stream" };
7659
7659
  } else if (type === "execute") {
7660
- const result = StmtResult(object2(obj["result"]));
7660
+ const result = StmtResult(object(obj["result"]));
7661
7661
  return { type: "execute", result };
7662
7662
  } else if (type === "batch") {
7663
- const result = BatchResult(object2(obj["result"]));
7663
+ const result = BatchResult(object(obj["result"]));
7664
7664
  return { type: "batch", result };
7665
7665
  } else if (type === "open_cursor") {
7666
7666
  return { type: "open_cursor" };
@@ -7668,19 +7668,19 @@ function Response(obj) {
7668
7668
  return { type: "close_cursor" };
7669
7669
  } else if (type === "fetch_cursor") {
7670
7670
  const entries = arrayObjectsMap(obj["entries"], CursorEntry);
7671
- const done = boolean2(obj["done"]);
7671
+ const done = boolean(obj["done"]);
7672
7672
  return { type: "fetch_cursor", entries, done };
7673
7673
  } else if (type === "sequence") {
7674
7674
  return { type: "sequence" };
7675
7675
  } else if (type === "describe") {
7676
- const result = DescribeResult(object2(obj["result"]));
7676
+ const result = DescribeResult(object(obj["result"]));
7677
7677
  return { type: "describe", result };
7678
7678
  } else if (type === "store_sql") {
7679
7679
  return { type: "store_sql" };
7680
7680
  } else if (type === "close_sql") {
7681
7681
  return { type: "close_sql" };
7682
7682
  } else if (type === "get_autocommit") {
7683
- const isAutocommit = boolean2(obj["is_autocommit"]);
7683
+ const isAutocommit = boolean(obj["is_autocommit"]);
7684
7684
  return { type: "get_autocommit", isAutocommit };
7685
7685
  } else {
7686
7686
  throw new ProtoError("Unexpected type of Response");
@@ -8427,38 +8427,38 @@ function PipelineRespBody(obj) {
8427
8427
  return { baton, baseUrl, results };
8428
8428
  }
8429
8429
  function StreamResult(obj) {
8430
- const type = string2(obj["type"]);
8430
+ const type = string(obj["type"]);
8431
8431
  if (type === "ok") {
8432
- const response = StreamResponse(object2(obj["response"]));
8432
+ const response = StreamResponse(object(obj["response"]));
8433
8433
  return { type: "ok", response };
8434
8434
  } else if (type === "error") {
8435
- const error = Error2(object2(obj["error"]));
8435
+ const error = Error2(object(obj["error"]));
8436
8436
  return { type: "error", error };
8437
8437
  } else {
8438
8438
  throw new ProtoError("Unexpected type of StreamResult");
8439
8439
  }
8440
8440
  }
8441
8441
  function StreamResponse(obj) {
8442
- const type = string2(obj["type"]);
8442
+ const type = string(obj["type"]);
8443
8443
  if (type === "close") {
8444
8444
  return { type: "close" };
8445
8445
  } else if (type === "execute") {
8446
- const result = StmtResult(object2(obj["result"]));
8446
+ const result = StmtResult(object(obj["result"]));
8447
8447
  return { type: "execute", result };
8448
8448
  } else if (type === "batch") {
8449
- const result = BatchResult(object2(obj["result"]));
8449
+ const result = BatchResult(object(obj["result"]));
8450
8450
  return { type: "batch", result };
8451
8451
  } else if (type === "sequence") {
8452
8452
  return { type: "sequence" };
8453
8453
  } else if (type === "describe") {
8454
- const result = DescribeResult(object2(obj["result"]));
8454
+ const result = DescribeResult(object(obj["result"]));
8455
8455
  return { type: "describe", result };
8456
8456
  } else if (type === "store_sql") {
8457
8457
  return { type: "store_sql" };
8458
8458
  } else if (type === "close_sql") {
8459
8459
  return { type: "close_sql" };
8460
8460
  } else if (type === "get_autocommit") {
8461
- const isAutocommit = boolean2(obj["is_autocommit"]);
8461
+ const isAutocommit = boolean(obj["is_autocommit"]);
8462
8462
  return { type: "get_autocommit", isAutocommit };
8463
8463
  } else {
8464
8464
  throw new ProtoError("Unexpected type of StreamResponse");
@@ -11628,29 +11628,14 @@ var init_dist = __esm({
11628
11628
  this.vectorIndexes = this.isMemoryDb ? Promise.resolve(/* @__PURE__ */ new Set()) : this.discoverVectorIndexes();
11629
11629
  }
11630
11630
  /**
11631
- * Closes the underlying libsql client, releasing all OS file handles.
11632
- *
11633
- * For local file databases, first runs PRAGMA wal_checkpoint(TRUNCATE) and
11634
- * switches back to journal_mode=DELETE so the -wal and -shm sidecar files
11635
- * are released promptly (mirrors LibSQLStore.close()).
11636
- *
11637
- * Remote (Turso) databases skip the WAL pragmas and just close the client.
11631
+ * Closes the underlying libsql client, releasing this vector store's OS file handles.
11638
11632
  *
11639
11633
  * Safe to call more than once; subsequent calls are no-ops.
11640
11634
  */
11641
11635
  async close() {
11642
- if (this.turso.closed) {
11643
- return;
11636
+ if (!this.turso.closed) {
11637
+ this.turso.close();
11644
11638
  }
11645
- if (this.turso.protocol === "file" && !this.isMemoryDb) {
11646
- try {
11647
- await this.turso.execute("PRAGMA wal_checkpoint(TRUNCATE);");
11648
- await this.turso.execute("PRAGMA journal_mode=DELETE;");
11649
- } catch (err) {
11650
- this.logger.warn("LibSQLVector: Failed to checkpoint WAL before close.", err);
11651
- }
11652
- }
11653
- this.turso.close();
11654
11639
  }
11655
11640
  async discoverVectorIndexes() {
11656
11641
  try {
@@ -24409,32 +24394,14 @@ ${unreflectedContent}` : bufferedReflection;
24409
24394
  await this.initDomainsSequentially();
24410
24395
  }
24411
24396
  /**
24412
- * Closes the underlying libsql client, releasing all OS file handles.
24413
- *
24414
- * For local file databases, first runs PRAGMA wal_checkpoint(TRUNCATE) and
24415
- * switches back to journal_mode=DELETE so that Windows releases the -wal
24416
- * and -shm sidecar files promptly. Without this, the handles stay open
24417
- * until process exit, causing EBUSY errors when callers try to fs.rm the
24418
- * storage directory after Mastra.shutdown().
24419
- *
24420
- * Remote (Turso) databases skip the WAL pragmas and just close the client.
24397
+ * Closes the underlying libsql client, releasing this store's OS file handles.
24421
24398
  *
24422
24399
  * Safe to call more than once; subsequent calls are no-ops.
24423
24400
  */
24424
24401
  async close() {
24425
- if (this.client.closed) {
24426
- return;
24402
+ if (!this.client.closed) {
24403
+ this.client.close();
24427
24404
  }
24428
- const isLocalFileDb = this.isLocalDb || this.client.protocol === "file";
24429
- if (isLocalFileDb) {
24430
- try {
24431
- await this.client.execute("PRAGMA wal_checkpoint(TRUNCATE);");
24432
- await this.client.execute("PRAGMA journal_mode=DELETE;");
24433
- } catch (err) {
24434
- this.logger.warn("LibSQLStore: Failed to checkpoint WAL before close.", err);
24435
- }
24436
- }
24437
- this.client.close();
24438
24405
  }
24439
24406
  };
24440
24407
  LIBSQL_PROMPT = `When querying LibSQL Vector, you can ONLY use the operators listed below. Any other operators will be rejected.
@@ -24599,6 +24566,179 @@ async function ensureFactoryRuleSession(args) {
24599
24566
  return { sessionId: session.sessionId, userId };
24600
24567
  }
24601
24568
 
24569
+ // src/rules/start-coordinator.ts
24570
+ import { RequestContext } from "@mastra/core/request-context";
24571
+ import { formatSkillActivation } from "@mastra/core/workspace";
24572
+ var FactoryStartTransitionError = class extends Error {
24573
+ result;
24574
+ constructor(result) {
24575
+ super(result.reason);
24576
+ this.name = "FactoryStartTransitionError";
24577
+ this.result = result;
24578
+ }
24579
+ };
24580
+ function escapeSkillBoundary(value) {
24581
+ return value.replaceAll("</skill>", "&lt;/skill&gt;");
24582
+ }
24583
+ async function resolveKickoffMessage(session, invocation) {
24584
+ if (!invocation) return null;
24585
+ if (invocation.type === "prompt") return invocation.prompt;
24586
+ const skills = session.getWorkspace().skills;
24587
+ await skills?.maybeRefresh();
24588
+ const skill = await skills?.get(invocation.skillName);
24589
+ if (!skill || skill["user-invocable"] === false) {
24590
+ throw new Error(`Skill not found: ${invocation.skillName}.`);
24591
+ }
24592
+ const args = invocation.arguments.trim();
24593
+ const content = `${formatSkillActivation(skill)}${args ? `
24594
+
24595
+ ARGUMENTS: ${args}` : ""}`.trim();
24596
+ return `<skill name="${skill.name}">
24597
+ ${escapeSkillBoundary(content)}
24598
+ </skill>`;
24599
+ }
24600
+ async function resolveSourceSession(storage, request) {
24601
+ const session = await storage.sessions.getBySessionId(request.sessionId);
24602
+ if (!session || session.orgId !== request.orgId || session.userId !== request.userId) {
24603
+ throw new Error("Factory session not found");
24604
+ }
24605
+ const projectRepository = await storage.projectRepositories.get({
24606
+ orgId: request.orgId,
24607
+ id: session.projectRepositoryId
24608
+ });
24609
+ if (!projectRepository) throw new Error("Factory session repository not found");
24610
+ const connection = await storage.connections.get({ orgId: request.orgId, id: projectRepository.connectionId });
24611
+ if (!connection || connection.factoryProjectId !== request.factoryProjectId) {
24612
+ throw new Error("Factory session does not belong to this project");
24613
+ }
24614
+ return session;
24615
+ }
24616
+ async function configureThread(session, request) {
24617
+ const threadId = session.thread.requireId();
24618
+ await session.thread.rename({ title: request.threadTitle });
24619
+ const settings = { ...request.threadTags ?? {}, factorySessionId: request.sessionId };
24620
+ await Promise.all(Object.entries(settings).map(([key, value]) => session.thread.setSetting({ key, value })));
24621
+ return threadId;
24622
+ }
24623
+ async function applyMemorySettings(session, record) {
24624
+ if (record?.observerModelId) await session.om.observer.switchModel({ modelId: record.observerModelId });
24625
+ if (record?.reflectorModelId) await session.om.reflector.switchModel({ modelId: record.reflectorModelId });
24626
+ const state = {
24627
+ ...record?.observationThreshold != null ? { observationThreshold: record.observationThreshold } : {},
24628
+ ...record?.reflectionThreshold != null ? { reflectionThreshold: record.reflectionThreshold } : {},
24629
+ ...record?.observeAttachments != null ? { observeAttachments: record.observeAttachments } : {}
24630
+ };
24631
+ if (Object.keys(state).length > 0) await session.state.set(state);
24632
+ }
24633
+ var FactoryStartCoordinator = class {
24634
+ #controller;
24635
+ #storage;
24636
+ #transitionService;
24637
+ #sourceControl;
24638
+ #memorySettings;
24639
+ constructor(controller, storage, transitionService, sourceControl, memorySettings) {
24640
+ this.#controller = controller;
24641
+ this.#storage = storage;
24642
+ this.#transitionService = transitionService;
24643
+ this.#sourceControl = sourceControl;
24644
+ this.#memorySettings = memorySettings;
24645
+ }
24646
+ async prepare(request) {
24647
+ const storage = this.#storage;
24648
+ if (!this.#sourceControl) throw new Error("Factory source control storage is unavailable");
24649
+ const sourceSession = await resolveSourceSession(this.#sourceControl, request);
24650
+ const requestContext = request.requestContext ?? new RequestContext();
24651
+ if (!request.requestContext) {
24652
+ requestContext.set("user", { workosId: request.userId, organizationId: request.orgId });
24653
+ }
24654
+ const sessionState = {
24655
+ factoryProjectId: request.factoryProjectId,
24656
+ projectRepositoryId: sourceSession.projectRepositoryId
24657
+ };
24658
+ const session = await this.#controller.createSession({
24659
+ id: sourceSession.sessionId,
24660
+ ownerId: request.userId,
24661
+ resourceId: sourceSession.sessionId,
24662
+ threadId: sourceSession.sessionId,
24663
+ requestContext,
24664
+ tags: sessionState
24665
+ });
24666
+ await session.state.set(sessionState);
24667
+ if (this.#memorySettings) {
24668
+ try {
24669
+ const record = await this.#memorySettings.get({ orgId: request.orgId, userId: request.userId });
24670
+ await applyMemorySettings(session, record);
24671
+ } catch (error) {
24672
+ console.warn("[Factory Start] Failed to apply observational-memory settings", {
24673
+ error: error instanceof Error ? error.message : String(error)
24674
+ });
24675
+ }
24676
+ }
24677
+ if (request.defaultModelId) {
24678
+ try {
24679
+ await session.model.switch({ modelId: request.defaultModelId });
24680
+ } catch (error) {
24681
+ console.warn("[Factory Start] Failed to apply factory default model", {
24682
+ modelId: request.defaultModelId,
24683
+ error: error instanceof Error ? error.message : String(error)
24684
+ });
24685
+ }
24686
+ }
24687
+ const threadId = await configureThread(session, request);
24688
+ const kickoffMessage = await resolveKickoffMessage(session, request.invocation);
24689
+ const prepared = await storage.prepareRunStart({
24690
+ orgId: request.orgId,
24691
+ userId: request.userId,
24692
+ factoryProjectId: request.factoryProjectId,
24693
+ workItem: { id: request.workItem.id, input: request.workItem.input },
24694
+ role: request.workItem.role,
24695
+ session: { sessionId: sourceSession.sessionId, branch: sourceSession.branch, threadId },
24696
+ resourceId: sourceSession.sessionId,
24697
+ kickoffKey: request.kickoffKey,
24698
+ kickoffMessage
24699
+ });
24700
+ await session.thread.setSetting({ key: "factoryWorkItemId", value: prepared.item.id });
24701
+ let revision = prepared.item.revision;
24702
+ if (prepared.item.stages.length !== 1 || prepared.item.stages[0] !== request.destinationStage) {
24703
+ if (!this.#transitionService) throw new Error("Factory transition service is unavailable.");
24704
+ const transition = await this.#transitionService.transition({
24705
+ orgId: request.orgId,
24706
+ factoryProjectId: request.factoryProjectId,
24707
+ workItemId: prepared.item.id,
24708
+ board: prepared.item.externalSource?.type === "pull-request" ? "review" : "work",
24709
+ stage: request.destinationStage,
24710
+ expectedRevision: prepared.item.revision,
24711
+ actor: { type: "human", id: request.userId },
24712
+ ingress: { type: "human", identity: `start:${request.kickoffKey}:transition` },
24713
+ cause: "run_start"
24714
+ });
24715
+ if (transition.status === "rejected") {
24716
+ await storage.markPendingStart(prepared.binding.id, "failed", transition.reason);
24717
+ throw new FactoryStartTransitionError(transition);
24718
+ }
24719
+ revision = transition.revision;
24720
+ }
24721
+ if (kickoffMessage === null) {
24722
+ await storage.markPendingStart(prepared.binding.id, "sent");
24723
+ prepared.pendingStart.status = "sent";
24724
+ }
24725
+ return {
24726
+ workItemId: prepared.item.id,
24727
+ bindingId: prepared.binding.id,
24728
+ threadId,
24729
+ resourceId: sourceSession.sessionId,
24730
+ sessionId: sourceSession.sessionId,
24731
+ branch: sourceSession.branch,
24732
+ revision,
24733
+ kickoffStatus: prepared.pendingStart.status,
24734
+ replayed: prepared.replayed
24735
+ };
24736
+ }
24737
+ };
24738
+
24739
+ // src/rules/transition-service.ts
24740
+ import { randomUUID as randomUUID2 } from "crypto";
24741
+
24602
24742
  // src/rules/resolve.ts
24603
24743
  function resolveFactoryStageRules(rules, input) {
24604
24744
  if (input.fromStage === input.toStage && !input.initialEntry) return [];
@@ -24610,12 +24750,6 @@ function resolveFactoryStageRules(rules, input) {
24610
24750
  if (onEnter) resolved.push({ phase: "enter", handler: onEnter });
24611
24751
  return resolved;
24612
24752
  }
24613
- function resolveFactoryGithubRule(rules, event) {
24614
- return rules.github[event]?.onEvent;
24615
- }
24616
- function resolveFactoryLinearRule(rules, event) {
24617
- return rules.linear[event]?.onEvent;
24618
- }
24619
24753
 
24620
24754
  // src/rules/types.ts
24621
24755
  var FACTORY_RULE_STAGES = ["intake", "triage", "planning", "execute", "review", "done", "canceled"];
@@ -24869,508 +25003,8 @@ function validateFactoryRuleDecisions(values, causalDepth = 0) {
24869
25003
  return decisions;
24870
25004
  }
24871
25005
 
24872
- // src/rules/github-service.ts
24873
- var TRUSTED_PERMISSIONS = /* @__PURE__ */ new Set(["write", "admin"]);
24874
- var RULE_TIMEOUT_MS = 5e3;
24875
- async function withRuleTimeout(promise) {
24876
- let timeout;
24877
- try {
24878
- return await Promise.race([
24879
- promise,
24880
- new Promise((_, reject) => {
24881
- timeout = setTimeout(() => reject(new Error("FACTORY_RULE_TIMEOUT")), RULE_TIMEOUT_MS);
24882
- })
24883
- ]);
24884
- } finally {
24885
- if (timeout) clearTimeout(timeout);
24886
- }
24887
- }
24888
- function object(value) {
24889
- return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
24890
- }
24891
- function string(value) {
24892
- return typeof value === "string" && value.length > 0 ? value : void 0;
24893
- }
24894
- function number(value) {
24895
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
24896
- }
24897
- function boolean(value) {
24898
- return typeof value === "boolean" ? value : void 0;
24899
- }
24900
- function eventName(parsed) {
24901
- const action = string(parsed.payload.action);
24902
- if (parsed.event === "issues" && action === "opened") return "issueOpened";
24903
- if (parsed.event === "pull_request" && action === "opened") return "pullRequestOpened";
24904
- if (parsed.event === "pull_request" && action === "synchronize") return "pullRequestUpdated";
24905
- if (parsed.event === "pull_request" && action === "closed" && boolean(object(parsed.payload.pull_request)?.merged)) {
24906
- return "pullRequestMerged";
24907
- }
24908
- if (parsed.event === "pull_request" && action === "review_requested") return "pullRequestReviewRequested";
24909
- return void 0;
24910
- }
24911
- function canonicalSourceKey(kind, itemNumber) {
24912
- return kind === "issue" ? `github-issue:${itemNumber}` : `github-pr:${itemNumber}`;
24913
- }
24914
- function legacySourceKey(repositoryId, kind, itemNumber) {
24915
- return `github:${repositoryId}:${kind}:${itemNumber}`;
24916
- }
24917
- function provenanceTarget(repositoryId, pullRequestNumber) {
24918
- return `factory-pr-provenance:${repositoryId}:${pullRequestNumber}`;
24919
- }
24920
- function workItemSource(item) {
24921
- if (!item.externalSource) return "manual";
24922
- return item.externalSource.type === "pull-request" ? "github-pr" : "github-issue";
24923
- }
24924
- function workItemSourceKey(item) {
24925
- return item.externalSource?.externalId ?? null;
24926
- }
24927
- async function githubActor(github, input) {
24928
- let trusted = false;
24929
- try {
24930
- const permission = await github.getRepositoryCollaboratorPermission(
24931
- input.installationId,
24932
- input.repository,
24933
- input.login
24934
- );
24935
- trusted = permission !== void 0 && TRUSTED_PERMISSIONS.has(permission);
24936
- } catch {
24937
- trusted = false;
24938
- }
24939
- return { type: "github", login: input.login, trusted, factoryAuthored: input.factoryAuthored };
24940
- }
24941
- var FactoryGithubEventService = class {
24942
- constructor(options) {
24943
- this.options = options;
24944
- }
24945
- options;
24946
- async ingest(parsed) {
24947
- const event = eventName(parsed);
24948
- const repository = object(parsed.payload.repository);
24949
- const installationId = number(object(parsed.payload.installation)?.id);
24950
- const repositoryId = number(repository?.id);
24951
- const repositoryName = string(repository?.full_name);
24952
- const login = string(object(parsed.payload.sender)?.login);
24953
- if (!event || !installationId || !repositoryId || !repositoryName || !login) return { status: "ignored" };
24954
- const projects = await this.options.sourceControl.projectRepositories.listByExternalRepository({
24955
- installationExternalId: String(installationId),
24956
- repositoryExternalId: String(repositoryId)
24957
- });
24958
- if (projects.length === 0) return { status: "ignored" };
24959
- const results = [];
24960
- for (const project of projects) {
24961
- results.push(
24962
- await this.#ingestProject(parsed, event, installationId, repositoryId, repositoryName, login, project)
24963
- );
24964
- }
24965
- if (results.some((result) => result.status === "committed")) return { status: "committed" };
24966
- if (results.some((result) => result.status === "replayed")) return { status: "replayed" };
24967
- return results[0] ?? { status: "ignored" };
24968
- }
24969
- async #ingestProject(parsed, event, installationId, repositoryId, repositoryName, login, project) {
24970
- const factoryProject = await this.options.projects.get({
24971
- orgId: project.orgId,
24972
- id: project.factoryProjectId
24973
- });
24974
- if (!factoryProject) return { status: "missing" };
24975
- const issue = object(parsed.payload.issue);
24976
- const pullRequest = object(parsed.payload.pull_request);
24977
- const issueNumber = number(issue?.number);
24978
- const pullRequestNumber = number(pullRequest?.number);
24979
- const provenance = pullRequestNumber ? (await this.options.integrationStorage.subscriptions.listByTarget(
24980
- provenanceTarget(repositoryId, pullRequestNumber),
24981
- { status: "active" }
24982
- )).find((subscription) => subscription.orgId === project.orgId)?.data ?? null : null;
24983
- const relatedItem = await this.#relatedItem(
24984
- project.orgId,
24985
- project.factoryProjectId,
24986
- repositoryId,
24987
- issueNumber,
24988
- pullRequestNumber,
24989
- string(object(pullRequest?.head)?.ref),
24990
- provenance
24991
- );
24992
- const actor = await githubActor(this.options.github, {
24993
- installationId,
24994
- repository: repositoryName,
24995
- login,
24996
- factoryAuthored: provenance !== null
24997
- });
24998
- const context = {
24999
- tenant: { orgId: project.orgId, projectId: project.factoryProjectId },
25000
- actor,
25001
- ingress: { type: "github", id: `${installationId}:${parsed.deliveryId}` },
25002
- cause: `github.${event}`,
25003
- causalChain: [],
25004
- ruleSetVersion: this.options.rules.version,
25005
- ...relatedItem ? {
25006
- item: {
25007
- id: relatedItem.id,
25008
- source: workItemSource(relatedItem),
25009
- sourceKey: workItemSourceKey(relatedItem),
25010
- parentWorkItemId: relatedItem.parentWorkItemId,
25011
- title: relatedItem.title,
25012
- url: relatedItem.externalSource?.url ?? null,
25013
- stages: relatedItem.stages
25014
- },
25015
- board: relatedItem.externalSource?.type === "pull-request" ? "review" : "work",
25016
- itemRevision: relatedItem.revision
25017
- } : {},
25018
- event,
25019
- deliveryId: parsed.deliveryId,
25020
- factory: { createdAt: factoryProject.createdAt.toISOString() },
25021
- repository: { id: repositoryId, fullName: repositoryName },
25022
- ...issueNumber && string(issue?.title) && string(issue?.html_url) ? {
25023
- issue: {
25024
- number: issueNumber,
25025
- title: string(issue?.title),
25026
- url: string(issue?.html_url),
25027
- ...string(issue?.created_at) ? { createdAt: string(issue?.created_at) } : {}
25028
- }
25029
- } : {},
25030
- ...pullRequestNumber && string(pullRequest?.title) && string(pullRequest?.html_url) ? {
25031
- pullRequest: {
25032
- number: pullRequestNumber,
25033
- title: string(pullRequest?.title),
25034
- url: string(pullRequest?.html_url),
25035
- ...string(pullRequest?.created_at) ? { createdAt: string(pullRequest?.created_at) } : {},
25036
- state: string(pullRequest?.state) === "closed" ? "closed" : "open",
25037
- merged: boolean(pullRequest?.merged) ?? false,
25038
- headBranch: string(object(pullRequest?.head)?.ref) ?? "",
25039
- baseBranch: string(object(pullRequest?.base)?.ref) ?? ""
25040
- }
25041
- } : {},
25042
- ...object(parsed.payload.review) ? {
25043
- review: {
25044
- id: number(object(parsed.payload.review)?.id) ?? 0,
25045
- state: string(object(parsed.payload.review)?.state) ?? "unknown",
25046
- url: string(object(parsed.payload.review)?.html_url) ?? ""
25047
- }
25048
- } : {}
25049
- };
25050
- const rule = resolveFactoryGithubRule(this.options.rules, event);
25051
- let decision;
25052
- let decisions = [];
25053
- let outcome = { status: "accepted" };
25054
- try {
25055
- decision = rule ? await withRuleTimeout(Promise.resolve(rule(Object.freeze(context)))) : void 0;
25056
- if (decision?.type === "reject") {
25057
- outcome = { status: "rejected", code: decision.code, reason: decision.reason };
25058
- } else if (decision) {
25059
- decisions = validateFactoryRuleDecisions([decision]).map((entry) => ({ ...entry }));
25060
- }
25061
- } catch (error) {
25062
- const timedOut = error instanceof Error && error.message === "FACTORY_RULE_TIMEOUT";
25063
- outcome = {
25064
- status: "rejected",
25065
- code: timedOut ? "timeout" : "rule_error",
25066
- reason: timedOut ? "Factory rule evaluation timed out." : error instanceof Error ? error.message.slice(0, 2e3) : "Factory GitHub rule failed."
25067
- };
25068
- }
25069
- const committed = await this.options.storage.commitRuleEvaluation({
25070
- orgId: project.orgId,
25071
- factoryProjectId: project.factoryProjectId,
25072
- workItemId: relatedItem?.id ?? null,
25073
- ingress: { identity: `${installationId}:${parsed.deliveryId}`, triggerType: `github.${event}` },
25074
- ruleSetVersion: this.options.rules.version,
25075
- expectedRevision: relatedItem?.revision ?? null,
25076
- actor: { ...actor },
25077
- outcome,
25078
- decisions,
25079
- causalChain: [],
25080
- now: /* @__PURE__ */ new Date()
25081
- });
25082
- return { status: committed.status };
25083
- }
25084
- async #relatedItem(orgId, projectId, repositoryId, issueNumber, pullRequestNumber, pullRequestHeadBranch, provenance) {
25085
- const items = await this.options.storage.list({ orgId, factoryProjectId: projectId });
25086
- if (provenance) return items.find((item) => item.id === provenance.workItemId);
25087
- if (issueNumber) {
25088
- return items.find((item) => item.externalSource?.externalId === canonicalSourceKey("issue", issueNumber)) ?? items.find((item) => item.externalSource?.externalId === legacySourceKey(repositoryId, "issue", issueNumber));
25089
- }
25090
- if (pullRequestNumber) {
25091
- return items.find((item) => item.externalSource?.externalId === canonicalSourceKey("pull-request", pullRequestNumber)) ?? items.find(
25092
- (item) => item.externalSource?.externalId === legacySourceKey(repositoryId, "pull-request", pullRequestNumber)
25093
- ) ?? // Provenance fallback: a PR pushed from a work item's session branch
25094
- // belongs to that item even when no gh-pr-create provenance was
25095
- // recorded (session predating state seeding, or the PR was opened
25096
- // outside the tracked tool call). Session branches are per-item
25097
- // (`factory/issue-N`), so a head-branch match is unambiguous.
25098
- (pullRequestHeadBranch ? items.find(
25099
- (item) => item.externalSource?.type !== "pull-request" && Object.values(item.sessions).some((session) => session.branch === pullRequestHeadBranch)
25100
- ) : void 0);
25101
- }
25102
- return void 0;
25103
- }
25104
- };
25105
-
25106
- // src/rules/linear-service.ts
25107
- var RULE_TIMEOUT_MS2 = 5e3;
25108
- async function withRuleTimeout2(promise) {
25109
- let timeout;
25110
- try {
25111
- return await Promise.race([
25112
- promise,
25113
- new Promise((_, reject) => {
25114
- timeout = setTimeout(() => reject(new Error("FACTORY_RULE_TIMEOUT")), RULE_TIMEOUT_MS2);
25115
- })
25116
- ]);
25117
- } finally {
25118
- if (timeout) clearTimeout(timeout);
25119
- }
25120
- }
25121
- var FactoryLinearIssueService = class {
25122
- constructor(options) {
25123
- this.options = options;
25124
- }
25125
- options;
25126
- async ingest(input) {
25127
- const project = await this.options.projects.get({ orgId: input.orgId, id: input.factoryProjectId });
25128
- if (!project) return { status: "missing", ingested: 0 };
25129
- const items = await this.options.storage.list({ orgId: input.orgId, factoryProjectId: input.factoryProjectId });
25130
- const itemsBySourceKey = new Map(items.map((item) => [item.externalSource?.externalId, item]));
25131
- const statuses = [];
25132
- for (const issue of input.issues) {
25133
- statuses.push(await this.#ingestIssue(input, issue, itemsBySourceKey.get(`linear:${issue.identifier}`)));
25134
- }
25135
- if (statuses.some((status) => status === "committed")) return { status: "committed", ingested: statuses.length };
25136
- if (statuses.some((status) => status === "replayed")) return { status: "replayed", ingested: statuses.length };
25137
- return { status: "missing", ingested: statuses.length };
25138
- }
25139
- async #ingestIssue(input, issue, relatedItem) {
25140
- const ingressId = `linear:${issue.id}:${issue.updatedAt}`;
25141
- const actor = { type: "human", id: input.userId };
25142
- const context = {
25143
- tenant: { orgId: input.orgId, projectId: input.factoryProjectId },
25144
- actor,
25145
- ingress: { type: "linear", id: ingressId },
25146
- cause: "linear.issueObserved",
25147
- causalChain: [],
25148
- ruleSetVersion: this.options.rules.version,
25149
- ...relatedItem ? {
25150
- item: {
25151
- id: relatedItem.id,
25152
- source: "linear-issue",
25153
- sourceKey: relatedItem.externalSource?.externalId ?? null,
25154
- parentWorkItemId: relatedItem.parentWorkItemId,
25155
- title: relatedItem.title,
25156
- url: relatedItem.externalSource?.url ?? null,
25157
- stages: relatedItem.stages
25158
- },
25159
- board: "work",
25160
- itemRevision: relatedItem.revision
25161
- } : {},
25162
- event: "issueObserved",
25163
- issue
25164
- };
25165
- const rule = resolveFactoryLinearRule(this.options.rules, context.event);
25166
- let decision;
25167
- let decisions = [];
25168
- let outcome = { status: "accepted" };
25169
- try {
25170
- decision = rule ? await withRuleTimeout2(Promise.resolve(rule(Object.freeze(context)))) : void 0;
25171
- if (decision?.type === "reject") {
25172
- outcome = { status: "rejected", code: decision.code, reason: decision.reason };
25173
- } else if (decision) {
25174
- decisions = validateFactoryRuleDecisions([decision]).map((entry) => ({ ...entry }));
25175
- }
25176
- } catch (error) {
25177
- const timedOut = error instanceof Error && error.message === "FACTORY_RULE_TIMEOUT";
25178
- outcome = {
25179
- status: "rejected",
25180
- code: timedOut ? "timeout" : "rule_error",
25181
- reason: timedOut ? "Factory rule evaluation timed out." : error instanceof Error ? error.message.slice(0, 2e3) : "Factory Linear rule failed."
25182
- };
25183
- }
25184
- const committed = await this.options.storage.commitRuleEvaluation({
25185
- orgId: input.orgId,
25186
- factoryProjectId: input.factoryProjectId,
25187
- workItemId: relatedItem?.id ?? null,
25188
- ingress: { identity: ingressId, triggerType: "linear.issueObserved" },
25189
- ruleSetVersion: this.options.rules.version,
25190
- expectedRevision: relatedItem?.revision ?? null,
25191
- actor,
25192
- outcome,
25193
- decisions,
25194
- causalChain: [],
25195
- now: /* @__PURE__ */ new Date()
25196
- });
25197
- return committed.status;
25198
- }
25199
- };
25200
-
25201
- // src/rules/start-coordinator.ts
25202
- import { RequestContext } from "@mastra/core/request-context";
25203
- import { formatSkillActivation } from "@mastra/core/workspace";
25204
- var FactoryStartTransitionError = class extends Error {
25205
- result;
25206
- constructor(result) {
25207
- super(result.reason);
25208
- this.name = "FactoryStartTransitionError";
25209
- this.result = result;
25210
- }
25211
- };
25212
- function escapeSkillBoundary(value) {
25213
- return value.replaceAll("</skill>", "&lt;/skill&gt;");
25214
- }
25215
- async function resolveKickoffMessage(session, invocation) {
25216
- if (!invocation) return null;
25217
- if (invocation.type === "prompt") return invocation.prompt;
25218
- const skills = session.getWorkspace().skills;
25219
- await skills?.maybeRefresh();
25220
- const skill = await skills?.get(invocation.skillName);
25221
- if (!skill || skill["user-invocable"] === false) {
25222
- throw new Error(`Skill not found: ${invocation.skillName}.`);
25223
- }
25224
- const args = invocation.arguments.trim();
25225
- const content = `${formatSkillActivation(skill)}${args ? `
25226
-
25227
- ARGUMENTS: ${args}` : ""}`.trim();
25228
- return `<skill name="${skill.name}">
25229
- ${escapeSkillBoundary(content)}
25230
- </skill>`;
25231
- }
25232
- async function resolveSourceSession(storage, request) {
25233
- const session = await storage.sessions.getBySessionId(request.sessionId);
25234
- if (!session || session.orgId !== request.orgId || session.userId !== request.userId) {
25235
- throw new Error("Factory session not found");
25236
- }
25237
- const projectRepository = await storage.projectRepositories.get({
25238
- orgId: request.orgId,
25239
- id: session.projectRepositoryId
25240
- });
25241
- if (!projectRepository) throw new Error("Factory session repository not found");
25242
- const connection = await storage.connections.get({ orgId: request.orgId, id: projectRepository.connectionId });
25243
- if (!connection || connection.factoryProjectId !== request.factoryProjectId) {
25244
- throw new Error("Factory session does not belong to this project");
25245
- }
25246
- return session;
25247
- }
25248
- async function configureThread(session, request) {
25249
- const threadId = session.thread.requireId();
25250
- await session.thread.rename({ title: request.threadTitle });
25251
- const settings = { ...request.threadTags ?? {}, factorySessionId: request.sessionId };
25252
- await Promise.all(Object.entries(settings).map(([key, value]) => session.thread.setSetting({ key, value })));
25253
- return threadId;
25254
- }
25255
- async function applyMemorySettings(session, record) {
25256
- if (record?.observerModelId) await session.om.observer.switchModel({ modelId: record.observerModelId });
25257
- if (record?.reflectorModelId) await session.om.reflector.switchModel({ modelId: record.reflectorModelId });
25258
- const state = {
25259
- ...record?.observationThreshold != null ? { observationThreshold: record.observationThreshold } : {},
25260
- ...record?.reflectionThreshold != null ? { reflectionThreshold: record.reflectionThreshold } : {},
25261
- ...record?.observeAttachments != null ? { observeAttachments: record.observeAttachments } : {}
25262
- };
25263
- if (Object.keys(state).length > 0) await session.state.set(state);
25264
- }
25265
- var FactoryStartCoordinator = class {
25266
- #controller;
25267
- #storage;
25268
- #transitionService;
25269
- #sourceControl;
25270
- #memorySettings;
25271
- constructor(controller, storage, transitionService, sourceControl, memorySettings) {
25272
- this.#controller = controller;
25273
- this.#storage = storage;
25274
- this.#transitionService = transitionService;
25275
- this.#sourceControl = sourceControl;
25276
- this.#memorySettings = memorySettings;
25277
- }
25278
- async prepare(request) {
25279
- const storage = this.#storage;
25280
- if (!this.#sourceControl) throw new Error("Factory source control storage is unavailable");
25281
- const sourceSession = await resolveSourceSession(this.#sourceControl, request);
25282
- const requestContext = request.requestContext ?? new RequestContext();
25283
- if (!request.requestContext) {
25284
- requestContext.set("user", { workosId: request.userId, organizationId: request.orgId });
25285
- }
25286
- const sessionState = {
25287
- factoryProjectId: request.factoryProjectId,
25288
- projectRepositoryId: sourceSession.projectRepositoryId
25289
- };
25290
- const session = await this.#controller.createSession({
25291
- id: sourceSession.sessionId,
25292
- ownerId: request.userId,
25293
- resourceId: sourceSession.sessionId,
25294
- threadId: sourceSession.sessionId,
25295
- requestContext,
25296
- tags: sessionState
25297
- });
25298
- await session.state.set(sessionState);
25299
- if (this.#memorySettings) {
25300
- try {
25301
- const record = await this.#memorySettings.get({ orgId: request.orgId, userId: request.userId });
25302
- await applyMemorySettings(session, record);
25303
- } catch (error) {
25304
- console.warn("[Factory Start] Failed to apply observational-memory settings", {
25305
- error: error instanceof Error ? error.message : String(error)
25306
- });
25307
- }
25308
- }
25309
- if (request.defaultModelId) {
25310
- try {
25311
- await session.model.switch({ modelId: request.defaultModelId });
25312
- } catch (error) {
25313
- console.warn("[Factory Start] Failed to apply factory default model", {
25314
- modelId: request.defaultModelId,
25315
- error: error instanceof Error ? error.message : String(error)
25316
- });
25317
- }
25318
- }
25319
- const threadId = await configureThread(session, request);
25320
- const kickoffMessage = await resolveKickoffMessage(session, request.invocation);
25321
- const prepared = await storage.prepareRunStart({
25322
- orgId: request.orgId,
25323
- userId: request.userId,
25324
- factoryProjectId: request.factoryProjectId,
25325
- workItem: { id: request.workItem.id, input: request.workItem.input },
25326
- role: request.workItem.role,
25327
- session: { sessionId: sourceSession.sessionId, branch: sourceSession.branch, threadId },
25328
- resourceId: sourceSession.sessionId,
25329
- kickoffKey: request.kickoffKey,
25330
- kickoffMessage
25331
- });
25332
- await session.thread.setSetting({ key: "factoryWorkItemId", value: prepared.item.id });
25333
- let revision = prepared.item.revision;
25334
- if (prepared.item.stages.length !== 1 || prepared.item.stages[0] !== request.destinationStage) {
25335
- if (!this.#transitionService) throw new Error("Factory transition service is unavailable.");
25336
- const transition = await this.#transitionService.transition({
25337
- orgId: request.orgId,
25338
- factoryProjectId: request.factoryProjectId,
25339
- workItemId: prepared.item.id,
25340
- board: prepared.item.externalSource?.type === "pull-request" ? "review" : "work",
25341
- stage: request.destinationStage,
25342
- expectedRevision: prepared.item.revision,
25343
- actor: { type: "human", id: request.userId },
25344
- ingress: { type: "human", identity: `start:${request.kickoffKey}:transition` },
25345
- cause: "run_start"
25346
- });
25347
- if (transition.status === "rejected") {
25348
- await storage.markPendingStart(prepared.binding.id, "failed", transition.reason);
25349
- throw new FactoryStartTransitionError(transition);
25350
- }
25351
- revision = transition.revision;
25352
- }
25353
- if (kickoffMessage === null) {
25354
- await storage.markPendingStart(prepared.binding.id, "sent");
25355
- prepared.pendingStart.status = "sent";
25356
- }
25357
- return {
25358
- workItemId: prepared.item.id,
25359
- bindingId: prepared.binding.id,
25360
- threadId,
25361
- resourceId: sourceSession.sessionId,
25362
- sessionId: sourceSession.sessionId,
25363
- branch: sourceSession.branch,
25364
- revision,
25365
- kickoffStatus: prepared.pendingStart.status,
25366
- replayed: prepared.replayed
25367
- };
25368
- }
25369
- };
25370
-
25371
25006
  // src/rules/transition-service.ts
25372
- import { randomUUID as randomUUID2 } from "crypto";
25373
- var RULE_TIMEOUT_MS3 = 5e3;
25007
+ var RULE_TIMEOUT_MS = 5e3;
25374
25008
  var MAX_REJECTION_REASON = 512;
25375
25009
  function rejection(transitionId, itemId, code, reason) {
25376
25010
  return { status: "rejected", transitionId, itemId, code, reason: reason.slice(0, MAX_REJECTION_REASON) };
@@ -25391,7 +25025,7 @@ function currentStage(stages) {
25391
25025
  const stage = stages[0];
25392
25026
  return FACTORY_RULE_STAGES.includes(stage) ? stage : void 0;
25393
25027
  }
25394
- function workItemSource2(source) {
25028
+ function workItemSource(source) {
25395
25029
  if (!source) return "manual";
25396
25030
  if (source.integrationId === "linear") return "linear-issue";
25397
25031
  return source.type === "pull-request" ? "github-pr" : "github-issue";
@@ -25411,7 +25045,7 @@ function ruleFailure(error) {
25411
25045
  reason: error instanceof Error ? `Factory rule failed: ${error.message}` : "Factory rule failed."
25412
25046
  };
25413
25047
  }
25414
- async function withRuleTimeout3(operation, timeoutMs) {
25048
+ async function withRuleTimeout(operation, timeoutMs) {
25415
25049
  let timer;
25416
25050
  const timeout = new Promise((_, reject) => {
25417
25051
  timer = setTimeout(() => reject(new Error("FACTORY_RULE_TIMEOUT")), timeoutMs);
@@ -25429,7 +25063,7 @@ var FactoryTransitionService = class {
25429
25063
  constructor(options) {
25430
25064
  this.#rules = options.rules;
25431
25065
  this.#storage = options.storage;
25432
- this.#timeoutMs = options.timeoutMs ?? RULE_TIMEOUT_MS3;
25066
+ this.#timeoutMs = options.timeoutMs ?? RULE_TIMEOUT_MS;
25433
25067
  }
25434
25068
  get ruleSetVersion() {
25435
25069
  return this.#rules.version;
@@ -25454,7 +25088,7 @@ var FactoryTransitionService = class {
25454
25088
  "Factory rule causal depth exceeded."
25455
25089
  );
25456
25090
  }
25457
- const itemSource = workItemSource2(item.externalSource);
25091
+ const itemSource = workItemSource(item.externalSource);
25458
25092
  const source = factoryRuleSourceForWorkItem(itemSource);
25459
25093
  if (request.board === "review" !== (source === "pullRequest")) {
25460
25094
  return this.#commitRejection(
@@ -25497,7 +25131,7 @@ var FactoryTransitionService = class {
25497
25131
  };
25498
25132
  let evaluation;
25499
25133
  try {
25500
- evaluation = await withRuleTimeout3(
25134
+ evaluation = await withRuleTimeout(
25501
25135
  (async () => {
25502
25136
  const decisions = [];
25503
25137
  for (const rule of resolveFactoryStageRules(this.#rules, {
@@ -28816,6 +28450,7 @@ function buildIntegrationContext(deps, integrationId) {
28816
28450
  projects: deps.domains.projects,
28817
28451
  intake: deps.domains.intake
28818
28452
  },
28453
+ ...deps.factoryReady ? { rules: { config: deps.rules, workItems: deps.domains.workItems } } : {},
28819
28454
  ...deps.emitAudit ? { hooks: { emitAudit: deps.emitAudit } } : {}
28820
28455
  };
28821
28456
  }
@@ -28866,36 +28501,12 @@ function assembleFactoryApiRoutes(deps) {
28866
28501
  const emitAudit = (args) => deps.audit.emit(args);
28867
28502
  const registrations = deps.integrations ?? [];
28868
28503
  const githubRegistration = registrations.find(({ integration }) => integration.id === "github");
28869
- const linearRegistration = registrations.find(({ integration }) => integration.id === "linear");
28870
28504
  const githubStorage = githubRegistration ? deps.sourceControlStorage.forIntegration("github") : void 0;
28871
28505
  const githubIntegration = githubRegistration?.integration;
28872
- const workItems = deps.factoryReady ? deps.domains.workItems : void 0;
28873
- const githubEventService = githubIntegration && githubStorage && workItems ? new FactoryGithubEventService({
28874
- github: githubIntegration,
28875
- sourceControl: githubStorage,
28876
- integrationStorage: deps.integrationStorage.forIntegration("github"),
28877
- projects: deps.domains.projects,
28878
- storage: workItems,
28879
- rules: deps.rules
28880
- }) : void 0;
28881
- const linearIssueService = linearRegistration && workItems ? new FactoryLinearIssueService({
28882
- projects: deps.domains.projects,
28883
- storage: workItems,
28884
- rules: deps.rules
28885
- }) : void 0;
28886
28506
  const integrationRoutes = registrations.flatMap((registration) => {
28887
28507
  const { integration } = registration;
28888
28508
  if (!deps.stateSigner) return disabledIntegrationStatusRoutes(deps, integration.id, true);
28889
28509
  const context = buildIntegrationContext({ ...deps, stateSigner: deps.stateSigner, emitAudit }, integration.id);
28890
- if (integration.id === "github") {
28891
- context.hooks = {
28892
- ...context.hooks,
28893
- ...githubEventService ? { ingestGithubEvent: (event) => githubEventService.ingest(event) } : {}
28894
- };
28895
- }
28896
- if (integration.id === "linear" && linearIssueService) {
28897
- context.hooks = { ...context.hooks, ingestLinearIssues: (input) => linearIssueService.ingest(input) };
28898
- }
28899
28510
  return guardIntegrationRoutes({ ...registration, routes: integration.routes(context) });
28900
28511
  });
28901
28512
  const absentStubs = ["github", "linear"].filter((id) => !registrations.some(({ integration }) => integration.id === id)).flatMap((id) => disabledIntegrationStatusRoutes(deps, id));