fraim-hub 2.0.305 → 2.0.307

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.
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports._internal = void 0;
6
7
  exports.certPaths = certPaths;
7
8
  exports.loadOrCreateCert = loadOrCreateCert;
8
9
  exports.trustCert = trustCert;
@@ -49,6 +50,15 @@ async function loadOrCreateCert() {
49
50
  // HTTPS loopback certificate to be trusted before it can render the task pane.
50
51
  return { key: pem.private, cert: pem.cert };
51
52
  }
53
+ /**
54
+ * Test seam (issue #1589): the real OS command invocation goes through this mutable,
55
+ * module-owned indirection point instead of a bare `spawnSync` call, so a test can swap in
56
+ * a fake implementation and assert on the exact command/arguments `trustCert()`/`untrustCert()`
57
+ * would invoke, without spawning a process, touching PATH, or mutating the real trust store.
58
+ * Swapping the export on Node's built-in `child_process` module itself is not reliable for
59
+ * this (confirmed empirically while fixing this issue) - this repo-owned seam is.
60
+ */
61
+ exports._internal = { spawnSync: child_process_1.spawnSync };
52
62
  /**
53
63
  * Installs the cert as a trusted root CA in the user's OS certificate store.
54
64
  * ONLY call this for explicit Word-Online support — it prompts a Windows security
@@ -59,7 +69,7 @@ async function loadOrCreateCert() {
59
69
  */
