@tpsdev-ai/flair 0.13.0 → 0.14.0

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/dist/cli.js CHANGED
@@ -7005,6 +7005,7 @@ memory.command("add [content]").requiredOption("--agent <id>")
7005
7005
  .option("--summary <text>", "agent-set multi-sentence dense compression (3-tier chain: subject → summary → content; ops-wkoh)")
7006
7006
  .option("--subject <text>", "one-line title / entity this memory is about")
7007
7007
  .option("--derived-from <csv>", "Comma-separated source Memory IDs this memory was distilled/reflected from (sets Memory.derivedFrom; used by the `rem rapid` reflection loop)")
7008
+ .option("--visibility <value>", "Memory visibility (sets Memory.visibility). Use 'office' to share office-wide with every team agent; omit (default) to keep it private to this agent (flair#509)")
7008
7009
  .action(async (contentArg, opts) => {
7009
7010
  const content = contentArg ?? opts.content;
7010
7011
  if (!content) {
@@ -7021,6 +7022,8 @@ memory.command("add [content]").requiredOption("--agent <id>")
7021
7022
  body.summary = opts.summary;
7022
7023
  if (opts.subject)
7023
7024
  body.subject = opts.subject;
7025
+ if (opts.visibility)
7026
+ body.visibility = String(opts.visibility).trim();
7024
7027
  if (opts.derivedFrom) {
7025
7028
  body.derivedFrom = String(opts.derivedFrom).split(",").map((x) => x.trim()).filter(Boolean);
7026
7029
  }
@@ -2,6 +2,7 @@ import { Resource, databases } from "@harperfast/harper";
2
2
  import { access, readFile, readdir } from "node:fs/promises";
3
3
  import { constants } from "node:fs";
4
4
  import { basename, extname, join } from "node:path";
5
+ import { localBaseUrl, resolvePublicBaseUrl } from "./a2a-url.js";
5
6
  const BEADS_ROOT = join(process.env.HOME || "/root", "ops", ".beads");
6
7
  const BEADS_ISSUES_DIR = join(BEADS_ROOT, "issues");
7
8
  const BEADS_ISSUES_JSONL = join(BEADS_ROOT, "issues.jsonl");
@@ -277,7 +278,13 @@ export class A2AAdapter extends Resource {
277
278
  allowRead() { return true; }
278
279
  allowCreate() { return true; }
279
280
  async get() {
280
- const host = process.env.FLAIR_PUBLIC_URL || "http://localhost:9926";
281
+ // Resolve the URL remote peers should use to reach this Flair. Prefer the
282
+ // request Host header (how the caller actually reached us) over a
283
+ // hardcoded port, so a default local install advertises the REAL HTTP
284
+ // port (19926), not the dead legacy 9926 (flair#507).
285
+ const ctx = this.getContext?.() ?? {};
286
+ const ctxRequest = ctx.request ?? ctx;
287
+ const host = resolvePublicBaseUrl(ctxRequest);
281
288
  return new Response(JSON.stringify({
282
289
  name: "TPS Agent Team",
283
290
  description: "TPS — agent OS for humans and AI agents. Coordinates via Flair.",
@@ -367,7 +374,7 @@ export class A2AAdapter extends Resource {
367
374
  closeStream();
368
375
  return;
369
376
  }
370
- const catchupUrl = `http://localhost:9926/OrgEventCatchup/${encodeURIComponent(agentId)}?since=${lastSeen}`;
377
+ const catchupUrl = `${localBaseUrl()}/OrgEventCatchup/${encodeURIComponent(agentId)}?since=${lastSeen}`;
371
378
  let events = [];
372
379
  try {
373
380
  const response = await fetch(catchupUrl);
@@ -0,0 +1,53 @@
1
+ // URL resolution for the A2A adapter — flair#507.
2
+ //
3
+ // A default local Flair install listens on DEFAULT_HTTP_PORT (19926, the
4
+ // CLI's `DEFAULT_PORT` in src/cli.ts), NOT on 9926 (the legacy early-install
5
+ // port). The A2A agent-card `url` and the streaming catch-up self-fetch must
6
+ // reflect the REAL listening port, or a remote A2A peer that follows discovery
7
+ // hits a dead port.
8
+ //
9
+ // Kept free of any @harperfast/harper import so the resolution logic is
10
+ // unit-testable without spinning up Harper (mirrors agentcard-fields.ts —
11
+ // avoids the simulator-pattern drift that let the AdminInstance predicate be
12
+ // reproduced-not-imported).
13
+ // The CLI's DEFAULT_HTTP_PORT (src/cli.ts `DEFAULT_PORT`). Keep in sync.
14
+ export const DEFAULT_HTTP_PORT = 19926;
15
+ // Loopback base URL for in-process self-calls (e.g. the streaming catch-up
16
+ // fetch). Points at the port Flair is ACTUALLY listening on, which Harper
17
+ // exposes via HTTP_PORT in the runtime env — never the public/proxy URL.
18
+ // Falls back to DEFAULT_HTTP_PORT for a default local install.
19
+ export function localBaseUrl(env = process.env) {
20
+ return `http://127.0.0.1:${env.HTTP_PORT || DEFAULT_HTTP_PORT}`;
21
+ }
22
+ // Public base URL advertised in the A2A agent card so remote peers can reach
23
+ // this Flair. Mirrors AdminInstance.resolvePublicUrl (flair#404):
24
+ // 1. FLAIR_PUBLIC_URL (explicit override, always wins)
25
+ // 2. Request headers (X-Forwarded-Proto/Host or Host) — how the caller
26
+ // actually reached us
27
+ // 3. http://127.0.0.1:${HTTP_PORT} — local-only fallback on the REAL port
28
+ export function resolvePublicBaseUrl(request, env = process.env) {
29
+ if (env.FLAIR_PUBLIC_URL) {
30
+ return env.FLAIR_PUBLIC_URL.replace(/\/$/, "");
31
+ }
32
+ const getHeader = (name) => {
33
+ const h = request?.headers;
34
+ if (!h)
35
+ return undefined;
36
+ if (typeof h.get === "function")
37
+ return h.get(name) ?? h.get(name.toLowerCase()) ?? undefined;
38
+ if (typeof h === "object") {
39
+ const obj = h.asObject ?? h;
40
+ return obj[name] ?? obj[name.toLowerCase()] ?? undefined;
41
+ }
42
+ return undefined;
43
+ };
44
+ const fwdProto = getHeader("X-Forwarded-Proto");
45
+ const fwdHost = getHeader("X-Forwarded-Host");
46
+ const host = fwdHost ?? getHeader("Host");
47
+ if (host && /^[\w.\-:]+$/.test(host)) {
48
+ const scheme = fwdProto && (fwdProto === "http" || fwdProto === "https") ? fwdProto : "https";
49
+ const effectiveScheme = fwdProto ? scheme : (host.includes(":") ? "http" : scheme);
50
+ return `${effectiveScheme}://${host}`;
51
+ }
52
+ return localBaseUrl(env);
53
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpsdev-ai/flair",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Identity, memory, and soul for AI agents. Cryptographic identity (Ed25519), semantic memory with local embeddings, and persistent personality — all in a single process.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",