atris 3.15.44 → 3.15.45

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 (2) hide show
  1. package/commands/xp.js +84 -9
  2. package/package.json +1 -1
package/commands/xp.js CHANGED
@@ -10,6 +10,7 @@ const DEFAULT_GRAPH_DAYS = 365;
10
10
  const INTENSITY_CHARS = [' ', '.', ':', '*', '#'];
11
11
  const ROW_LABELS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
12
12
  const TASK_EPISODES_FILE = path.join('.atris', 'state', 'task_episodes.jsonl');
13
+ const BUSINESS_BINDING_FILE = path.join('.atris', 'business.json');
13
14
  const CAREER_XP_RECEIPTS_FILE = path.join('.atris', 'state', 'career_xp_receipts.jsonl');
14
15
  const CAREER_XP_PROJECTION_FILE = path.join('.atris', 'state', 'career_xp.projection.json');
15
16
  const CAREER_XP_CURSOR_FILE = path.join('.atris', 'state', 'career_xp.cursor.json');
@@ -1122,6 +1123,24 @@ function workspaceName(workspace) {
1122
1123
  return path.basename(workspace) || workspace;
1123
1124
  }
1124
1125
 
1126
+ function publicWorkspaceBinding(workspace) {
1127
+ const binding = readJsonFile(path.join(workspace, BUSINESS_BINDING_FILE), null);
1128
+ if (!binding || typeof binding !== 'object') return null;
1129
+ const businessId = String(binding.business_id || binding.id || '').trim();
1130
+ const workspaceId = String(binding.workspace_id || '').trim();
1131
+ const businessSlug = slugify(binding.slug || binding.business_slug || binding.name);
1132
+ const workspaceTemplate = slugify(binding.workspace_template || binding.organization_type || binding.computer_type);
1133
+ if (!businessId && !workspaceId && !businessSlug) return null;
1134
+ return {
1135
+ business_id: businessId || null,
1136
+ workspace_id: workspaceId || null,
1137
+ business_slug: businessSlug || null,
1138
+ workspace_template: workspaceTemplate || null,
1139
+ computer: businessSlug || workspaceName(workspace),
1140
+ computer_slug: businessSlug || slugify(workspaceName(workspace)),
1141
+ };
1142
+ }
1143
+
1125
1144
  function isVerifiedProjection(projection) {
1126
1145
  return projection?.schema === 'atris.career_xp_projection.v1'
1127
1146
  && projection.integrity_status === 'verified'
@@ -1719,14 +1738,18 @@ function verifiedProjection(projection) {
1719
1738
 
1720
1739
  function projectionWorkspaceSummaries(projection) {
1721
1740
  if (Array.isArray(projection?.workspaces)) {
1722
- return projection.workspaces.map(workspace => ({
1723
- name: workspace.name || workspaceName(workspace.workspace_root || 'workspace'),
1724
- workspace_root_hash: workspace.workspace_root ? sha256(path.resolve(workspace.workspace_root)) : null,
1725
- included: Boolean(workspace.included),
1726
- agent_xp: asNumber(workspace.total_xp),
1727
- receipts_count: asNumber(workspace.receipts_count),
1728
- integrity_status: workspace.integrity_status || 'unknown',
1729
- }));
1741
+ return projection.workspaces.map((workspace) => {
1742
+ const workspaceRoot = workspace.workspace_root || '';
1743
+ return {
1744
+ name: workspace.name || workspaceName(workspaceRoot || 'workspace'),
1745
+ workspace_root_hash: workspaceRoot ? sha256(path.resolve(workspaceRoot)) : null,
1746
+ included: Boolean(workspace.included),
1747
+ agent_xp: asNumber(workspace.total_xp),
1748
+ receipts_count: asNumber(workspace.receipts_count),
1749
+ integrity_status: workspace.integrity_status || 'unknown',
1750
+ ...(workspaceRoot ? publicWorkspaceBinding(workspaceRoot) : null),
1751
+ };
1752
+ });
1730
1753
  }
1731
1754
 
1732
1755
  const workspaceRoot = projection?.workspace_root || path.resolve(process.cwd());
@@ -1737,9 +1760,43 @@ function projectionWorkspaceSummaries(projection) {
1737
1760
  agent_xp: asNumber(projection?.total_agent_xp ?? projection?.total_xp),
1738
1761
  receipts_count: asNumber(projection?.receipts_count),
1739
1762
  integrity_status: projection?.integrity_status || projection?.integrity?.status || 'unknown',
1763
+ ...publicWorkspaceBinding(workspaceRoot),
1740
1764
  }];
1741
1765
  }
1742
1766
 
1767
+ function uniqueTruthy(values) {
1768
+ return Array.from(new Set(values.map(value => String(value || '').trim()).filter(Boolean))).sort();
1769
+ }
1770
+
1771
+ function syncAttribution(workspaces) {
1772
+ const included = workspaces.filter(workspace => workspace.included !== false);
1773
+ const scoped = included.length ? included : workspaces;
1774
+ const businessIds = uniqueTruthy(scoped.map(workspace => workspace.business_id));
1775
+ const workspaceIds = uniqueTruthy(scoped.map(workspace => workspace.workspace_id));
1776
+ const businessSlugs = uniqueTruthy(scoped.map(workspace => workspace.business_slug || workspace.computer_slug));
1777
+ const templates = uniqueTruthy(scoped.map(workspace => workspace.workspace_template));
1778
+
1779
+ if (businessIds.length > 1) {
1780
+ return { attribution_scope: 'multi_business', computer: 'multiple-workspaces' };
1781
+ }
1782
+ if (businessIds.length === 0 && scoped.length > 1 && businessSlugs.length > 1) {
1783
+ return { attribution_scope: 'multi_workspace', computer: 'multiple-workspaces' };
1784
+ }
1785
+
1786
+ const businessId = businessIds[0] || null;
1787
+ const workspaceId = workspaceIds.length === 1 ? workspaceIds[0] : null;
1788
+ const businessSlug = businessSlugs.length === 1 ? businessSlugs[0] : null;
1789
+ const workspaceTemplate = templates.length === 1 ? templates[0] : null;
1790
+ return {
1791
+ attribution_scope: businessId || businessSlug ? 'business_bound' : 'workspace_only',
1792
+ business_id: businessId,
1793
+ workspace_id: workspaceId,
1794
+ business_slug: businessSlug,
1795
+ workspace_template: workspaceTemplate,
1796
+ computer: businessSlug || scoped[0]?.computer || scoped[0]?.name || 'local',
1797
+ };
1798
+ }
1799
+
1743
1800
  function credentialHandle(credentials) {
1744
1801
  return slugify(
1745
1802
  credentials?.username
@@ -1798,6 +1855,7 @@ function buildAgentXpSyncPacket(args = []) {
1798
1855
  : collectLocalXpProjection(projectionArgs);
1799
1856
  const player = syncPlayer(args, projection);
1800
1857
  const workspaces = projectionWorkspaceSummaries(projection);
1858
+ const attribution = syncAttribution(workspaces);
1801
1859
  const totalXp = asNumber(projection.total_agent_xp ?? projection.agent_xp ?? projection.total_xp ?? projection.career_xp);
1802
1860
  const receiptsCount = asNumber(projection.receipts_count);
1803
1861
  const eligible = verifiedProjection(projection) && receiptsCount > 0 && totalXp > 0;
@@ -1825,7 +1883,12 @@ function buildAgentXpSyncPacket(args = []) {
1825
1883
  schema: 'atris.agentxp_sync_packet.v1',
1826
1884
  generated_at: new Date().toISOString(),
1827
1885
  workspace_root_hash: workspaceRootHash,
1828
- computer: projection.workspace_name || workspaces[0]?.name || 'local',
1886
+ attribution_scope: attribution.attribution_scope,
1887
+ business_id: attribution.business_id || null,
1888
+ workspace_id: attribution.workspace_id || null,
1889
+ business_slug: attribution.business_slug || null,
1890
+ workspace_template: attribution.workspace_template || null,
1891
+ computer: attribution.computer || projection.workspace_name || workspaces[0]?.name || 'local',
1829
1892
  operator: player,
1830
1893
  privacy: {
1831
1894
  raw_proofs_included: false,
@@ -1840,6 +1903,12 @@ function buildAgentXpSyncPacket(args = []) {
1840
1903
  local_evidence: {
1841
1904
  schema: 'atris.agentxp_local_evidence.v1',
1842
1905
  workspace_root_hash: workspaceRootHash,
1906
+ attribution_scope: attribution.attribution_scope,
1907
+ business_id: attribution.business_id || null,
1908
+ workspace_id: attribution.workspace_id || null,
1909
+ business_slug: attribution.business_slug || null,
1910
+ workspace_template: attribution.workspace_template || null,
1911
+ computer: attribution.computer || null,
1843
1912
  workspaces,
1844
1913
  verified_workspace_count: asNumber(projection.verified_workspace_count, verifiedProjection(projection) ? 1 : 0),
1845
1914
  receipts_count: receiptsCount,
@@ -1864,6 +1933,12 @@ function buildAgentXpSyncPacket(args = []) {
1864
1933
  gm_projection: {
1865
1934
  schema: 'atris.gm_xp_projection.v1',
1866
1935
  workspace_root_hash: workspaceRootHash,
1936
+ attribution_scope: attribution.attribution_scope,
1937
+ business_id: attribution.business_id || null,
1938
+ workspace_id: attribution.workspace_id || null,
1939
+ business_slug: attribution.business_slug || null,
1940
+ workspace_template: attribution.workspace_template || null,
1941
+ computer: attribution.computer || null,
1867
1942
  operator: player,
1868
1943
  player_score: {
1869
1944
  agent_xp: totalXp,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "3.15.44",
3
+ "version": "3.15.45",
4
4
  "main": "bin/atris.js",
5
5
  "bin": {
6
6
  "atris": "bin/atris.js",