@rosthq/cli 0.7.51 → 0.7.53

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/config.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import { z } from "zod";
2
+ export declare const canonicalAppUrl = "https://rost.elevatez.ai";
3
+ export declare const legacyAppUrlAliases: readonly ["https://rost-pink.vercel.app"];
4
+ export declare const allowInsecureAppUrlEnv: string;
2
5
  declare const configSchema: z.ZodObject<{
3
6
  appUrl: z.ZodString;
4
7
  supabaseUrl: z.ZodString;
@@ -6,5 +9,13 @@ declare const configSchema: z.ZodObject<{
6
9
  }, z.core.$strict>;
7
10
  export type CliConfig = z.infer<typeof configSchema>;
8
11
  export declare function loadConfig(env?: NodeJS.ProcessEnv): CliConfig;
12
+ export declare function loadConfigWithDiagnostics(env?: NodeJS.ProcessEnv): {
13
+ config: CliConfig;
14
+ appUrlWarning: string | null;
15
+ };
16
+ export declare function resolveAppUrl(value: string, env?: NodeJS.ProcessEnv): {
17
+ value: string;
18
+ warning: string | null;
19
+ };
9
20
  export {};
10
21
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,QAAA,MAAM,YAAY;;;;kBAIP,CAAC;AAEZ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAErD,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,CAM1E"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,eAAe,6BAA6B,CAAC;AAC1D,eAAO,MAAM,mBAAmB,2CAA4C,CAAC;AAC7E,eAAO,MAAM,sBAAsB,QAAiD,CAAC;AAYrF,QAAA,MAAM,YAAY;;;;kBAIP,CAAC;AAEZ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAErD,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,CAE1E;AAED,wBAAgB,yBAAyB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG;IAC/E,MAAM,EAAE,SAAS,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAcA;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG;IAClF,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CA2CA"}
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAKrE,KAAK,QAAQ,GAAG;IACd,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;CAC3C,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,EAAE,EAAE,QAAQ,CAAC;IACb,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,CAAC;AAEF,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAoCvE"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAKrE,KAAK,QAAQ,GAAG;IACd,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;CAC3C,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,EAAE,EAAE,QAAQ,CAAC;IACb,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,CAAC;AAEF,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAuCvE"}
@@ -1 +1 @@
1
- {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../src/generator/groups.ts"],"names":[],"mappings":"AAuCA,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAM1D,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAKhF,CAAC;AAmmCF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CA2OjD,CAAC"}
1
+ {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../src/generator/groups.ts"],"names":[],"mappings":"AAuCA,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAM1D,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAKhF,CAAC;AAonCF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CA2OjD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAoB,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAwBrE,KAAK,KAAK,GAAG;IACX,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAK1C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,EAAE,CAAC,EAAE,KAAK,CAAC;IACX,KAAK,CAAC,EAAE,OAAO,gBAAgB,CAAC;IAChC,WAAW,CAAC,EAAE,OAAO,mBAAmB,CAAC;IACzC,cAAc,CAAC,EAAE,OAAO,yBAAyB,CAAC;CACnD,CAAC;AAEF,wBAAsB,IAAI,CAAC,IAAI,WAAwB,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8QnG;AAsdD,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAUxF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAoB,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAwBrE,KAAK,KAAK,GAAG;IACX,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAK1C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,EAAE,CAAC,EAAE,KAAK,CAAC;IACX,KAAK,CAAC,EAAE,OAAO,gBAAgB,CAAC;IAChC,WAAW,CAAC,EAAE,OAAO,mBAAmB,CAAC;IACzC,cAAc,CAAC,EAAE,OAAO,yBAAyB,CAAC;CACnD,CAAC;AAEF,wBAAsB,IAAI,CAAC,IAAI,WAAwB,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqRnG;AAsdD,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAUxF"}
package/dist/index.js CHANGED
@@ -27862,7 +27862,7 @@ var CommandClient = class {
27862
27862
  };
27863
27863
 
27864
27864
  // ../../packages/design/src/brand.ts
27865
- var appUrl = process.env.NEXT_PUBLIC_APP_URL ?? "";
27865
+ var appUrl = process.env.NEXT_PUBLIC_APP_URL ?? "https://rost.elevatez.ai";
27866
27866
  var marketingUrl = process.env.NEXT_PUBLIC_MARKETING_URL ?? "";
27867
27867
  var BRAND = {
27868
27868
  name: "ROST",
@@ -27925,8 +27925,11 @@ var cliBrand = {
27925
27925
  };
27926
27926
 
27927
27927
  // src/config.ts
27928
+ var canonicalAppUrl = "https://rost.elevatez.ai";
27929
+ var legacyAppUrlAliases = ["https://rost-pink.vercel.app"];
27930
+ var allowInsecureAppUrlEnv = `${cliBrand.envPrefix}_ALLOW_INSECURE_APP_URL`;
27928
27931
  var publicProductionDefaults = {
27929
- appUrl: "https://rost-pink.vercel.app",
27932
+ appUrl: canonicalAppUrl,
27930
27933
  supabaseUrl: "https://jysblwhxtagcibpxdtnc.supabase.co",
27931
27934
  supabaseAnonKey: [
27932
27935
  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
@@ -27940,11 +27943,58 @@ var configSchema = external_exports.object({
27940
27943
  supabaseAnonKey: external_exports.string().min(1)
27941
27944
  }).strict();
27942
27945
  function loadConfig(env = process.env) {
27943
- return configSchema.parse({
27946
+ return loadConfigWithDiagnostics(env).config;
27947
+ }
27948
+ function loadConfigWithDiagnostics(env = process.env) {
27949
+ const parsed = configSchema.parse({
27944
27950
  appUrl: firstValue(env[brandEnvKey("APP_URL")], env.NEXT_PUBLIC_APP_URL, cliBrand.appUrl, publicProductionDefaults.appUrl),
27945
27951
  supabaseUrl: firstValue(env[brandEnvKey("SUPABASE_URL")], env.NEXT_PUBLIC_SUPABASE_URL, publicProductionDefaults.supabaseUrl),
27946
27952
  supabaseAnonKey: firstValue(env[brandEnvKey("SUPABASE_ANON_KEY")], env.NEXT_PUBLIC_SUPABASE_ANON_KEY, publicProductionDefaults.supabaseAnonKey)
27947
27953
  });
27954
+ const appUrl2 = resolveAppUrl(parsed.appUrl, env);
27955
+ return {
27956
+ config: {
27957
+ ...parsed,
27958
+ appUrl: appUrl2.value
27959
+ },
27960
+ appUrlWarning: appUrl2.warning
27961
+ };
27962
+ }
27963
+ function resolveAppUrl(value, env = process.env) {
27964
+ const normalized = value.trim().replace(/\/+$/, "");
27965
+ const parsed = new URL(normalized);
27966
+ const displayUrl = `${parsed.protocol}//${parsed.host}`;
27967
+ const allowDevelopmentAppUrl = env[allowInsecureAppUrlEnv] === "1";
27968
+ const isLocalHttpHost = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
27969
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
27970
+ throw new Error(
27971
+ `Configured app_url must use https://. Use ${canonicalAppUrl} for production, http://localhost or http://127.0.0.1 for local development, or set ${allowInsecureAppUrlEnv}=1 only for explicit development.`
27972
+ );
27973
+ }
27974
+ if (parsed.protocol === "http:" && !allowDevelopmentAppUrl && !isLocalHttpHost) {
27975
+ throw new Error(
27976
+ `Refusing insecure app_url ${displayUrl}. Use ${canonicalAppUrl} for production. Only http://localhost and http://127.0.0.1 are allowed without ${allowInsecureAppUrlEnv}=1.`
27977
+ );
27978
+ }
27979
+ if (legacyAppUrlAliases.includes(normalized)) {
27980
+ return {
27981
+ value: canonicalAppUrl,
27982
+ warning: `app_url ${displayUrl} is a legacy alias; using ${canonicalAppUrl}. Update ${brandEnvKey("APP_URL")} or NEXT_PUBLIC_APP_URL.`
27983
+ };
27984
+ }
27985
+ if (normalized !== canonicalAppUrl) {
27986
+ if (!allowDevelopmentAppUrl && !isLocalHttpHost) {
27987
+ throw new Error(
27988
+ `Refusing non-canonical app_url ${displayUrl}. Use ${canonicalAppUrl} for production. Set ${allowInsecureAppUrlEnv}=1 only for explicit development or preview testing.`
27989
+ );
27990
+ }
27991
+ const insecureNote = parsed.protocol === "http:" ? " and is allowed only for development" : " and requires explicit development opt-in";
27992
+ return {
27993
+ value: normalized,
27994
+ warning: `app_url ${displayUrl} is not the canonical production host ${canonicalAppUrl}${insecureNote}.`
27995
+ };
27996
+ }
27997
+ return { value: normalized, warning: null };
27948
27998
  }
27949
27999
  function brandEnvKey(suffix) {
27950
28000
  return `${cliBrand.envPrefix}_${suffix}`;
@@ -38996,6 +39046,16 @@ var agentTurnExecutionSchema = external_exports.object({
38996
39046
  claimedByRunnerId: uuidSchema4.nullable(),
38997
39047
  linkedRunId: uuidSchema4.nullable(),
38998
39048
  interactiveDeadline: external_exports.string().datetime({ offset: true }),
39049
+ claimedAt: external_exports.string().datetime({ offset: true }).nullable().optional(),
39050
+ startedAt: external_exports.string().datetime({ offset: true }).nullable().optional(),
39051
+ finishedAt: external_exports.string().datetime({ offset: true }).nullable().optional(),
39052
+ terminalReason: external_exports.string().min(1).max(120).nullable().optional(),
39053
+ claimLatencyMs: external_exports.number().int().nonnegative().nullable().optional(),
39054
+ runtimeDurationMs: external_exports.number().int().nonnegative().nullable().optional(),
39055
+ totalDurationMs: external_exports.number().int().nonnegative().nullable().optional(),
39056
+ inputTokens: external_exports.number().int().nonnegative().nullable().optional(),
39057
+ outputTokens: external_exports.number().int().nonnegative().nullable().optional(),
39058
+ model: external_exports.string().min(1).max(120).nullable().optional(),
38999
39059
  createdAt: external_exports.string().datetime({ offset: true }),
39000
39060
  updatedAt: external_exports.string().datetime({ offset: true })
39001
39061
  }).strict();
@@ -39008,6 +39068,8 @@ var aicosPendingTurnSchema = external_exports.object({
39008
39068
  status: agentTurnExecutionStatusSchema,
39009
39069
  linkedRunId: uuidSchema4.nullable(),
39010
39070
  interactiveDeadline: external_exports.string().datetime({ offset: true }),
39071
+ terminalReason: external_exports.string().min(1).max(120).nullable().default(null),
39072
+ totalDurationMs: external_exports.number().int().nonnegative().nullable().default(null),
39011
39073
  updatedAt: external_exports.string().datetime({ offset: true })
39012
39074
  }).strict();
39013
39075
  var aicosMessageRoleSchema = external_exports.enum(["user", "assistant", "tool", "system"]);
@@ -41339,7 +41401,9 @@ var graphNodeSchema = external_exports.object({
41339
41401
  }).strict().nullable(),
41340
41402
  signal_state: rollupStateSchema,
41341
41403
  cascade_state: rollupStateSchema,
41342
- open_task_count: external_exports.number().int().nonnegative()
41404
+ open_task_count: external_exports.number().int().nonnegative(),
41405
+ unassigned: external_exports.boolean(),
41406
+ is_peer: external_exports.boolean()
41343
41407
  }).strict();
41344
41408
  var graphEdgeSchema = external_exports.object({
41345
41409
  parent_seat_id: uuidSchema11,
@@ -44399,7 +44463,7 @@ The Compass is drafted, then activated by a human through supersession.
44399
44463
  order: 15,
44400
44464
  title: "AICOS chat guide",
44401
44465
  summary: "How the AI Chief of Staff chat works in the authenticated app shell and what it can safely do today.",
44402
- version: "2026-07-04.11",
44466
+ version: "2026-07-07.1",
44403
44467
  public: true,
44404
44468
  audiences: ["human", "in_app_agent"],
44405
44469
  stages: ["company_setup", "operating_rhythm"],
@@ -44455,9 +44519,9 @@ The current chat harness shows the effective lane and model. Cloud is the defaul
44455
44519
 
44456
44520
  AICOS also has an explicit brain selection. Cloud can use the included managed allowance or a tenant Anthropic key (BYOK). Runner can target the paired machine's Claude subscription or Codex subscription. MCP is labeled as the MCP client. Read this selection with \`aicos.brain_settings.get\`; owners can update it with \`aicos.brain_settings.update\` (CLI flags: \`--lane\`, \`--cloud-brain managed|byok\`, \`--runner-brain claude-cli|codex-cli\`). BYOK requires an active tenant Anthropic key. Runner and MCP answers are labeled in the transcript and do not consume the managed cloud allowance.
44457
44521
 
44458
- Runner mode uses the same AICOS sessions and transcript. Interactive chat turns are tracked separately from scheduled work orders: each user message has one user-owned turn execution with a short deadline, so two users' chat messages cannot collapse into the same scheduled-work slot. Scheduled and Forge runner work still use work orders. Runner claim packets carry ids and execution metadata, not the user's free-text request. After claim, the local runner calls the governed AICOS context-load tool, which re-checks seat scope and returns the bounded transcript, session summary, route context, tenant clock, grounding text, and source counts authorized for that turn. The runner treats that loaded tenant clock and context as authoritative instead of substituting its own runtime clock or local files. A runner must be paired, execute-ready, and backed by the live AICOS agent before it can be selected.
44522
+ Runner mode uses the same AICOS sessions and transcript. Interactive chat turns are tracked separately from scheduled work orders: each user message has one user-owned turn execution with a short deadline, so two users' chat messages cannot collapse into the same scheduled-work slot. Scheduled and Forge runner work still use work orders. Runner claim packets carry ids and execution metadata, not the user's free-text request. After claim, the local runner calls the governed AICOS context-load tool, which re-checks seat scope and returns the bounded transcript, session summary, route context, tenant clock, grounding text, and source counts authorized for that turn. For execute-ready runners, the claim also carries a server-built Seat work contract that names the governed runtime profile, the AICOS context-loader tool, and the Seat's permission manifest; action attempts still route through command guards, tool-call audit, and pending confirmations. The runner treats loaded tenant clock, context, and contract as authoritative instead of substituting its own runtime clock, local files, or self-invented authority. A runner must be paired, execute-ready, and backed by the live AICOS agent before it can be selected.
44459
44523
 
44460
- When a runner claims an interactive AICOS turn, the runner reports start and final result back to {{brand}}. The server appends exactly one assistant transcript message for the result, links that message to run evidence, and treats duplicate or late result reports as idempotent. If the runner answer includes a validated in-app link, the web panel can surface the same consent-required navigation suggestion once polling sees that the pending turn is terminal. If a queued, claimed, or running interactive turn passes its short deadline before a runner writes back, the turn is marked offline and the transcript receives a terminal assistant message instead of leaving a permanent "queued" placeholder.
44524
+ When a runner claims an interactive AICOS turn, the runner reports start and final result back to {{brand}}. The turn row records claim/start/finish timing, terminal reason, model label, and token counts when the runner or cloud path provides them. The server appends exactly one assistant transcript message for the result, links that message to run evidence, and treats duplicate or late result reports as idempotent. If the runner answer includes a validated in-app link, the web panel can surface the same consent-required navigation suggestion once polling sees that the pending turn is terminal. If a queued, claimed, or running interactive turn passes its short deadline before a runner writes back, the turn is marked offline and the transcript receives a terminal assistant message instead of leaving a permanent "queued" placeholder.
44461
44525
 
44462
44526
  While a runner turn is queued, claimed, or running, the chat panel keeps showing the pending state and polls the session until the answer or terminal status arrives. The composer, model picker, and lane picker are disabled during that active turn so a second browser tab or direct API call cannot overlap another runner turn in the same thread. Terminal runner statuses such as failed, offline, or canceled stay visible only while they are still the latest user turn; if the user continues the conversation in another lane, the stale terminal marker is no longer projected at the bottom of the thread.
44463
44527
 
@@ -44471,7 +44535,7 @@ You can attach images to a message from the composer. {{brand}} accepts PNG, JPE
44471
44535
 
44472
44536
  AICOS can also retrieve bounded summaries from prior sessions owned by the same tenant user and surface them as session-memory read models. Setup-limited users do not retrieve sensitive historical run or company-memory summaries through this path. This is a practical memory seam for AICOS continuity; it is not yet the future Company Brain, and it does not make unaccepted knowledge authoritative.
44473
44537
 
44474
- Platform allowance and BYOK state are checked before routing; an exhausted included allowance keeps AICOS in guided mode and points the user toward BYOK/manual continuation. Because the DER-1068 implementation still uses the deterministic harness, it does not create fake llm_calls rows or settle fake allowance usage; reservation/settlement belongs to the real provider call path.
44538
+ Platform allowance and BYOK state are checked before routing; an exhausted included allowance keeps AICOS in guided mode and points the user toward BYOK, billing, or manual continuation. The chat transcript and Settings surface the exhaustion reason and provide direct paths to add a tenant Anthropic key or review the plan. Because the DER-1068 implementation still uses the deterministic harness, it does not create fake llm_calls rows or settle fake allowance usage; reservation/settlement belongs to the real provider call path.
44475
44539
 
44476
44540
  During onboarding, the deterministic harness may execute only explicit low-risk draft actions. In the Responsibility Graph step, a user with setup/action authority can ask AICOS to create one named draft seat per turn; the seat stays vacant, is traceable to the AICOS message through artifact sources, and still needs human review before staffing, Charters, manifests, or go-live. Compass and Charter help remains draft guidance and clarifying questions until the live drafting planner lands. AICOS never silently finishes onboarding, approves Compass or Charter content, signs manifests, goes live, stores credentials, changes autonomy policy, or invites external people; those actions remain human-confirmed.
44477
44541
 
@@ -44516,7 +44580,7 @@ Treat AICOS as a coordinator over the operating system. It can become more usefu
44516
44580
  order: 20,
44517
44581
  title: "Responsibility Graph playbook",
44518
44582
  summary: "How to build a functions-first graph with seats, owners, Stewards, vacancies, and clean authority.",
44519
- version: "2026-07-04.3",
44583
+ version: "2026-07-06.2",
44520
44584
  public: true,
44521
44585
  audiences: ["human", "cli", "mcp", "in_app_agent"],
44522
44586
  stages: ["graph_design", "staffing"],
@@ -44549,6 +44613,23 @@ The Responsibility Graph is the company's operating structure. It answers who ow
44549
44613
  - Give every agent seat a human Steward chain.
44550
44614
  - Attach goals, measurables, and tasks to seats, not directly to people.
44551
44615
 
44616
+ ## Single-root chart and parent resolution
44617
+
44618
+ The graph always resolves to a single canonical root seat (Founder & CEO in the standard shape) so the web canvas, CLI \`rost graph show\`, and MCP \`graph.get\` all agree on the same structure. The parent of each seat is derived deterministically:
44619
+
44620
+ 1. An explicit parent seat id, when it names a live seat in the same chart.
44621
+ 2. The seat at the ltree parent path, when one exists.
44622
+ 3. The steward seat, when the seat is not AICOS and the steward is a live non-self seat.
44623
+ 4. Otherwise the seat has no parent.
44624
+
44625
+ AICOS always stays a peer of the root (never a child of its steward) and never appears as an orphan in the unassigned set \u2014 this is intentional and not a data-quality issue.
44626
+
44627
+ Seats that do not reach the single root through the resolved parent chain are listed as **unassigned** (with \`unassigned: true\` and no incoming edge). Each \`graph.get\` node carries an \`unassigned\` boolean, and the CLI \`rost graph show\` output prints unassigned seats under a separate \`Unassigned\` section so operators can see which parts of the chart need attention. The web canvas parks unassigned seats at the bottom of the chart instead of inventing fake root edges.
44628
+
44629
+ ## Forge Developer Team seats are pinned
44630
+
44631
+ Seats under \`forge.developer_team\` are resolved by Forge build dispatch using their exact path, so they cannot be reparented or merged from the graph, CLI, or MCP \u2014 moving a seat (or an ancestor whose subtree holds them) would eject them from dispatch, and no command can restore the path. To change who is accountable for one of these seats, update its steward with \`agent_setup.update\`; the reporting path stays fixed. To stand the team down, use the governed Forge team teardown flow, not a merge or decommission.
44632
+
44552
44633
  ## Add an agent from the graph
44553
44634
 
44554
44635
  Agent creation is a first-class graph action. From a seat in the Responsibility Graph you can open the agent setup wizard with the seat already chosen as the parent:
@@ -44619,7 +44700,7 @@ Each seat needs:
44619
44700
 
44620
44701
  Read before you write. Load the whole graph first so seat ids, parents, and occupancy are known.
44621
44702
 
44622
- - CLI: \`{{cli}} command graph.get --json '{}'\` (scope: tenant). Returns seats, edges, root, occupants, and status rollups.
44703
+ - CLI: \`{{cli}} command graph.get --json '{}'\` (scope: tenant). Returns seats, edges, root, occupants, status rollups, and unassigned-seat flags. The output includes a single \`root_seat_id\`, exactly one \`is_root: true\` node, and unassigned seats listed under their own section with a data-quality note.
44623
44704
  - MCP tool: \`rost_get_responsibility_graph\` with \`{}\`; or read the resource \`rost://graph\` (tenant-admin token).
44624
44705
  - One seat: \`{{cli}} command seat.get --json '{"seat_id":"<seat-id>"}'\` or \`rost_get_seat_detail\`.
44625
44706
 
@@ -47262,7 +47343,7 @@ Every notification should include the seat, cause, evidence, and requested decis
47262
47343
  order: 75,
47263
47344
  title: "Local runner guide",
47264
47345
  summary: "How local agent sessions and runner surfaces should operate through {{brand}} without bypassing Charters or audit.",
47265
- version: "2026-07-05.1",
47346
+ version: "2026-07-07.2",
47266
47347
  public: true,
47267
47348
  audiences: ["human", "cli", "mcp", "in_app_agent"],
47268
47349
  stages: ["staffing", "operating_rhythm"],
@@ -47304,6 +47385,8 @@ Two ways to authenticate and pair \u2014 both first-class, an agent can drive ei
47304
47385
 
47305
47386
  Headless and service accounts are supported first-class: when no usable OS keychain exists (for example a service or SSH account), the CLI stores the session in an owner-only file (directory 0700, file 0600) automatically \u2014 no development flag. That file holds only the CLI session token, never tenant secrets, API keys, or vault refs.
47306
47387
 
47388
+ Runner transport defaults to the canonical production host \`https://rost.elevatez.ai\`. The CLI rewrites the old \`https://rost-pink.vercel.app\` alias to the canonical host with a visible warning. External \`http://\` app URLs and non-canonical hosts are refused before pairing, heartbeat, claim, MCP, or result traffic starts; only \`http://localhost\`, \`http://127.0.0.1\`, or the explicit development/preview escape \`ROST_ALLOW_INSECURE_APP_URL=1\` are allowed.
47389
+
47307
47390
  Choose the local runtime with \`--runtime auto|claude|codex\` (default \`auto\`). The same short-lived seat-scoped MCP token and server-side guard apply regardless of runtime; results are tagged \`claude-cli\` or \`codex-cli\`.
47308
47391
 
47309
47392
  For always-on execution, \`runner serve --execute\` heartbeats on its normal interval and also checks for claimable work between heartbeats. The default interactive claim cadence is about one second; set \`RUNNER_INTERACTIVE_CLAIM_MS\` or \`--interactive-claim-ms\` to tune it for a host. Claude-backed turns reuse a per-seat Claude Code resume session when Claude reports a session id, so later turns can run with \`claude --resume <session-id>\` under the local subscription account. The runner stores only the opaque resume id in its owner-only state file; it does not use API-billed managed sessions to make subscription runner turns faster. A resident long-lived Claude/Codex process is still prototype-gated, so do not assume a fixed latency SLA from this guide alone.
@@ -47312,7 +47395,7 @@ The runner bearer secret is stored locally and is never displayed. Each claimed
47312
47395
 
47313
47396
  ## Interactive AICOS runner turns
47314
47397
 
47315
- AICOS runner chat uses the same runner process but not the scheduled work-order queue. The runner claims an \`agent_turn_executions\` item for one user message, starts it, executes the local Claude or Codex runtime with bounded AICOS context, then reports the final assistant answer back to {{brand}}. The transcript write-back is server-owned: one terminal result creates one assistant message, links it to run evidence, and records the final turn status. Retrying the same final report does not append a second answer. If an interactive turn expires while queued, claimed, or running, {{brand}} marks it offline and appends a visible terminal assistant message so the user is not left waiting on a stale queued state.
47398
+ AICOS runner chat uses the same runner process but not the scheduled work-order queue. The runner claims an \`agent_turn_executions\` item for one user message, starts it, executes the local Claude or Codex runtime with bounded AICOS context, then reports the final assistant answer back to {{brand}}. Execute-ready AICOS claims include a server-built Seat work contract: the governed runtime profile, the AICOS context-loader tool, and the Seat permission manifest. Actions invoked from that local runtime still pass through command guards, \`tool_calls\` audit, pending confirmations, human gates, and tenant isolation before anything durable changes. The transcript write-back is server-owned: one terminal result creates one assistant message, links it to run evidence, and records the final turn status, terminal reason, timings, model, and token telemetry when present. Retrying the same final report does not append a second answer. If an interactive turn expires while queued, claimed, or running, {{brand}} marks it offline and appends a visible terminal assistant message so the user is not left waiting on a stale queued state.
47316
47399
 
47317
47400
  ## Forge scheduler work
47318
47401
 
@@ -49864,10 +49947,27 @@ function renderGraph(output) {
49864
49947
  `root_seat_id=${field(record2, "root_seat_id")}`,
49865
49948
  "seat_id parent_seat_id type status name"
49866
49949
  ];
49950
+ const assigned = [];
49951
+ const unassigned = [];
49867
49952
  for (const node of nodes) {
49868
49953
  const row = asRecord(node);
49954
+ if (row.unassigned) {
49955
+ unassigned.push(row);
49956
+ } else {
49957
+ assigned.push(row);
49958
+ }
49959
+ }
49960
+ for (const row of assigned) {
49869
49961
  lines.push(`${field(row, "seat_id")} ${field(row, "parent_seat_id")} ${field(row, "seat_type")} ${field(row, "status")} ${field(row, "name")}`);
49870
49962
  }
49963
+ if (unassigned.length > 0) {
49964
+ lines.push("");
49965
+ lines.push("Unassigned (no path to root \u2014 data-quality signal)");
49966
+ lines.push("seat_id parent_seat_id type status name");
49967
+ for (const row of unassigned) {
49968
+ lines.push(`${field(row, "seat_id")} ${field(row, "parent_seat_id")} ${field(row, "seat_type")} ${field(row, "status")} ${field(row, "name")}`);
49969
+ }
49970
+ }
49871
49971
  return lines.join("\n");
49872
49972
  }
49873
49973
  function renderSeatList(output) {
@@ -51737,8 +51837,11 @@ async function runDoctor(options) {
51737
51837
  const env = options.env ?? process.env;
51738
51838
  const store = options.store ?? createTokenStore();
51739
51839
  const lines = [`${cliBrand.name} CLI doctor`];
51740
- const config2 = loadConfig(env);
51840
+ const { config: config2, appUrlWarning } = loadConfigWithDiagnostics(env);
51741
51841
  lines.push(`app_url: ${config2.appUrl}`);
51842
+ if (appUrlWarning) {
51843
+ lines.push(`app_url_warning: ${appUrlWarning}`);
51844
+ }
51742
51845
  lines.push(`supabase_url: ${config2.supabaseUrl}`);
51743
51846
  const currentVersion = await readCliVersion();
51744
51847
  lines.push(`version: ${currentVersion ?? "unknown"}`);
@@ -52866,6 +52969,10 @@ ${usage()}
52866
52969
  }
52867
52970
  options.io.stdout.write(`runner serve: ${options.appUrl} name=${config2.name} execute=${config2.execute}
52868
52971
  `);
52972
+ if (options.appUrlWarning) {
52973
+ options.io.stderr.write(`warning: ${options.appUrlWarning}
52974
+ `);
52975
+ }
52869
52976
  options.io.stdout.write(`runner sandbox: ${config2.sandbox.kind} - ${config2.sandbox.reason}
52870
52977
  `);
52871
52978
  if (config2.sandboxNetworkWarning) {
@@ -54363,7 +54470,9 @@ ${programArgsXml}
54363
54470
  <dict>
54364
54471
  <key>${cliBrand.envPrefix}_APP_URL</key>
54365
54472
  <string>${escapePlist(config2.appUrl)}</string>
54366
- <key>PATH</key>
54473
+ ${config2.allowDevelopmentAppUrl === true ? ` <key>${allowInsecureAppUrlEnv}</key>
54474
+ <string>1</string>
54475
+ ` : ""} <key>PATH</key>
54367
54476
  <string>${escapePlist(config2.servicePath)}</string>
54368
54477
  </dict>
54369
54478
  <key>KeepAlive</key>
@@ -54406,6 +54515,7 @@ ${serviceUsage()}
54406
54515
  io: options.io,
54407
54516
  runnerName: parsed.runnerName,
54408
54517
  appUrl: options.appUrl,
54518
+ allowDevelopmentAppUrl: options.allowDevelopmentAppUrl === true,
54409
54519
  execute: parsed.execute,
54410
54520
  userCode: parsed.userCode,
54411
54521
  nodePath: options.nodePath ?? resolveNodePath(),
@@ -54457,6 +54567,7 @@ async function installService(options) {
54457
54567
  stateFile: paths.stateFile,
54458
54568
  logDir: paths.logDir,
54459
54569
  appUrl: options.appUrl,
54570
+ ...options.allowDevelopmentAppUrl ? { allowDevelopmentAppUrl: true } : {},
54460
54571
  servicePath: buildServicePath(options.nodePath, options.homeDir),
54461
54572
  execute: options.execute
54462
54573
  });
@@ -54824,7 +54935,11 @@ async function pairRunnerOnce(options, safeName, stateFile, userCode) {
54824
54935
  ];
54825
54936
  try {
54826
54937
  await options.exec(options.nodePath, serveArgs, {
54827
- env: { ...process.env, [`${cliBrand.envPrefix}_APP_URL`]: options.appUrl }
54938
+ env: {
54939
+ ...process.env,
54940
+ [`${cliBrand.envPrefix}_APP_URL`]: options.appUrl,
54941
+ ...options.allowDevelopmentAppUrl === true ? { [allowInsecureAppUrlEnv]: "1" } : {}
54942
+ }
54828
54943
  });
54829
54944
  options.io.stdout.write(`Paired runner ${safeName}.
54830
54945
  `);
@@ -55005,7 +55120,8 @@ async function main(argv = process.argv.slice(2), options = {}) {
55005
55120
  `);
55006
55121
  justLoggedIn = true;
55007
55122
  }
55008
- const config2 = loadConfig();
55123
+ const configResult = loadConfigWithDiagnostics();
55124
+ const config2 = configResult.config;
55009
55125
  if (command === "runner" && args[0] === "serve") {
55010
55126
  let runnerClient = null;
55011
55127
  if (session) {
@@ -55025,12 +55141,18 @@ async function main(argv = process.argv.slice(2), options = {}) {
55025
55141
  io,
55026
55142
  client: runnerClient,
55027
55143
  appUrl: config2.appUrl,
55144
+ appUrlWarning: configResult.appUrlWarning,
55028
55145
  args: args.slice(1),
55029
55146
  env: process.env
55030
55147
  });
55031
55148
  }
55032
55149
  if (command === "runner" && isRunnerServiceInvocation(args)) {
55033
- return runRunnerService({ io, args, appUrl: config2.appUrl });
55150
+ return runRunnerService({
55151
+ io,
55152
+ args,
55153
+ appUrl: config2.appUrl,
55154
+ allowDevelopmentAppUrl: process.env[allowInsecureAppUrlEnv] === "1"
55155
+ });
55034
55156
  }
55035
55157
  if (!session) {
55036
55158
  io.stderr.write(`Not logged in. Run ${cliBrand.binName} login first.