60
70
  function trustCert(certPath) {
61
71
  if (process.platform === 'win32') {
62
- const result = (0, child_process_1.spawnSync)('certutil', ['-addstore', '-user', 'Root', certPath], { encoding: 'utf8' });
72
+ const result = exports._internal.spawnSync('certutil', ['-addstore', '-user', 'Root', certPath], { encoding: 'utf8' });
63
73
  if (result.status !== 0) {
64
74
  console.warn('[fraim] could not trust localhost certificate:', result.stderr || result.stdout);
65
75
  }
@@ -67,17 +77,17 @@ function trustCert(certPath) {
67
77
  }
68
78
  if (process.platform === 'darwin') {
69
79
  const keychain = path_1.default.join(os_1.default.homedir(), 'Library', 'Keychains', 'login.keychain-db');
70
- (0, child_process_1.spawnSync)('security', ['add-trusted-cert', '-r', 'trustRoot', '-k', keychain, certPath], { stdio: 'ignore' });
80
+ exports._internal.spawnSync('security', ['add-trusted-cert', '-r', 'trustRoot', '-k', keychain, certPath], { stdio: 'ignore' });
71
81
  }
72
82
  }
73
83
  /** Removes a specific cert from the trust store, by fingerprint. Test-only helper. */
74
84
  function untrustCert(fingerprint) {
75
85
  if (process.platform === 'win32') {
76
- (0, child_process_1.spawnSync)('certutil', ['-delstore', '-user', 'Root', fingerprint], { encoding: 'utf8' });
86
+ exports._internal.spawnSync('certutil', ['-delstore', '-user', 'Root', fingerprint], { encoding: 'utf8' });
77
87
  return;
78
88
  }
79
89
  if (process.platform === 'darwin') {
80
- (0, child_process_1.spawnSync)('security', ['delete-certificate', '-Z', fingerprint], { stdio: 'ignore' });
90
+ exports._internal.spawnSync('security', ['delete-certificate', '-Z', fingerprint], { stdio: 'ignore' });
81
91
  }
82
92
  }
83
93
  /**
@@ -495,6 +495,8 @@ function readAgentFromArgs(args) {
495
495
  function readToolName(candidate) {
496
496
  if (typeof candidate.name === 'string')
497
497
  return candidate.name;
498
+ if (typeof candidate.mcpToolName === 'string')
499
+ return candidate.mcpToolName;
498
500
  if (typeof candidate.tool_name === 'string')
499
501
  return candidate.tool_name;
500
502
  if (typeof candidate.tool === 'string')
@@ -591,6 +593,118 @@ function normalizeToolArgs(rawArgs) {
591
593
  }
592
594
  return null;
593
595
  }
596
+ function stringifyMentorCallArgs(rawArgs) {
597
+ const args = normalizeToolArgs(rawArgs);
598
+ if (!args)
599
+ return null;
600
+ return JSON.stringify(args, null, 2);
601
+ }
602
+ function resultTextFromValue(value) {
603
+ if (typeof value === 'string')
604
+ return value.trim() || null;
605
+ if (Array.isArray(value)) {
606
+ const parts = value
607
+ .map((entry) => resultTextFromValue(entry))
608
+ .filter((entry) => Boolean(entry));
609
+ return parts.length > 0 ? parts.join('\n') : null;
610
+ }
611
+ if (!value || typeof value !== 'object')
612
+ return null;
613
+ const record = value;
614
+ for (const key of ['text', 'message', 'output', 'result', 'content']) {
615
+ const text = resultTextFromValue(record[key]);
616
+ if (text)
617
+ return text;
618
+ }
619
+ return null;
620
+ }
621
+ function microEventFromToolCall(candidate) {
622
+ if (!isFraimTool(readToolName(candidate), 'seekMentoring'))
623
+ return undefined;
624
+ const text = stringifyMentorCallArgs(candidate.input || candidate.arguments || candidate.parameters);
625
+ return text ? [{ kind: 'mentor_call', text }] : undefined;
626
+ }
627
+ function microEventFromToolResult(candidate) {
628
+ if (!isFraimTool(readToolName(candidate), 'seekMentoring'))
629
+ return undefined;
630
+ const text = resultTextFromValue(candidate.result ?? candidate.output ?? candidate.content ?? candidate.message);
631
+ return text ? [{ kind: 'mentor_reply', text }] : undefined;
632
+ }
633
+ function readToolUseId(candidate) {
634
+ return stringValue(candidate.id) || stringValue(candidate.tool_use_id) || stringValue(candidate.toolUseId);
635
+ }
636
+ function parseMicroEventsSignal(line) {
637
+ let parsed;
638
+ try {
639
+ parsed = JSON.parse(line);
640
+ }
641
+ catch {
642
+ return {};
643
+ }
644
+ if (!parsed || typeof parsed !== 'object')
645
+ return {};
646
+ const root = parsed;
647
+ const events = [];
648
+ const mentorToolUseIds = [];
649
+ const mentorToolResults = [];
650
+ const item = root.item && typeof root.item === 'object' ? root.item : null;
651
+ if (item && item.type === 'mcp_tool_call' && isFraimTool(readToolName(item), 'seekMentoring')) {
652
+ const rootType = typeof root.type === 'string' ? root.type : '';
653
+ const argsText = stringifyMentorCallArgs(item.arguments);
654
+ if (argsText && rootType !== 'item.completed')
655
+ events.push({ kind: 'mentor_call', text: argsText });
656
+ const replyText = resultTextFromValue(item.result);
657
+ if (replyText)
658
+ events.push({ kind: 'mentor_reply', text: replyText });
659
+ }
660
+ const data = root.data && typeof root.data === 'object' ? root.data : null;
661
+ if (data && isFraimTool(readToolName(data), 'seekMentoring')) {
662
+ if (root.type === 'tool.execution_start') {
663
+ const argsText = stringifyMentorCallArgs(data.arguments ?? data.input ?? data.parameters);
664
+ if (argsText)
665
+ events.push({ kind: 'mentor_call', text: argsText });
666
+ }
667
+ if (root.type === 'tool.execution_complete' || root.type === 'model.tool_execution') {
668
+ const replyText = resultTextFromValue(data.result ?? data.output ?? data.content);
669
+ if (replyText)
670
+ events.push({ kind: 'mentor_reply', text: replyText });
671
+ }
672
+ }
673
+ const contents = [];
674
+ if (Array.isArray(root.content))
675
+ contents.push(...root.content.filter((entry) => !!entry && typeof entry === 'object' && !Array.isArray(entry)));
676
+ const message = root.message && typeof root.message === 'object' ? root.message : null;
677
+ if (message && Array.isArray(message.content))
678
+ contents.push(...message.content.filter((entry) => !!entry && typeof entry === 'object' && !Array.isArray(entry)));
679
+ for (const content of contents) {
680
+ if (content.type === 'tool_use' || content.type === 'function_call') {
681
+ const call = microEventFromToolCall(content);
682
+ if (call) {
683
+ events.push(...call);
684
+ const id = readToolUseId(content);
685
+ if (id)
686
+ mentorToolUseIds.push(id);
687
+ }
688
+ }
689
+ if (content.type === 'tool_result') {
690
+ const reply = microEventFromToolResult(content);
691
+ if (reply) {
692
+ events.push(...reply);
693
+ }
694
+ else {
695
+ const toolUseId = readToolUseId(content);
696
+ const text = resultTextFromValue(content.result ?? content.output ?? content.content ?? content.message);
697
+ if (toolUseId && text)
698
+ mentorToolResults.push({ toolUseId, text });
699
+ }
700
+ }
701
+ }
702
+ return {
703
+ ...(events.length > 0 ? { microEvents: events } : {}),
704
+ ...(mentorToolUseIds.length > 0 ? { mentorToolUseIds } : {}),
705
+ ...(mentorToolResults.length > 0 ? { mentorToolResults } : {}),
706
+ };
707
+ }
594
708
  function numberOrNull(v) {
595
709
  return typeof v === 'number' && Number.isFinite(v) ? v : null;
596
710
  }
@@ -627,6 +741,10 @@ function extractSignalFromArgs(args) {
627
741
  const reviewHandoff = extractReviewHandoffFromArgs(args);
628
742
  const delegationLedger = extractDelegationLedgerFromArgs(args);
629
743
  const nextJobRecommendations = extractNextJobRecommendationsFromArgs(args);
744
+ const deliveryActionId = readNullableString(evidenceArgs?.deliveryActionId);
745
+ const completedDeliveryActionId = readNullableString(evidenceArgs?.completedDeliveryActionId);
746
+ const selectedReviewAction = readReviewActionCandidate(evidenceArgs?.selectedReviewAction);
747
+ const deliveryActionResult = readDeliveryActionResultCandidate(evidenceArgs?.deliveryActionResult);
630
748
  return {
631
749
  phaseId,
632
750
  phaseStatus,
@@ -638,6 +756,64 @@ function extractSignalFromArgs(args) {
638
756
  ...(reviewHandoff ? { reviewHandoff } : {}),
639
757
  ...(delegationLedger ? { delegationLedger } : {}),
640
758
  ...(nextJobRecommendations ? { nextJobRecommendations } : {}),
759
+ ...(deliveryActionId !== undefined ? { deliveryActionId } : {}),
760
+ ...(selectedReviewAction !== undefined ? { selectedReviewAction } : {}),
761
+ ...(completedDeliveryActionId !== undefined ? { completedDeliveryActionId } : {}),
762
+ ...(deliveryActionResult ? { deliveryActionResult } : {}),
763
+ };
764
+ }
765
+ function readNullableString(value) {
766
+ if (value === null)
767
+ return null;
768
+ if (typeof value !== 'string')
769
+ return undefined;
770
+ const trimmed = value.trim();
771
+ return trimmed.length > 0 ? trimmed : null;
772
+ }
773
+ function readReviewActionCandidate(value) {
774
+ if (value === null)
775
+ return null;
776
+ if (!value || typeof value !== 'object' || Array.isArray(value))
777
+ return undefined;
778
+ const action = value;
779
+ const id = typeof action.id === 'string' && action.id.trim().length > 0 ? action.id.trim() : '';
780
+ const label = typeof action.label === 'string' && action.label.trim().length > 0 ? action.label.trim() : '';
781
+ const kind = typeof action.kind === 'string' && action.kind.trim().length > 0 ? action.kind.trim() : '';
782
+ if (!id || !label || !kind)
783
+ return undefined;
784
+ const target = action.target && typeof action.target === 'object' && !Array.isArray(action.target)
785
+ ? action.target
786
+ : undefined;
787
+ return {
788
+ id,
789
+ label,
790
+ kind,
791
+ ...(typeof action.style === 'string' ? { style: action.style } : {}),
792
+ ...(typeof action.deliveryActionId === 'string' && action.deliveryActionId.trim().length > 0 ? { deliveryActionId: action.deliveryActionId.trim() } : {}),
793
+ ...(typeof action.description === 'string' ? { description: action.description } : {}),
794
+ ...(target ? { target } : {}),
795
+ };
796
+ }
797
+ function readDeliveryActionResultCandidate(value) {
798
+ if (!value || typeof value !== 'object' || Array.isArray(value))
799
+ return null;
800
+ const result = value;
801
+ const actionId = typeof result.actionId === 'string' && result.actionId.trim().length > 0
802
+ ? result.actionId.trim()
803
+ : typeof result.deliveryActionId === 'string' && result.deliveryActionId.trim().length > 0
804
+ ? result.deliveryActionId.trim()
805
+ : '';
806
+ if (!actionId)
807
+ return null;
808
+ return {
809
+ actionId,
810
+ ...(typeof result.prNumber === 'number' && Number.isFinite(result.prNumber) ? { prNumber: result.prNumber } : {}),
811
+ ...(typeof result.pushed === 'boolean' ? { pushed: result.pushed } : {}),
812
+ ...(typeof result.merged === 'boolean' ? { merged: result.merged } : {}),
813
+ ...(typeof result.defaultBranchVerified === 'boolean' ? { defaultBranchVerified: result.defaultBranchVerified } : {}),
814
+ ...(typeof result.issueClosed === 'boolean' ? { issueClosed: result.issueClosed } : {}),
815
+ ...(typeof result.branchCleaned === 'boolean' ? { branchCleaned: result.branchCleaned } : {}),
816
+ ...(typeof result.domainActionCompleted === 'boolean' ? { domainActionCompleted: result.domainActionCompleted } : {}),
641
817
  };
642
818
  }
643
819
  function extractDelegationLedgerFromArgs(args) {
@@ -1808,11 +1984,15 @@ function parseHostLine(hostId, line) {
1808
1984
  const usage = parseUsageSignal(trimmed);
1809
1985
  const agentIdentity = parseAgentIdentitySignal(trimmed);
1810
1986
  const recurringPark = parseRecurringParkSignal(trimmed);
1987
+ const microSignals = parseMicroEventsSignal(trimmed);
1988
+ const microEvents = microSignals.microEvents;
1989
+ const mentorToolUseIds = microSignals.mentorToolUseIds;
1990
+ const mentorToolResults = microSignals.mentorToolResults;
1811
1991
  // Issue #1264 (R3): only worth checking when seekMentoring itself did not
1812
1992
  // already resolve — a recognized call has nothing to diagnose.
1813
1993
  const unrecognizedReviewHandoffError = seekMentoring ? null : detectUnrecognizedReviewHandoffCall(trimmed);
1814
1994
  const withSignal = (event) => {
1815
- if (!seekMentoring && !fraimJob && !executionMode && !usage && !agentIdentity && !recurringPark && !unrecognizedReviewHandoffError)
1995
+ if (!seekMentoring && !fraimJob && !executionMode && !usage && !agentIdentity && !recurringPark && !microEvents && !mentorToolUseIds && !mentorToolResults && !unrecognizedReviewHandoffError)
1816
1996
  return event;
1817
1997
  return {
1818
1998
  ...event,
@@ -1822,6 +2002,9 @@ function parseHostLine(hostId, line) {
1822
2002
  ...(usage ? { usage } : {}),
1823
2003
  ...(agentIdentity ? { agentIdentity } : {}),
1824
2004
  ...(recurringPark ? { recurringPark: true } : {}),
2005
+ ...(microEvents ? { microEvents } : {}),
2006
+ ...(mentorToolUseIds ? { mentorToolUseIds } : {}),
2007
+ ...(mentorToolResults ? { mentorToolResults } : {}),
1825
2008
  ...(unrecognizedReviewHandoffError && !event.hostError ? { hostError: unrecognizedReviewHandoffError } : {}),
1826
2009
  };
1827
2010
  };
@@ -22,6 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports._internal = exports.WEF_DEVELOPER_KEY = void 0;
25
26
  exports.manifestXmlForPort = manifestXmlForPort;
26
27
  exports.shouldSkipSideloadForFlag = shouldSkipSideloadForFlag;
27
28
  exports.winDeveloperManifestSubkey = winDeveloperManifestSubkey;
@@ -32,7 +33,7 @@ const fs_1 = __importDefault(require("fs"));
32
33
  const path_1 = __importDefault(require("path"));
33
34
  const os_1 = __importDefault(require("os"));
34
35
  const child_process_1 = require("child_process");
35
- const WEF_DEVELOPER_KEY = 'HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer';
36
+ exports.WEF_DEVELOPER_KEY = 'HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer';
36
37
  const MANIFESTS = [
37
38
  {
38
39
  guid: 'd1090951-50cf-4cf2-9d12-b0f8541d265c',
@@ -93,7 +94,7 @@ function prepareManifestForSideload(entry, sourcePath, options) {
93
94
  // Windows registry helpers
94
95
  // ---------------------------------------------------------------------------
95
96
  function winDeveloperManifestSubkey(guid) {
96
- return `${WEF_DEVELOPER_KEY}\\{${guid}}`;
97
+ return `${exports.WEF_DEVELOPER_KEY}\\{${guid}}`;
97
98
  }
98
99
  function parseRegSzValue(stdout) {
99
100
  const line = stdout.split(/\r?\n/).find(l => l.includes('REG_SZ'));
@@ -103,7 +104,7 @@ function parseRegSzValue(stdout) {
103
104
  return line.slice(idx + 'REG_SZ'.length).trim() || null;
104
105
  }
105
106
  function winRegisteredRootValue(guid) {
106
- const r = (0, child_process_1.spawnSync)('reg', ['query', WEF_DEVELOPER_KEY, '/v', guid], { encoding: 'utf8' });
107
+ const r = (0, child_process_1.spawnSync)('reg', ['query', exports.WEF_DEVELOPER_KEY, '/v', guid], { encoding: 'utf8' });
107
108
  if (r.status !== 0 || !r.stdout.includes('REG_SZ'))
108
109
  return null;
109
110
  // Output line looks like: " <guid> REG_SZ C:\path\to\manifest.xml"
@@ -122,14 +123,21 @@ function winRegisteredValue(guid) {
122
123
  // but treat the root value as the effective discovery registration.
123
124
  return winRegisteredRootValue(guid) ?? winRegisteredDefaultValue(guid);
124
125
  }
126
+ /**
127
+ * Test seam (issue #1589): the registry-write invocations go through this mutable,
128
+ * module-owned indirection point instead of a bare `spawnSync` call, so a test can swap in a
129
+ * fake implementation and assert on the exact `reg` command/arguments `sideloadManifest()`
130
+ * would invoke, without spawning a process, touching PATH, or mutating the real registry.
131
+ */
132
+ exports._internal = { spawnSync: child_process_1.spawnSync };
125
133
  function winWriteRegisteredValue(guid, manifestPath) {
126
- const root = (0, child_process_1.spawnSync)('reg', [
127
- 'add', WEF_DEVELOPER_KEY,
134
+ const root = exports._internal.spawnSync('reg', [
135
+ 'add', exports.WEF_DEVELOPER_KEY,
128
136
  '/v', guid, '/t', 'REG_SZ', '/d', manifestPath, '/f',
129
137
  ], { encoding: 'utf8' });
130
138
  if (root.status !== 0)
131
139
  return { ok: false, reason: root.stderr || `reg add failed for ${guid}` };
132
- const r = (0, child_process_1.spawnSync)('reg', [
140
+ const r = exports._internal.spawnSync('reg', [
133
141
  'add', winDeveloperManifestSubkey(guid),
134
142
  '/ve', '/t', 'REG_SZ', '/d', manifestPath, '/f',
135
143
  ], { encoding: 'utf8' });
@@ -137,7 +145,7 @@ function winWriteRegisteredValue(guid, manifestPath) {
137
145
  return { ok: false, reason: r.stderr || `reg add failed for ${guid}` };
138
146
  // Remove an accidental unbraced subkey left by older debugger settings. This
139
147
  // is different from the root named value above, which Word needs.
140
- (0, child_process_1.spawnSync)('reg', ['delete', `${WEF_DEVELOPER_KEY}\\${guid}`, '/f'], { encoding: 'utf8' });
148
+ exports._internal.spawnSync('reg', ['delete', `${exports.WEF_DEVELOPER_KEY}\\${guid}`, '/f'], { encoding: 'utf8' });
141
149
  return { ok: true };
142
150
  }
143
151
  // ---------------------------------------------------------------------------
@@ -195,8 +203,8 @@ function sideloadManifest(projectPath, options = {}) {
195
203
  function removeSideload() {
196
204
  for (const entry of MANIFESTS) {
197
205
  if (process.platform === 'win32') {
198
- (0, child_process_1.spawnSync)('reg', ['delete', WEF_DEVELOPER_KEY, '/v', entry.guid, '/f'], { encoding: 'utf8' });
199
- (0, child_process_1.spawnSync)('reg', ['delete', `${WEF_DEVELOPER_KEY}\\${entry.guid}`, '/f'], { encoding: 'utf8' });
206
+ (0, child_process_1.spawnSync)('reg', ['delete', exports.WEF_DEVELOPER_KEY, '/v', entry.guid, '/f'], { encoding: 'utf8' });
207
+ (0, child_process_1.spawnSync)('reg', ['delete', `${exports.WEF_DEVELOPER_KEY}\\${entry.guid}`, '/f'], { encoding: 'utf8' });
200
208
  (0, child_process_1.spawnSync)('reg', ['delete', winDeveloperManifestSubkey(entry.guid), '/f'], { encoding: 'utf8' });
201
209
  }
202
210
  else if (process.platform === 'darwin') {