@cohortapp/agent-sdk 2.11.6 → 2.11.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cohortapp/agent-sdk",
3
- "version": "2.11.6",
3
+ "version": "2.11.7",
4
4
  "description": "Cohort Agent SDK — autonomous AI colleague runtime. Deploy senior AI colleagues on dedicated Mac minis, wired to the Cohort operating surface.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -84,7 +84,7 @@ const RUNG_MECHANISM = Object.fromEntries(RUNGS.map((r) => [r.id, r.mechanism]))
84
84
  // work and land it in the org's shared, ACL'd store so the fleet's memory
85
85
  // compounds. Fail-open + best-effort — never blocks or throws into the hot path;
86
86
  // local SQLite/interaction logs remain the fail-open cache.
87
- import { isEnabled as orgEnabled, loadOrgConfig, configFromAgent, electResponder } from "../../lib/org/client.mjs";
87
+ import { isEnabled as orgEnabled, loadOrgConfig, configFromAgent, electResponder, fetchSelfProfile } from "../../lib/org/client.mjs";
88
88
  import { remember as orgRemember } from "../../lib/org/knowledge.mjs";
89
89
  import { sweepSessionOutcomes, resultTextFromStdout } from "./session-outcomes.mjs";
90
90
  import { recordWorkStep as orgRecordWorkStep, cohortChannelId, cohortMessageId } from "../../lib/org/work-ledger.mjs";
@@ -1085,8 +1085,70 @@ export function _resetLadderCaches() {
1085
1085
  _ladderDisabledWarned = false;
1086
1086
  }
1087
1087
 
1088
+ // The seat's own member id (cuid), resolved once. This is the `actor` on every
1089
+ // message/event the seat sends, so the own-echo guard needs it — NOT the slug
1090
+ // (e.g. "A016"), which never matches a cuid actor. COHORT_AGENT_ID is a slug, so
1091
+ // using it as `me` (the old behaviour) left own-echo permanently broken: the seat
1092
+ // re-ingested its own replies and held a conversation with itself.
1093
+ let _selfMemberId = null;
1094
+
1095
+ /** Read a previously-persisted memberId (cuid) from config/agent.json. */
1096
+ function persistedSelfMemberId() {
1097
+ try {
1098
+ const c = JSON.parse(readFileSync(join(AGENT_REPO_DIR, "config", "agent.json"), "utf-8"));
1099
+ const mid = c && typeof c.memberId === "string" ? c.memberId : "";
1100
+ // Guard against a slug ever being persisted here — the field MUST be a cuid.
1101
+ return mid && mid !== process.env.COHORT_AGENT_ID ? mid : "";
1102
+ } catch {
1103
+ return "";
1104
+ }
1105
+ }
1106
+
1107
+ /**
1108
+ * Resolve + cache the seat's own member cuid via /whoami (org/members/<slug>),
1109
+ * persisting it to config/agent.json so subsequent boots — and every own-echo
1110
+ * check — have it instantly. Best-effort and idempotent; safe to await on the
1111
+ * hot path (cached after the first success). Requires hq to expose `memberId`
1112
+ * on the profile DTO (org-api whoami/members).
1113
+ */
1114
+ export async function resolveSelfMemberId(deps = {}) {
1115
+ if (_selfMemberId) return _selfMemberId;
1116
+ const fromDisk = persistedSelfMemberId();
1117
+ if (fromDisk) { _selfMemberId = fromDisk; return _selfMemberId; }
1118
+ try {
1119
+ const cfg = daemonOrgCfg();
1120
+ const conn = configFromAgent(cfg) || {};
1121
+ const apiKey = conn.apiKey || process.env.COHORT_API_KEY || "";
1122
+ const orgId = conn.orgId || process.env.COHORT_ORG_ID || "";
1123
+ const agentId = conn.agentId || process.env.COHORT_AGENT_ID || "";
1124
+ if (!apiKey || !orgId) return _selfMemberId;
1125
+ const fetchSelf = deps.fetchSelfProfileImpl || fetchSelfProfile;
1126
+ const res = await fetchSelf({ base: conn.base, apiKey, orgId, agentId });
1127
+ const mid = res && res.ok && res.member && res.member.memberId ? String(res.member.memberId) : "";
1128
+ if (mid) {
1129
+ _selfMemberId = mid;
1130
+ try {
1131
+ const cfgPath = join(AGENT_REPO_DIR, "config", "agent.json");
1132
+ const c = JSON.parse(readFileSync(cfgPath, "utf-8"));
1133
+ c.memberId = mid;
1134
+ writeFileSync(cfgPath, JSON.stringify(c, null, 2) + "\n");
1135
+ } catch { /* best-effort persist — resolution still cached in-memory */ }
1136
+ }
1137
+ } catch { /* best-effort — falls back to slug until hq exposes memberId */ }
1138
+ return _selfMemberId;
1139
+ }
1140
+
1141
+ /** For tests: reset the cached self member id. */
1142
+ export function _resetSelfMemberId() { _selfMemberId = null; }
1143
+
1088
1144
  /** The seat's org member id — the `me` every directedness join is relative to. */
1089
1145
  function ladderMemberId() {
1146
+ // Prefer the resolved member cuid (the actor on events). Fall back to the slug
1147
+ // only until resolution completes — the slug can't power own-echo, but it is no
1148
+ // worse than the historical behaviour.
1149
+ if (_selfMemberId) return _selfMemberId;
1150
+ const disk = persistedSelfMemberId();
1151
+ if (disk) { _selfMemberId = disk; return disk; }
1090
1152
  const cfg = daemonOrgCfg();
1091
1153
  return (
1092
1154
  process.env.COHORT_AGENT_ID ||
@@ -1315,6 +1377,10 @@ export async function runExecutionLadder(item, service, itemId, trace_id, deps =
1315
1377
 
1316
1378
  let answered = null;
1317
1379
  try {
1380
+ // Ensure the seat's own member cuid is resolved BEFORE directedness runs, so
1381
+ // the own-echo guard can recognize this seat's own messages (the actor on an
1382
+ // event is a cuid, not the slug). Cached after the first resolve.
1383
+ await resolveSelfMemberId(deps);
1318
1384
  const me = ladderMemberId();
1319
1385
  const obligations = await ladderObligations(log);
1320
1386